source: protocols/oscar/oscar.c @ 85e9644

Last change on this file since 85e9644 was 68b518d6, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-05-26T09:03:38Z

Merging from main development tree.

  • Property mode set to 100644
File size: 76.0 KB
Line 
1/*
2 * gaim
3 *
4 * Some code copyright (C) 2002-2006, Jelmer Vernooij <jelmer@samba.org>
5 *                                    and the BitlBee team.
6 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
7 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 *
23 */
24
25#include <errno.h>
26#include <ctype.h>
27#include <string.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <time.h>
31#include <sys/stat.h>
32#include <glib.h>
33#include "nogaim.h"
34#include "bitlbee.h"
35#include "proxy.h"
36#include "sock.h"
37
38#include "aim.h"
39#include "icq.h"
40#include "bos.h"
41#include "ssi.h"
42#include "im.h"
43#include "info.h"
44#include "buddylist.h"
45#include "chat.h"
46#include "chatnav.h"
47
48/* constants to identify proto_opts */
49#define USEROPT_AUTH      0
50#define USEROPT_AUTHPORT  1
51
52#define UC_AOL          0x02
53#define UC_ADMIN        0x04
54#define UC_UNCONFIRMED  0x08
55#define UC_NORMAL       0x10
56#define UC_AB           0x20
57#define UC_WIRELESS     0x40
58
59#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
60
61#define OSCAR_GROUP "Friends"
62
63/* Don't know if support for UTF8 is really working. For now it's UTF16 here.
64   static int gaim_caps = AIM_CAPS_UTF8; */
65
66static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY | AIM_CAPS_CHAT;
67static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02};
68
69struct oscar_data {
70        aim_session_t *sess;
71        aim_conn_t *conn;
72
73        guint cnpa;
74        guint paspa;
75
76        GSList *create_rooms;
77
78        gboolean conf;
79        gboolean reqemail;
80        gboolean setemail;
81        char *email;
82        gboolean setnick;
83        char *newsn;
84        gboolean chpass;
85        char *oldp;
86        char *newp;
87
88        GSList *oscar_chats;
89
90        gboolean killme;
91        gboolean icq;
92        GSList *evilhack;
93
94        struct {
95                guint maxbuddies; /* max users you can watch */
96                guint maxwatchers; /* max users who can watch you */
97                guint maxpermits; /* max users on permit list */
98                guint maxdenies; /* max users on deny list */
99                guint maxsiglen; /* max size (bytes) of profile */
100                guint maxawaymsglen; /* max size (bytes) of posted away message */
101        } rights;
102};
103
104struct create_room {
105        char *name;
106        int exchange;
107};
108
109struct chat_connection {
110        char *name;
111        char *show; /* AOL did something funny to us */
112        guint16 exchange;
113        guint16 instance;
114        int fd; /* this is redundant since we have the conn below */
115        aim_conn_t *conn;
116        int inpa;
117        int id;
118        struct gaim_connection *gc; /* i hate this. */
119        struct conversation *cnv; /* bah. */
120        int maxlen;
121        int maxvis;
122};
123
124struct ask_direct {
125        struct gaim_connection *gc;
126        char *sn;
127        char ip[64];
128        guint8 cookie[8];
129};
130
131struct icq_auth {
132        struct gaim_connection *gc;
133        guint32 uin;
134};
135
136static char *extract_name(const char *name) {
137        char *tmp;
138        int i, j;
139        char *x = strchr(name, '-');
140        if (!x) return g_strdup(name);
141        x = strchr(++x, '-');
142        if (!x) return g_strdup(name);
143        tmp = g_strdup(++x);
144
145        for (i = 0, j = 0; x[i]; i++) {
146                char hex[3];
147                if (x[i] != '%') {
148                        tmp[j++] = x[i];
149                        continue;
150                }
151                strncpy(hex, x + ++i, 2); hex[2] = 0;
152                i++;
153                tmp[j++] = (char)strtol(hex, NULL, 16);
154        }
155
156        tmp[j] = 0;
157        return tmp;
158}
159
160static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) {
161        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
162        struct chat_connection *c = NULL;
163
164        while (g) {
165                c = (struct chat_connection *)g->data;
166                if (c->id == id)
167                        break;
168                g = g->next;
169                c = NULL;
170        }
171
172        return c;
173}
174
175
176static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc,
177                                                        aim_conn_t *conn) {
178        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
179        struct chat_connection *c = NULL;
180
181        while (g) {
182                c = (struct chat_connection *)g->data;
183                if (c->conn == conn)
184                        break;
185                g = g->next;
186                c = NULL;
187        }
188
189        return c;
190}
191
192static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
193static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
194static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
195static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
196static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
197static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
198static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
199static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
200static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
201static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
202static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
203static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
204static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
205static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
206static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
207static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
208static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
209static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
210static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
211static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
212static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
213static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
214static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
215static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
216static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
217static int gaim_memrequest       (aim_session_t *, aim_frame_t *, ...);
218static int gaim_selfinfo         (aim_session_t *, aim_frame_t *, ...);
219static int gaim_offlinemsg       (aim_session_t *, aim_frame_t *, ...);
220static int gaim_offlinemsgdone   (aim_session_t *, aim_frame_t *, ...);
221static int gaim_ssi_parserights  (aim_session_t *, aim_frame_t *, ...);
222static int gaim_ssi_parselist    (aim_session_t *, aim_frame_t *, ...);
223static int gaim_ssi_parseack     (aim_session_t *, aim_frame_t *, ...);
224static int gaim_parsemtn         (aim_session_t *, aim_frame_t *, ...);
225static int gaim_icqinfo          (aim_session_t *, aim_frame_t *, ...);
226static int gaim_parseaiminfo     (aim_session_t *, aim_frame_t *, ...);
227
228static char *msgerrreason[] = {
229        "Invalid error",
230        "Invalid SNAC",
231        "Rate to host",
232        "Rate to client",
233        "Not logged in",
234        "Service unavailable",
235        "Service not defined",
236        "Obsolete SNAC",
237        "Not supported by host",
238        "Not supported by client",
239        "Refused by client",
240        "Reply too big",
241        "Responses lost",
242        "Request denied",
243        "Busted SNAC payload",
244        "Insufficient rights",
245        "In local permit/deny",
246        "Too evil (sender)",
247        "Too evil (receiver)",
248        "User temporarily unavailable",
249        "No match",
250        "List overflow",
251        "Request ambiguous",
252        "Queue full",
253        "Not while on AOL"
254};
255static int msgerrreasonlen = 25;
256
257static gboolean oscar_callback(gpointer data, gint source,
258                                b_input_condition condition) {
259        aim_conn_t *conn = (aim_conn_t *)data;
260        aim_session_t *sess = aim_conn_getsess(conn);
261        struct gaim_connection *gc = sess ? sess->aux_data : NULL;
262        struct oscar_data *odata;
263
264        if (!gc) {
265                /* gc is null. we return, else we seg SIGSEG on next line. */
266                return FALSE;
267        }
268     
269        if (!g_slist_find(get_connections(), gc)) {
270                /* oh boy. this is probably bad. i guess the only thing we
271                 * can really do is return? */
272                return FALSE;
273        }
274
275        odata = (struct oscar_data *)gc->proto_data;
276
277        if (condition & GAIM_INPUT_READ) {
278                if (aim_get_command(odata->sess, conn) >= 0) {
279                        aim_rxdispatch(odata->sess);
280                               if (odata->killme)
281                                       signoff(gc);
282                } else {
283                        if ((conn->type == AIM_CONN_TYPE_BOS) ||
284                                   !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) {
285                                hide_login_progress_error(gc, _("Disconnected."));
286                                signoff(gc);
287                        } else if (conn->type == AIM_CONN_TYPE_CHAT) {
288                                struct chat_connection *c = find_oscar_chat_by_conn(gc, conn);
289                                char buf[BUF_LONG];
290                                c->conn = NULL;
291                                if (c->inpa > 0)
292                                        b_event_remove(c->inpa);
293                                c->inpa = 0;
294                                c->fd = -1;
295                                aim_conn_kill(odata->sess, &conn);
296                                sprintf(buf, _("You have been disconnected from chat room %s."), c->name);
297                                do_error_dialog(sess->aux_data, buf, _("Chat Error!"));
298                        } else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
299                                if (odata->cnpa > 0)
300                                        b_event_remove(odata->cnpa);
301                                odata->cnpa = 0;
302                                while (odata->create_rooms) {
303                                        struct create_room *cr = odata->create_rooms->data;
304                                        g_free(cr->name);
305                                        odata->create_rooms =
306                                                g_slist_remove(odata->create_rooms, cr);
307                                        g_free(cr);
308                                        do_error_dialog(sess->aux_data, _("Chat is currently unavailable"),
309                                                        _("Gaim - Chat"));
310                                }
311                                aim_conn_kill(odata->sess, &conn);
312                        } else if (conn->type == AIM_CONN_TYPE_AUTH) {
313                                if (odata->paspa > 0)
314                                        b_event_remove(odata->paspa);
315                                odata->paspa = 0;
316                                aim_conn_kill(odata->sess, &conn);
317                        } else {
318                                aim_conn_kill(odata->sess, &conn);
319                        }
320                }
321        } else {
322                /* WTF??? */
323                return FALSE;
324        }
325               
326        return TRUE;
327}
328
329static gboolean oscar_login_connect(gpointer data, gint source, b_input_condition cond)
330{
331        struct gaim_connection *gc = data;
332        struct oscar_data *odata;
333        aim_session_t *sess;
334        aim_conn_t *conn;
335
336        if (!g_slist_find(get_connections(), gc)) {
337                closesocket(source);
338                return FALSE;
339        }
340
341        odata = gc->proto_data;
342        sess = odata->sess;
343        conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
344
345        if (source < 0) {
346                hide_login_progress(gc, _("Couldn't connect to host"));
347                signoff(gc);
348                return FALSE;
349        }
350
351        aim_conn_completeconnect(sess, conn);
352        gc->inpa = b_input_add(conn->fd, GAIM_INPUT_READ,
353                        oscar_callback, conn);
354       
355        return FALSE;
356}
357
358static void oscar_login(struct aim_user *user) {
359        aim_session_t *sess;
360        aim_conn_t *conn;
361        char buf[256];
362        struct gaim_connection *gc = new_gaim_conn(user);
363        struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1);
364
365        if (isdigit(*user->username)) {
366                odata->icq = TRUE;
367                /* This is odd but it's necessary for a proper do_import and do_export.
368                   We don't do those anymore, but let's stick with it, just in case
369                   it accidentally fixes something else too... */
370                gc->password[8] = 0;
371        } else {
372                gc->flags |= OPT_CONN_HTML;
373        }
374
375        sess = g_new0(aim_session_t, 1);
376
377        aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
378
379        /* we need an immediate queue because we don't use a while-loop to
380         * see if things need to be sent. */
381        aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
382        odata->sess = sess;
383        sess->aux_data = gc;
384
385        conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
386        if (conn == NULL) {
387                hide_login_progress(gc, _("Unable to login to AIM"));
388                signoff(gc);
389                return;
390        }
391       
392        if (g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.icq.com") != 0 &&
393            g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.oscar.aol.com") != 0) {
394                serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.",user->proto_opt[USEROPT_AUTH]);
395        }
396       
397        g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username);
398        set_login_progress(gc, 2, buf);
399
400        aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
401        aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
402
403        conn->status |= AIM_CONN_STATUS_INPROGRESS;
404        conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ?
405                                        user->proto_opt[USEROPT_AUTH] : AIM_DEFAULT_LOGIN_SERVER,
406                                 user->proto_opt[USEROPT_AUTHPORT][0] ?
407                                        atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
408                                 oscar_login_connect, gc);
409        if (conn->fd < 0) {
410                hide_login_progress(gc, _("Couldn't connect to host"));
411                signoff(gc);
412                return;
413        }
414        aim_request_login(sess, conn, gc->username);
415}
416
417static void oscar_close(struct gaim_connection *gc) {
418        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
419       
420        while (odata->oscar_chats) {
421                struct chat_connection *n = odata->oscar_chats->data;
422                if (n->inpa > 0)
423                        b_event_remove(n->inpa);
424                g_free(n->name);
425                g_free(n->show);
426                odata->oscar_chats = g_slist_remove(odata->oscar_chats, n);
427                g_free(n);
428        }
429        while (odata->create_rooms) {
430                struct create_room *cr = odata->create_rooms->data;
431                g_free(cr->name);
432                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
433                g_free(cr);
434        }
435        if (odata->email)
436                g_free(odata->email);
437        if (odata->newp)
438                g_free(odata->newp);
439        if (odata->oldp)
440                g_free(odata->oldp);
441        if (gc->inpa > 0)
442                b_event_remove(gc->inpa);
443        if (odata->cnpa > 0)
444                b_event_remove(odata->cnpa);
445        if (odata->paspa > 0)
446                b_event_remove(odata->paspa);
447        aim_session_kill(odata->sess);
448        g_free(odata->sess);
449        odata->sess = NULL;
450        g_free(gc->proto_data);
451        gc->proto_data = NULL;
452}
453
454static gboolean oscar_bos_connect(gpointer data, gint source, b_input_condition cond) {
455        struct gaim_connection *gc = data;
456        struct oscar_data *odata;
457        aim_session_t *sess;
458        aim_conn_t *bosconn;
459
460        if (!g_slist_find(get_connections(), gc)) {
461                closesocket(source);
462                return FALSE;
463        }
464
465        odata = gc->proto_data;
466        sess = odata->sess;
467        bosconn = odata->conn;
468
469        if (source < 0) {
470                hide_login_progress(gc, _("Could Not Connect"));
471                signoff(gc);
472                return FALSE;
473        }
474
475        aim_conn_completeconnect(sess, bosconn);
476        gc->inpa = b_input_add(bosconn->fd, GAIM_INPUT_READ,
477                        oscar_callback, bosconn);
478        set_login_progress(gc, 4, _("Connection established, cookie sent"));
479       
480        return FALSE;
481}
482
483static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
484        va_list ap;
485        struct aim_authresp_info *info;
486        int i; char *host; int port;
487        struct aim_user *user;
488        aim_conn_t *bosconn;
489
490        struct gaim_connection *gc = sess->aux_data;
491        struct oscar_data *od = gc->proto_data;
492        user = gc->user;
493        port = user->proto_opt[USEROPT_AUTHPORT][0] ?
494                atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
495
496        va_start(ap, fr);
497        info = va_arg(ap, struct aim_authresp_info *);
498        va_end(ap);
499
500        if (info->errorcode || !info->bosip || !info->cookie) {
501                switch (info->errorcode) {
502                case 0x05:
503                        /* Incorrect nick/password */
504                        hide_login_progress(gc, _("Incorrect nickname or password."));
505//                      plugin_event(event_error, (void *)980, 0, 0, 0);
506                        break;
507                case 0x11:
508                        /* Suspended account */
509                        hide_login_progress(gc, _("Your account is currently suspended."));
510                        break;
511                case 0x18:
512                        /* connecting too frequently */
513                        hide_login_progress(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
514                        break;
515                case 0x1c:
516                        /* client too old */
517                        hide_login_progress(gc, _("The client version you are using is too old. Please upgrade at " WEBSITE));
518                        break;
519                default:
520                        hide_login_progress(gc, _("Authentication Failed"));
521                        break;
522                }
523                od->killme = TRUE;
524                return 1;
525        }
526
527
528        aim_conn_kill(sess, &fr->conn);
529
530        bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL);
531        if (bosconn == NULL) {
532                hide_login_progress(gc, _("Internal Error"));
533                od->killme = TRUE;
534                return 0;
535        }
536
537        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0);
538        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_RIGHTS, gaim_bosrights, 0);
539        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
540        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
541        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0);
542        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
543        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
544        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
545        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
546        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
547        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
548        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
549        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
550        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
551        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_PARAMINFO, gaim_icbm_param_info, 0);
552        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_ERROR, gaim_parse_genericerr, 0);
553        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ERROR, gaim_parse_genericerr, 0);
554        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_ERROR, gaim_parse_genericerr, 0);
555        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x1f, gaim_memrequest, 0);
556        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SELFINFO, gaim_selfinfo, 0);
557        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
558        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
559        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0);
560        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
561        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
562        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);
563        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0);
564        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parsemtn, 0);
565
566        ((struct oscar_data *)gc->proto_data)->conn = bosconn;
567        for (i = 0; i < (int)strlen(info->bosip); i++) {
568                if (info->bosip[i] == ':') {
569                        port = atoi(&(info->bosip[i+1]));
570                        break;
571                }
572        }
573        host = g_strndup(info->bosip, i);
574        bosconn->status |= AIM_CONN_STATUS_INPROGRESS;
575        bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc);
576        g_free(host);
577        if (bosconn->fd < 0) {
578                hide_login_progress(gc, _("Could Not Connect"));
579                od->killme = TRUE;
580                return 0;
581        }
582        aim_sendcookie(sess, bosconn, info->cookie);
583        b_event_remove(gc->inpa);
584
585        return 1;
586}
587
588struct pieceofcrap {
589        struct gaim_connection *gc;
590        unsigned long offset;
591        unsigned long len;
592        char *modname;
593        int fd;
594        aim_conn_t *conn;
595        unsigned int inpa;
596};
597
598static gboolean damn_you(gpointer data, gint source, b_input_condition c)
599{
600        struct pieceofcrap *pos = data;
601        struct oscar_data *od = pos->gc->proto_data;
602        char in = '\0';
603        int x = 0;
604        unsigned char m[17];
605
606        while (read(pos->fd, &in, 1) == 1) {
607                if (in == '\n')
608                        x++;
609                else if (in != '\r')
610                        x = 0;
611                if (x == 2)
612                        break;
613                in = '\0';
614        }
615        if (in != '\n') {
616                do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
617                                " You may be disconnected shortly.", "Login Error");
618                b_event_remove(pos->inpa);
619                closesocket(pos->fd);
620                g_free(pos);
621                return FALSE;
622        }
623        /* [WvG] Wheeeee! Who needs error checking anyway? ;-) */
624        read(pos->fd, m, 16);
625        m[16] = '\0';
626        b_event_remove(pos->inpa);
627        closesocket(pos->fd);
628        aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
629        g_free(pos);
630       
631        return FALSE;
632}
633
634static gboolean straight_to_hell(gpointer data, gint source, b_input_condition cond) {
635        struct pieceofcrap *pos = data;
636        char buf[BUF_LONG];
637
638        if (source < 0) {
639                do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
640                                " You may be disconnected shortly.", "Login Error");
641                if (pos->modname)
642                        g_free(pos->modname);
643                g_free(pos);
644                return FALSE;
645        }
646
647        g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA
648                        "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
649                        pos->offset, pos->len, pos->modname ? pos->modname : "");
650        write(pos->fd, buf, strlen(buf));
651        if (pos->modname)
652                g_free(pos->modname);
653        pos->inpa = b_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos);
654        return FALSE;
655}
656
657/* size of icbmui.ocm, the largest module in AIM 3.5 */
658#define AIM_MAX_FILE_SIZE 98304
659
660int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
661        va_list ap;
662        struct pieceofcrap *pos;
663        guint32 offset, len;
664        char *modname;
665        int fd;
666
667        va_start(ap, fr);
668        offset = (guint32)va_arg(ap, unsigned long);
669        len = (guint32)va_arg(ap, unsigned long);
670        modname = va_arg(ap, char *);
671        va_end(ap);
672
673        if (len == 0) {
674                aim_sendmemblock(sess, fr->conn, offset, len, NULL,
675                                AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
676                return 1;
677        }
678        /* uncomment this when you're convinced it's right. remember, it's been wrong before.
679        if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
680                char *buf;
681                int i = 8;
682                if (modname)
683                        i += strlen(modname);
684                buf = g_malloc(i);
685                i = 0;
686                if (modname) {
687                        memcpy(buf, modname, strlen(modname));
688                        i += strlen(modname);
689                }
690                buf[i++] = offset & 0xff;
691                buf[i++] = (offset >> 8) & 0xff;
692                buf[i++] = (offset >> 16) & 0xff;
693                buf[i++] = (offset >> 24) & 0xff;
694                buf[i++] = len & 0xff;
695                buf[i++] = (len >> 8) & 0xff;
696                buf[i++] = (len >> 16) & 0xff;
697                buf[i++] = (len >> 24) & 0xff;
698                aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
699                g_free(buf);
700                return 1;
701        }
702        */
703
704        pos = g_new0(struct pieceofcrap, 1);
705        pos->gc = sess->aux_data;
706        pos->conn = fr->conn;
707
708        pos->offset = offset;
709        pos->len = len;
710        pos->modname = modname ? g_strdup(modname) : NULL;
711
712        fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos);
713        if (fd < 0) {
714                if (pos->modname)
715                        g_free(pos->modname);
716                g_free(pos);
717                do_error_dialog(sess->aux_data, "Gaim was unable to get a valid hash for logging into AIM."
718                                " You may be disconnected shortly.", "Login Error");
719        }
720        pos->fd = fd;
721
722        return 1;
723}
724
725static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {
726#if 0
727        struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b};
728#else
729        struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD;
730#endif
731        char *key;
732        va_list ap;
733        struct gaim_connection *gc = sess->aux_data;
734
735        va_start(ap, fr);
736        key = va_arg(ap, char *);
737        va_end(ap);
738
739        aim_send_login(sess, fr->conn, gc->username, gc->password, &info, key);
740
741        return 1;
742}
743
744static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {
745        struct gaim_connection *gc = sess->aux_data;
746        struct chat_connection *chatcon;
747        static int id = 1;
748
749        aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0);
750        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
751        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
752        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
753        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
754
755        aim_clientready(sess, fr->conn);
756
757        chatcon = find_oscar_chat_by_conn(gc, fr->conn);
758        chatcon->id = id;
759        chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
760
761        return 1;
762}
763
764static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
765
766        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_ERROR, gaim_parse_genericerr, 0);
767        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
768
769        aim_clientready(sess, fr->conn);
770
771        aim_chatnav_reqrights(sess, fr->conn);
772
773        return 1;
774}
775
776static gboolean oscar_chatnav_connect(gpointer data, gint source, b_input_condition cond) {
777        struct gaim_connection *gc = data;
778        struct oscar_data *odata;
779        aim_session_t *sess;
780        aim_conn_t *tstconn;
781
782        if (!g_slist_find(get_connections(), gc)) {
783                closesocket(source);
784                return FALSE;
785        }
786
787        odata = gc->proto_data;
788        sess = odata->sess;
789        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
790
791        if (source < 0) {
792                aim_conn_kill(sess, &tstconn);
793                return FALSE;
794        }
795
796        aim_conn_completeconnect(sess, tstconn);
797        odata->cnpa = b_input_add(tstconn->fd, GAIM_INPUT_READ,
798                                        oscar_callback, tstconn);
799       
800        return FALSE;
801}
802
803static gboolean oscar_auth_connect(gpointer data, gint source, b_input_condition cond)
804{
805        struct gaim_connection *gc = data;
806        struct oscar_data *odata;
807        aim_session_t *sess;
808        aim_conn_t *tstconn;
809
810        if (!g_slist_find(get_connections(), gc)) {
811                closesocket(source);
812                return FALSE;
813        }
814
815        odata = gc->proto_data;
816        sess = odata->sess;
817        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
818
819        if (source < 0) {
820                aim_conn_kill(sess, &tstconn);
821                return FALSE;
822        }
823
824        aim_conn_completeconnect(sess, tstconn);
825        odata->paspa = b_input_add(tstconn->fd, GAIM_INPUT_READ,
826                                oscar_callback, tstconn);
827       
828        return FALSE;
829}
830
831static gboolean oscar_chat_connect(gpointer data, gint source, b_input_condition cond)
832{
833        struct chat_connection *ccon = data;
834        struct gaim_connection *gc = ccon->gc;
835        struct oscar_data *odata;
836        aim_session_t *sess;
837        aim_conn_t *tstconn;
838
839        if (!g_slist_find(get_connections(), gc)) {
840                closesocket(source);
841                g_free(ccon->show);
842                g_free(ccon->name);
843                g_free(ccon);
844                return FALSE;
845        }
846
847        odata = gc->proto_data;
848        sess = odata->sess;
849        tstconn = ccon->conn;
850
851        if (source < 0) {
852                aim_conn_kill(sess, &tstconn);
853                g_free(ccon->show);
854                g_free(ccon->name);
855                g_free(ccon);
856                return FALSE;
857        }
858
859        aim_conn_completeconnect(sess, ccon->conn);
860        ccon->inpa = b_input_add(tstconn->fd,
861                        GAIM_INPUT_READ,
862                        oscar_callback, tstconn);
863        odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon);
864       
865        return FALSE;
866}
867
868/* Hrmph. I don't know how to make this look better. --mid */
869static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
870        va_list ap;
871        struct aim_redirect_data *redir;
872        struct gaim_connection *gc = sess->aux_data;
873        struct aim_user *user = gc->user;
874        aim_conn_t *tstconn;
875        int i;
876        char *host;
877        int port;
878
879        port = user->proto_opt[USEROPT_AUTHPORT][0] ?
880                atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
881
882        va_start(ap, fr);
883        redir = va_arg(ap, struct aim_redirect_data *);
884        va_end(ap);
885
886        for (i = 0; i < (int)strlen(redir->ip); i++) {
887                if (redir->ip[i] == ':') {
888                        port = atoi(&(redir->ip[i+1]));
889                        break;
890                }
891        }
892        host = g_strndup(redir->ip, i);
893
894        switch(redir->group) {
895        case 0x7: /* Authorizer */
896                tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
897                if (tstconn == NULL) {
898                        g_free(host);
899                        return 1;
900                }
901                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0);
902//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0);
903//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0);
904//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0);
905
906                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
907                tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc);
908                if (tstconn->fd < 0) {
909                        aim_conn_kill(sess, &tstconn);
910                        g_free(host);
911                        return 1;
912                }
913                aim_sendcookie(sess, tstconn, redir->cookie);
914                break;
915        case 0xd: /* ChatNav */
916                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL);
917                if (tstconn == NULL) {
918                        g_free(host);
919                        return 1;
920                }
921                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0);
922
923                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
924                tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc);
925                if (tstconn->fd < 0) {
926                        aim_conn_kill(sess, &tstconn);
927                        g_free(host);
928                        return 1;
929                }
930                aim_sendcookie(sess, tstconn, redir->cookie);
931                break;
932        case 0xe: /* Chat */
933                {
934                struct chat_connection *ccon;
935
936                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
937                if (tstconn == NULL) {
938                        g_free(host);
939                        return 1;
940                }
941
942                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0);
943
944                ccon = g_new0(struct chat_connection, 1);
945                ccon->conn = tstconn;
946                ccon->gc = gc;
947                ccon->fd = -1;
948                ccon->name = g_strdup(redir->chat.room);
949                ccon->exchange = redir->chat.exchange;
950                ccon->instance = redir->chat.instance;
951                ccon->show = extract_name(redir->chat.room);
952               
953                ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
954                ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon);
955                if (ccon->conn->fd < 0) {
956                        aim_conn_kill(sess, &tstconn);
957                        g_free(host);
958                        g_free(ccon->show);
959                        g_free(ccon->name);
960                        g_free(ccon);
961                        return 1;
962                }
963                aim_sendcookie(sess, tstconn, redir->cookie);
964                }
965                break;
966        default: /* huh? */
967                break;
968        }
969
970        g_free(host);
971        return 1;
972}
973
974static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {
975        struct gaim_connection *gc = sess->aux_data;
976        struct oscar_data *od = gc->proto_data;
977        aim_userinfo_t *info;
978        time_t time_idle = 0, signon = 0;
979        int type = 0;
980        int caps = 0;
981        char *tmp;
982
983        va_list ap;
984        va_start(ap, fr);
985        info = va_arg(ap, aim_userinfo_t *);
986        va_end(ap);
987
988        if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
989                caps = info->capabilities;
990        if (info->flags & AIM_FLAG_ACTIVEBUDDY)
991                type |= UC_AB;
992
993        if ((!od->icq) && (info->present & AIM_USERINFO_PRESENT_FLAGS)) {
994                if (info->flags & AIM_FLAG_UNCONFIRMED)
995                        type |= UC_UNCONFIRMED;
996                if (info->flags & AIM_FLAG_ADMINISTRATOR)
997                        type |= UC_ADMIN;
998                if (info->flags & AIM_FLAG_AOL)
999                        type |= UC_AOL;
1000                if (info->flags & AIM_FLAG_FREE)
1001                        type |= UC_NORMAL;
1002                if (info->flags & AIM_FLAG_AWAY)
1003                        type |= UC_UNAVAILABLE;
1004                if (info->flags & AIM_FLAG_WIRELESS)
1005                        type |= UC_WIRELESS;
1006        }
1007        if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
1008                type = (info->icqinfo.status << 7);
1009                if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
1010                      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
1011                        type |= UC_UNAVAILABLE;
1012                }
1013        }
1014
1015        if (caps & AIM_CAPS_ICQ)
1016                caps ^= AIM_CAPS_ICQ;
1017
1018        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
1019                time(&time_idle);
1020                time_idle -= info->idletime*60;
1021        }
1022
1023        if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
1024                signon = time(NULL) - info->sessionlen;
1025
1026        tmp = g_strdup(normalize(gc->username));
1027        if (!strcmp(tmp, normalize(info->sn)))
1028                g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", info->sn);
1029        g_free(tmp);
1030
1031        serv_got_update(gc, info->sn, 1, info->warnlevel/10, signon,
1032                        time_idle, type, caps);
1033
1034        return 1;
1035}
1036
1037static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
1038        aim_userinfo_t *info;
1039        va_list ap;
1040        struct gaim_connection *gc = sess->aux_data;
1041
1042        va_start(ap, fr);
1043        info = va_arg(ap, aim_userinfo_t *);
1044        va_end(ap);
1045
1046        serv_got_update(gc, info->sn, 0, 0, 0, 0, 0, 0);
1047
1048        return 1;
1049}
1050
1051static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
1052        char *tmp = g_malloc(BUF_LONG + 1);
1053        struct gaim_connection *gc = sess->aux_data;
1054        int flags = 0;
1055       
1056        if (args->icbmflags & AIM_IMFLAGS_AWAY)
1057                flags |= IM_FLAG_AWAY;
1058       
1059        if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) {
1060                char *src;
1061               
1062                if (args->icbmflags & AIM_IMFLAGS_UNICODE)
1063                        src = "UNICODEBIG";
1064                else
1065                        src = "ISO8859-1";
1066               
1067                /* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */
1068                if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) {
1069                        // Successfully converted!
1070                } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
1071                        int i;
1072                       
1073                        for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
1074                                unsigned short uni;
1075                               
1076                                uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1077       
1078                                if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1079                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
1080                                } else { /* something else, do UNICODE entity */
1081                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
1082                                }
1083                        }
1084                } else {
1085                        g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1086                }
1087        } else
1088                g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1089       
1090        strip_linefeed(tmp);
1091        serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1);
1092        g_free(tmp);
1093       
1094        return 1;
1095}
1096
1097void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv);
1098void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv);
1099       
1100static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
1101        struct gaim_connection *gc = sess->aux_data;
1102
1103        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1104                return 1;
1105
1106        if (args->reqclass & AIM_CAPS_CHAT) {
1107                char *name = extract_name(args->info.chat.roominfo.name);
1108                int *exch = g_new0(int, 1);
1109                GList *m = NULL;
1110                char txt[1024];
1111                struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1);
1112
1113                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1114                *exch = args->info.chat.roominfo.exchange;
1115                m = g_list_append(m, exch);
1116
1117                g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg );
1118
1119                inv->gc = gc;
1120                inv->exchange = *exch;
1121                inv->name = g_strdup(name);
1122               
1123                do_ask_dialog( gc, txt, inv, oscar_accept_chat, oscar_reject_chat);
1124       
1125                if (name)
1126                        g_free(name);
1127        }
1128
1129        return 1;
1130}
1131
1132static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
1133        char *uin, message;
1134        struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
1135       
1136        uin = g_strdup_printf("%u", data->uin);
1137        message = 0;
1138        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1139        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
1140        if(find_buddy(data->gc, uin) == NULL)
1141                show_got_added(data->gc, uin, NULL);
1142       
1143        g_free(uin);
1144        g_free(data);
1145}
1146
1147static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
1148        char *uin, *message;
1149        struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
1150       
1151        uin = g_strdup_printf("%u", data->uin);
1152        message = g_strdup_printf("No reason given.");
1153        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1154        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1155        g_free(message);
1156       
1157        g_free(uin);
1158        g_free(data);
1159}
1160
1161/*
1162 * For when other people ask you for authorization
1163 */
1164static void gaim_icq_authask(struct gaim_connection *gc, guint32 uin, char *msg) {
1165        struct icq_auth *data = g_new(struct icq_auth, 1);
1166        char *reason = NULL;
1167        char *dialog_msg;
1168       
1169        if (strlen(msg) > 6)
1170                reason = msg + 6;
1171       
1172        dialog_msg = g_strdup_printf("The user %u wants to add you to their buddy list for the following reason: %s", uin, reason ? reason : "No reason given.");
1173        data->gc = gc;
1174        data->uin = uin;
1175        do_ask_dialog(gc, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
1176        g_free(dialog_msg);
1177}
1178
1179static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
1180        struct gaim_connection *gc = sess->aux_data;
1181
1182        switch (args->type) {
1183                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1184                        char *uin, *message;
1185                        uin = g_strdup_printf("%u", args->uin);
1186                        message = g_strdup(args->msg);
1187                        strip_linefeed(message);
1188                        serv_got_im(gc, uin, message, 0, time(NULL), -1);
1189                        g_free(uin);
1190                        g_free(message);
1191                } break;
1192
1193                case 0x0004: { /* Someone sent you a URL */
1194                        char *uin, *message;
1195                        char **m;
1196       
1197                        uin = g_strdup_printf("%u", args->uin);
1198                        m = g_strsplit(args->msg, "\376", 2);
1199
1200                        if ((strlen(m[0]) != 0)) {
1201                          message = g_strjoinv(" -- ", m);
1202                        } else {
1203                          message = m[1];
1204                        }
1205
1206                        strip_linefeed(message);
1207                        serv_got_im(gc, uin, message, 0, time(NULL), -1);
1208                        g_free(uin);
1209                        g_free(m);
1210                        g_free(message);
1211                } break;
1212               
1213                case 0x0006: { /* Someone requested authorization */
1214                        gaim_icq_authask(gc, args->uin, args->msg);
1215                } break;
1216
1217                case 0x0007: { /* Someone has denied you authorization */
1218                        serv_got_crap(sess->aux_data, "The user %u has denied your request to add them to your contact list for the following reason:\n%s", args->uin, args->msg ? args->msg : _("No reason given.") );
1219                } break;
1220
1221                case 0x0008: { /* Someone has granted you authorization */
1222                        serv_got_crap(sess->aux_data, "The user %u has granted your request to add them to your contact list for the following reason:\n%s", args->uin, args->msg ? args->msg : _("No reason given.") );
1223                } break;
1224
1225                case 0x0012: {
1226                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1227                } break;
1228
1229                default: {;
1230                } break;
1231        }
1232
1233        return 1;
1234}
1235
1236static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1237        int channel, ret = 0;
1238        aim_userinfo_t *userinfo;
1239        va_list ap;
1240
1241        va_start(ap, fr);
1242        channel = va_arg(ap, int);
1243        userinfo = va_arg(ap, aim_userinfo_t *);
1244
1245    if (set_getint(sess->aux_data, "debug")) {
1246        serv_got_crap(sess->aux_data, "channel %i called", channel);
1247    }
1248
1249        switch (channel) {
1250                case 1: { /* standard message */
1251                        struct aim_incomingim_ch1_args *args;
1252                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1253                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1254                } break;
1255
1256                case 2: { /* rendevous */
1257                        struct aim_incomingim_ch2_args *args;
1258                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1259                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1260                } break;
1261
1262                case 4: { /* ICQ */
1263                        struct aim_incomingim_ch4_args *args;
1264                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1265                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1266                } break;
1267
1268                default: {;
1269                } break;
1270        }
1271
1272        va_end(ap);
1273
1274        return ret;
1275}
1276
1277static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1278        va_list ap;
1279        guint16 chan, nummissed, reason;
1280        aim_userinfo_t *userinfo;
1281        char buf[1024];
1282
1283        va_start(ap, fr);
1284        chan = (guint16)va_arg(ap, unsigned int);
1285        userinfo = va_arg(ap, aim_userinfo_t *);
1286        nummissed = (guint16)va_arg(ap, unsigned int);
1287        reason = (guint16)va_arg(ap, unsigned int);
1288        va_end(ap);
1289
1290        switch(reason) {
1291                case 0:
1292                        /* Invalid (0) */
1293                        g_snprintf(buf,
1294                                   sizeof(buf),
1295                                   nummissed == 1 ? 
1296                                   _("You missed %d message from %s because it was invalid.") :
1297                                   _("You missed %d messages from %s because they were invalid."),
1298                                   nummissed,
1299                                   userinfo->sn);
1300                        break;
1301                case 1:
1302                        /* Message too large */
1303                        g_snprintf(buf,
1304                                   sizeof(buf),
1305                                   nummissed == 1 ?
1306                                   _("You missed %d message from %s because it was too large.") :
1307                                   _("You missed %d messages from %s because they were too large."),
1308                                   nummissed,
1309                                   userinfo->sn);
1310                        break;
1311                case 2:
1312                        /* Rate exceeded */
1313                        g_snprintf(buf,
1314                                   sizeof(buf),
1315                                   nummissed == 1 ? 
1316                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1317                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1318                                   nummissed,
1319                                   userinfo->sn);
1320                        break;
1321                case 3:
1322                        /* Evil Sender */
1323                        g_snprintf(buf,
1324                                   sizeof(buf),
1325                                   nummissed == 1 ?
1326                                   _("You missed %d message from %s because it was too evil.") : 
1327                                   _("You missed %d messages from %s because they are too evil."),
1328                                   nummissed,
1329                                   userinfo->sn);
1330                        break;
1331                case 4:
1332                        /* Evil Receiver */
1333                        g_snprintf(buf,
1334                                   sizeof(buf),
1335                                   nummissed == 1 ? 
1336                                   _("You missed %d message from %s because you are too evil.") :
1337                                   _("You missed %d messages from %s because you are too evil."),
1338                                   nummissed,
1339                                   userinfo->sn);
1340                        break;
1341                default:
1342                        g_snprintf(buf,
1343                                   sizeof(buf),
1344                                   nummissed == 1 ? 
1345                                   _("You missed %d message from %s for unknown reasons.") :
1346                                   _("You missed %d messages from %s for unknown reasons."),
1347                                   nummissed,
1348                                   userinfo->sn);
1349                        break;
1350        }
1351        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1352
1353        return 1;
1354}
1355
1356static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1357        va_list ap;
1358        guint16 reason;
1359        char *m;
1360
1361        va_start(ap, fr);
1362        reason = (guint16)va_arg(ap, unsigned int);
1363        va_end(ap);
1364
1365        m = g_strdup_printf(_("SNAC threw error: %s\n"),
1366                        reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1367        do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error"));
1368        g_free(m);
1369
1370        return 1;
1371}
1372
1373static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1374        va_list ap;
1375        char *destn;
1376        guint16 reason;
1377        char buf[1024];
1378
1379        va_start(ap, fr);
1380        reason = (guint16)va_arg(ap, unsigned int);
1381        destn = va_arg(ap, char *);
1382        va_end(ap);
1383
1384        sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
1385                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1386        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1387
1388        return 1;
1389}
1390
1391static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1392        va_list ap;
1393        char *destn;
1394        guint16 reason;
1395        char buf[1024];
1396
1397        va_start(ap, fr);
1398        reason = (guint16)va_arg(ap, unsigned int);
1399        destn = va_arg(ap, char *);
1400        va_end(ap);
1401
1402        sprintf(buf, _("User information for %s unavailable: %s"), destn,
1403                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1404        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1405
1406
1407        return 1;
1408}
1409
1410static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1411        char *msg;
1412        guint16 id;
1413        va_list ap;
1414
1415        va_start(ap, fr);
1416        id  = (guint16)va_arg(ap, unsigned int);
1417        msg = va_arg(ap, char *);
1418        va_end(ap);
1419
1420        if (id < 4)
1421                do_error_dialog(sess->aux_data, _("Your connection may be lost."),
1422                                _("AOL error"));
1423
1424        return 1;
1425}
1426
1427static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1428        va_list ap;
1429        guint16 type;
1430        struct gaim_connection *gc = sess->aux_data;
1431        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1432
1433        va_start(ap, fr);
1434        type = (guint16)va_arg(ap, unsigned int);
1435
1436        switch(type) {
1437                case 0x0002: {
1438                        guint8 maxrooms;
1439                        struct aim_chat_exchangeinfo *exchanges;
1440                        int exchangecount; // i;
1441
1442                        maxrooms = (guint8)va_arg(ap, unsigned int);
1443                        exchangecount = va_arg(ap, int);
1444                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1445                        va_end(ap);
1446
1447                        while (odata->create_rooms) {
1448                                struct create_room *cr = odata->create_rooms->data;
1449                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1450                                g_free(cr->name);
1451                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1452                                g_free(cr);
1453                        }
1454                        }
1455                        break;
1456                case 0x0008: {
1457                        char *fqcn, *name, *ck;
1458                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1459                        guint8 createperms;
1460                        guint32 createtime;
1461
1462                        fqcn = va_arg(ap, char *);
1463                        instance = (guint16)va_arg(ap, unsigned int);
1464                        exchange = (guint16)va_arg(ap, unsigned int);
1465                        flags = (guint16)va_arg(ap, unsigned int);
1466                        createtime = va_arg(ap, guint32);
1467                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1468                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1469                        createperms = (guint8)va_arg(ap, int);
1470                        unknown = (guint16)va_arg(ap, unsigned int);
1471                        name = va_arg(ap, char *);
1472                        ck = va_arg(ap, char *);
1473                        va_end(ap);
1474
1475                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1476                        }
1477                        break;
1478                default:
1479                        va_end(ap);
1480                        break;
1481        }
1482        return 1;
1483}
1484
1485static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1486        va_list ap;
1487        int count, i;
1488        aim_userinfo_t *info;
1489        struct gaim_connection *g = sess->aux_data;
1490
1491        struct chat_connection *c = NULL;
1492
1493        va_start(ap, fr);
1494        count = va_arg(ap, int);
1495        info  = va_arg(ap, aim_userinfo_t *);
1496        va_end(ap);
1497
1498        c = find_oscar_chat_by_conn(g, fr->conn);
1499        if (!c)
1500                return 1;
1501
1502        for (i = 0; i < count; i++)
1503                add_chat_buddy(c->cnv, info[i].sn);
1504
1505        return 1;
1506}
1507
1508static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1509        va_list ap;
1510        int count, i;
1511        aim_userinfo_t *info;
1512        struct gaim_connection *g = sess->aux_data;
1513
1514        struct chat_connection *c = NULL;
1515
1516        va_start(ap, fr);
1517        count = va_arg(ap, int);
1518        info  = va_arg(ap, aim_userinfo_t *);
1519        va_end(ap);
1520
1521        c = find_oscar_chat_by_conn(g, fr->conn);
1522        if (!c)
1523                return 1;
1524
1525        for (i = 0; i < count; i++)
1526                remove_chat_buddy(c->cnv, info[i].sn, NULL);
1527
1528        return 1;
1529}
1530
1531static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1532        va_list ap;
1533        aim_userinfo_t *userinfo;
1534        struct aim_chat_roominfo *roominfo;
1535        char *roomname;
1536        int usercount;
1537        char *roomdesc;
1538        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1539        guint32 creationtime;
1540        struct gaim_connection *gc = sess->aux_data;
1541        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1542
1543        va_start(ap, fr);
1544        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1545        roomname = va_arg(ap, char *);
1546        usercount= va_arg(ap, int);
1547        userinfo = va_arg(ap, aim_userinfo_t *);
1548        roomdesc = va_arg(ap, char *);
1549        unknown_c9 = (guint16)va_arg(ap, int);
1550        creationtime = (guint32)va_arg(ap, unsigned long);
1551        maxmsglen = (guint16)va_arg(ap, int);
1552        unknown_d2 = (guint16)va_arg(ap, int);
1553        unknown_d5 = (guint16)va_arg(ap, int);
1554        maxvisiblemsglen = (guint16)va_arg(ap, int);
1555        va_end(ap);
1556
1557        ccon->maxlen = maxmsglen;
1558        ccon->maxvis = maxvisiblemsglen;
1559
1560        return 1;
1561}
1562
1563static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1564        va_list ap;
1565        aim_userinfo_t *info;
1566        char *msg;
1567        struct gaim_connection *gc = sess->aux_data;
1568        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1569        char *tmp;
1570
1571        va_start(ap, fr);
1572        info = va_arg(ap, aim_userinfo_t *);
1573        msg  = va_arg(ap, char *);
1574
1575        tmp = g_malloc(BUF_LONG);
1576        g_snprintf(tmp, BUF_LONG, "%s", msg);
1577        serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL));
1578        g_free(tmp);
1579
1580        return 1;
1581}
1582
1583static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1584#if 0
1585        static const char *codes[5] = {
1586                "invalid",
1587                 "change",
1588                 "warning",
1589                 "limit",
1590                 "limit cleared",
1591        };
1592#endif
1593        va_list ap;
1594        guint16 code, rateclass;
1595        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1596
1597        va_start(ap, fr); 
1598        code = (guint16)va_arg(ap, unsigned int);
1599        rateclass= (guint16)va_arg(ap, unsigned int);
1600        windowsize = (guint32)va_arg(ap, unsigned long);
1601        clear = (guint32)va_arg(ap, unsigned long);
1602        alert = (guint32)va_arg(ap, unsigned long);
1603        limit = (guint32)va_arg(ap, unsigned long);
1604        disconnect = (guint32)va_arg(ap, unsigned long);
1605        currentavg = (guint32)va_arg(ap, unsigned long);
1606        maxavg = (guint32)va_arg(ap, unsigned long);
1607        va_end(ap);
1608
1609        /* XXX fix these values */
1610        if (code == AIM_RATE_CODE_CHANGE) {
1611                if (currentavg >= clear)
1612                        aim_conn_setlatency(fr->conn, 0);
1613        } else if (code == AIM_RATE_CODE_WARNING) {
1614                aim_conn_setlatency(fr->conn, windowsize/4);
1615        } else if (code == AIM_RATE_CODE_LIMIT) {
1616                do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1617                                  "Please wait 10 seconds and try again."), _("Gaim - Error"));
1618                aim_conn_setlatency(fr->conn, windowsize/2);
1619        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1620                aim_conn_setlatency(fr->conn, 0);
1621        }
1622
1623        return 1;
1624}
1625
1626static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1627        va_list ap;
1628        aim_userinfo_t *info;
1629        struct gaim_connection *gc = sess->aux_data;
1630
1631        va_start(ap, fr);
1632        info = va_arg(ap, aim_userinfo_t *);
1633        va_end(ap);
1634
1635        gc->evil = info->warnlevel/10;
1636        /* gc->correction_time = (info->onlinesince - gc->login_time); */
1637
1638        return 1;
1639}
1640
1641static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1642
1643        aim_reqpersonalinfo(sess, fr->conn);
1644        aim_bos_reqlocaterights(sess, fr->conn);
1645        aim_bos_reqbuddyrights(sess, fr->conn);
1646
1647        aim_reqicbmparams(sess);
1648
1649        aim_bos_reqrights(sess, fr->conn);
1650        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1651        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1652                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1653
1654        return 1;
1655}
1656
1657static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1658        struct gaim_connection *gc = sess->aux_data;
1659        struct oscar_data *od = gc->proto_data;
1660
1661        aim_clientready(sess, fr->conn);
1662
1663        if (od->chpass) {
1664                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1665                g_free(od->oldp);
1666                od->oldp = NULL;
1667                g_free(od->newp);
1668                od->newp = NULL;
1669                od->chpass = FALSE;
1670        }
1671        if (od->setnick) {
1672                aim_admin_setnick(sess, fr->conn, od->newsn);
1673                g_free(od->newsn);
1674                od->newsn = NULL;
1675                od->setnick = FALSE;
1676        }
1677        if (od->conf) {
1678                aim_admin_reqconfirm(sess, fr->conn);
1679                od->conf = FALSE;
1680        }
1681        if (od->reqemail) {
1682                aim_admin_getinfo(sess, fr->conn, 0x0011);
1683                od->reqemail = FALSE;
1684        }
1685        if (od->setemail) {
1686                aim_admin_setemail(sess, fr->conn, od->email);
1687                g_free(od->email);
1688                od->setemail = FALSE;
1689        }
1690
1691        return 1;
1692}
1693
1694static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1695        struct aim_icbmparameters *params;
1696        va_list ap;
1697
1698        va_start(ap, fr);
1699        params = va_arg(ap, struct aim_icbmparameters *);
1700        va_end(ap);
1701
1702        /* Maybe senderwarn and recverwarn should be user preferences... */
1703        params->flags = 0x0000000b;
1704        params->maxmsglen = 8000;
1705        params->minmsginterval = 0;
1706
1707        aim_seticbmparam(sess, params);
1708
1709        return 1;
1710}
1711
1712static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1713{
1714        va_list ap;
1715        guint16 maxsiglen;
1716        struct gaim_connection *gc = sess->aux_data;
1717        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1718
1719        va_start(ap, fr);
1720        maxsiglen = va_arg(ap, int);
1721        va_end(ap);
1722
1723        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1724
1725        aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps);
1726
1727        return 1;
1728}
1729
1730static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1731        va_list ap;
1732        guint16 maxbuddies, maxwatchers;
1733        struct gaim_connection *gc = sess->aux_data;
1734        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1735
1736        va_start(ap, fr);
1737        maxbuddies = (guint16)va_arg(ap, unsigned int);
1738        maxwatchers = (guint16)va_arg(ap, unsigned int);
1739        va_end(ap);
1740
1741        odata->rights.maxbuddies = (guint)maxbuddies;
1742        odata->rights.maxwatchers = (guint)maxwatchers;
1743
1744        return 1;
1745}
1746
1747static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1748        guint16 maxpermits, maxdenies;
1749        va_list ap;
1750        struct gaim_connection *gc = sess->aux_data;
1751        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1752
1753        va_start(ap, fr);
1754        maxpermits = (guint16)va_arg(ap, unsigned int);
1755        maxdenies = (guint16)va_arg(ap, unsigned int);
1756        va_end(ap);
1757
1758        odata->rights.maxpermits = (guint)maxpermits;
1759        odata->rights.maxdenies = (guint)maxdenies;
1760
1761        aim_clientready(sess, fr->conn);
1762
1763        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1764
1765        aim_ssi_reqrights(sess, fr->conn);
1766        aim_ssi_reqalldata(sess, fr->conn);
1767
1768        return 1;
1769}
1770
1771static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1772        va_list ap;
1773        struct aim_icq_offlinemsg *msg;
1774        struct gaim_connection *gc = sess->aux_data;
1775
1776        va_start(ap, fr);
1777        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1778        va_end(ap);
1779
1780        switch (msg->type) {
1781                case 0x0001: { /* Basic offline message */
1782                        char sender[32];
1783                        char *dialog_msg = g_strdup(msg->msg);
1784                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1785                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1786                        strip_linefeed(dialog_msg);
1787                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1788                        g_free(dialog_msg);
1789                } break;
1790
1791                case 0x0004: { /* Someone sent you a URL */
1792                        char sender[32];
1793                        char *dialog_msg;
1794                        char **m;
1795
1796                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1797                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1798
1799                        m = g_strsplit(msg->msg, "\376", 2);
1800
1801                        if ((strlen(m[0]) != 0)) {
1802                          dialog_msg = g_strjoinv(" -- ", m);
1803                        } else {
1804                          dialog_msg = m[1];
1805                        }
1806
1807                        strip_linefeed(dialog_msg);
1808                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1809                        g_free(dialog_msg);
1810                        g_free(m);
1811                } break;
1812               
1813                case 0x0006: { /* Authorization request */
1814                        gaim_icq_authask(gc, msg->sender, msg->msg);
1815                } break;
1816
1817                case 0x0007: { /* Someone has denied you authorization */
1818                        serv_got_crap(sess->aux_data, "The user %u has denied your request to add them to your contact list for the following reason:\n%s", msg->sender, msg->msg ? msg->msg : _("No reason given.") );
1819                } break;
1820
1821                case 0x0008: { /* Someone has granted you authorization */
1822                        serv_got_crap(sess->aux_data, "The user %u has granted your request to add them to your contact list for the following reason:\n%s", msg->sender, msg->msg ? msg->msg : _("No reason given.") );
1823                } break;
1824
1825                case 0x0012: {
1826                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1827                } break;
1828
1829                default: {;
1830                }
1831        }
1832
1833        return 1;
1834}
1835
1836static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1837{
1838        aim_icq_ackofflinemsgs(sess);
1839        return 1;
1840}
1841
1842static void oscar_keepalive(struct gaim_connection *gc) {
1843        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1844        aim_flap_nop(odata->sess, odata->conn);
1845}
1846
1847static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) {
1848        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1849        int ret = 0;
1850        if (imflags & IM_FLAG_AWAY) {
1851                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1852        } else {
1853                struct aim_sendimext_args args;
1854                char *s;
1855               
1856                args.flags = AIM_IMFLAGS_ACK;
1857                if (odata->icq)
1858                        args.flags |= AIM_IMFLAGS_OFFLINE;
1859                for (s = message; *s; s++)
1860                        if (*s & 128)
1861                                break;
1862               
1863                /* Message contains high ASCII chars, time for some translation! */
1864                if (*s) {
1865                        s = g_malloc(BUF_LONG);
1866                        /* Try if we can put it in an ISO8859-1 string first.
1867                           If we can't, fall back to UTF16. */
1868                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1869                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1870                                len = ret;
1871                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1872                                args.flags |= AIM_IMFLAGS_UNICODE;
1873                                len = ret;
1874                        } else {
1875                                /* OOF, translation failed... Oh well.. */
1876                                g_free( s );
1877                                s = message;
1878                        }
1879                } else {
1880                        s = message;
1881                }
1882               
1883                args.features = gaim_features;
1884                args.featureslen = sizeof(gaim_features);
1885               
1886                args.destsn = name;
1887                args.msg    = s;
1888                args.msglen = len;
1889               
1890                ret = aim_send_im_ext(odata->sess, &args);
1891               
1892                if (s != message) {
1893                        g_free(s);
1894                }
1895        }
1896        if (ret >= 0)
1897                return 1;
1898        return ret;
1899}
1900
1901static void oscar_get_info(struct gaim_connection *g, char *name) {
1902        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1903        if (odata->icq)
1904                aim_icq_getallinfo(odata->sess, name);
1905        else {
1906                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1907                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1908        }
1909}
1910
1911static void oscar_get_away(struct gaim_connection *g, char *who) {
1912        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1913        if (odata->icq) {
1914                struct buddy *budlight = find_buddy(g, who);
1915                if (budlight)
1916                        if ((budlight->uc & 0xff80) >> 7)
1917                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1918                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1919        } else
1920                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1921}
1922
1923static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1924{
1925
1926        if (!g_strcasecmp(state, _("Visible"))) {
1927                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1928                return;
1929        } else if (!g_strcasecmp(state, _("Invisible"))) {
1930                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1931                return;
1932        } /* else... */
1933
1934        if (od->rights.maxawaymsglen == 0)
1935                do_error_dialog(gc, "oscar_set_away_aim called before locate rights received", "Protocol Error");
1936
1937        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1938
1939        if (gc->away)
1940                g_free(gc->away);
1941        gc->away = NULL;
1942
1943        if (!message) {
1944                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1945                return;
1946        }
1947
1948        if (strlen(message) > od->rights.maxawaymsglen) {
1949                gchar *errstr;
1950
1951                errstr = g_strdup_printf("Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
1952
1953                do_error_dialog(gc, errstr, "Away Message Too Long");
1954
1955                g_free(errstr);
1956        }
1957
1958        gc->away = g_strndup(message, od->rights.maxawaymsglen);
1959        aim_bos_setprofile(od->sess, od->conn, NULL, gc->away, gaim_caps);
1960
1961        return;
1962}
1963
1964static void oscar_set_away_icq(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1965{
1966    const char *msg = NULL;
1967        gboolean no_message = FALSE;
1968
1969        /* clean old states */
1970    if (gc->away) {
1971                g_free(gc->away);
1972                gc->away = NULL;
1973    }
1974        od->sess->aim_icq_state = 0;
1975
1976        /* if no message, then use an empty message */
1977    if (message) {
1978        msg = message;
1979    } else {
1980        msg = "";
1981                no_message = TRUE;
1982    }
1983
1984        if (!g_strcasecmp(state, "Online")) {
1985                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1986        } else if (!g_strcasecmp(state, "Away")) {
1987                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1988        gc->away = g_strdup(msg);
1989                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1990        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1991                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
1992        gc->away = g_strdup(msg);
1993                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1994        } else if (!g_strcasecmp(state, "Not Available")) {
1995                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
1996        gc->away = g_strdup(msg);
1997                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1998        } else if (!g_strcasecmp(state, "Occupied")) {
1999                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
2000        gc->away = g_strdup(msg);
2001                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
2002        } else if (!g_strcasecmp(state, "Free For Chat")) {
2003                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
2004        gc->away = g_strdup(msg);
2005                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
2006        } else if (!g_strcasecmp(state, "Invisible")) {
2007                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
2008        gc->away = g_strdup(msg);
2009        } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
2010                if (no_message) {
2011                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
2012                } else {
2013                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
2014            gc->away = g_strdup(msg);
2015                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
2016                }
2017        }
2018
2019        return;
2020}
2021
2022static void oscar_set_away(struct gaim_connection *gc, char *state, char *message)
2023{
2024        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2025
2026    oscar_set_away_aim(gc, od, state, message);
2027        if (od->icq)
2028                oscar_set_away_icq(gc, od, state, message);
2029
2030        return;
2031}
2032
2033static void oscar_add_buddy(struct gaim_connection *g, char *name) {
2034        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2035        aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
2036}
2037
2038static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) {
2039        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2040        struct aim_ssi_item *ssigroup;
2041        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
2042}
2043
2044static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
2045        return 1;
2046}
2047
2048static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
2049        struct gaim_connection *gc = sess->aux_data;
2050        struct aim_ssi_item *curitem;
2051        int tmp;
2052
2053        /* Add from server list to local list */
2054        tmp = 0;
2055        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
2056                switch (curitem->type) {
2057                        case 0x0000: /* Buddy */
2058                                if ((curitem->name) && (!find_buddy(gc, curitem->name))) {
2059                                        char *realname = NULL;
2060
2061                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
2062                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
2063                                               
2064                                        add_buddy(gc, NULL, curitem->name, realname);
2065                                       
2066                                        if (realname)
2067                                            g_free(realname);
2068                                }
2069                                break;
2070
2071                        case 0x0002: /* Permit buddy */
2072                                if (curitem->name) {
2073                                        GSList *list;
2074                                        for (list=gc->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2075                                        if (!list) {
2076                                                char *name;
2077                                                name = g_strdup(normalize(curitem->name));
2078                                                gc->permit = g_slist_append(gc->permit, name);
2079                                                tmp++;
2080                                        }
2081                                }
2082                                break;
2083
2084                        case 0x0003: /* Deny buddy */
2085                                if (curitem->name) {
2086                                        GSList *list;
2087                                        for (list=gc->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2088                                        if (!list) {
2089                                                char *name;
2090                                                name = g_strdup(normalize(curitem->name));
2091                                                gc->deny = g_slist_append(gc->deny, name);
2092                                                tmp++;
2093                                        }
2094                                }
2095                                break;
2096
2097                        case 0x0004: /* Permit/deny setting */
2098                                if (curitem->data) {
2099                                        guint8 permdeny;
2100                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != gc->permdeny)) {
2101                                                gc->permdeny = permdeny;
2102                                                tmp++;
2103                                        }
2104                                }
2105                                break;
2106
2107                        case 0x0005: /* Presence setting */
2108                                /* We don't want to change Gaim's setting because it applies to all accounts */
2109                                break;
2110                } /* End of switch on curitem->type */
2111        } /* End of for loop */
2112
2113        aim_ssi_enable(sess, fr->conn);
2114       
2115        /* Request offline messages, now that the buddy list is complete. */
2116        aim_icq_reqofflinemsgs(sess);
2117       
2118        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
2119        account_online(gc);
2120       
2121        return 1;
2122}
2123
2124static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2125{
2126        aim_snac_t *origsnac;
2127        va_list ap;
2128
2129        va_start( ap, fr );
2130        origsnac = va_arg( ap, aim_snac_t * );
2131        va_end( ap );
2132       
2133        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2134        {
2135                int i, st, count = aim_bstream_empty( &fr->data );
2136                char *list;
2137               
2138                if( count & 1 )
2139                {
2140                        /* Hmm, the length should be even... */
2141                        do_error_dialog( sess->aux_data, "Received SSI ACK package with non-even length", "Gaim - Error" );
2142                        return( 0 );
2143                }
2144                count >>= 1;
2145               
2146                list = (char *) origsnac->data;
2147                for( i = 0; i < count; i ++ )
2148                {
2149                        st = aimbs_get16( &fr->data );
2150                        if( st == 0x0E )
2151                        {
2152                                serv_got_crap( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
2153                               
2154                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2155                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2156                        }
2157                        list += strlen( list ) + 1;
2158                }
2159        }
2160       
2161        return( 1 );
2162}
2163
2164static void oscar_set_permit_deny(struct gaim_connection *gc) {
2165        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2166        if (od->icq) {
2167                GSList *list;
2168                char buf[MAXMSGLEN];
2169                int at;
2170
2171                switch(gc->permdeny) {
2172                case 1:
2173                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gc->username);
2174                        break;
2175                case 2:
2176                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gc->username);
2177                        break;
2178                case 3:
2179                        list = gc->permit;
2180                        at = 0;
2181                        while (list) {
2182                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2183                                list = list->next;
2184                        }
2185                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2186                        break;
2187                case 4:
2188                        list = gc->deny;
2189                        at = 0;
2190                        while (list) {
2191                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2192                                list = list->next;
2193                        }
2194                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2195                        break;
2196                        default:
2197                        break;
2198                }
2199                signoff_blocked(gc);
2200        } else {
2201                if (od->sess->ssi.received_data)
2202                        aim_ssi_setpermdeny(od->sess, od->conn, gc->permdeny, 0xffffffff);
2203        }
2204}
2205
2206static void oscar_add_permit(struct gaim_connection *gc, char *who) {
2207        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2208        if (od->icq) {
2209                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2210        } else {
2211                if (od->sess->ssi.received_data)
2212                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2213        }
2214}
2215
2216static void oscar_add_deny(struct gaim_connection *gc, char *who) {
2217        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2218        if (od->icq) {
2219                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2220        } else {
2221                if (od->sess->ssi.received_data)
2222                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2223        }
2224}
2225
2226static void oscar_rem_permit(struct gaim_connection *gc, char *who) {
2227        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2228        if (!od->icq) {
2229                if (od->sess->ssi.received_data)
2230                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2231        }
2232}
2233
2234static void oscar_rem_deny(struct gaim_connection *gc, char *who) {
2235        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2236        if (!od->icq) {
2237                if (od->sess->ssi.received_data)
2238                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2239        }
2240}
2241
2242static GList *oscar_away_states(struct gaim_connection *gc)
2243{
2244        struct oscar_data *od = gc->proto_data;
2245        GList *m = NULL;
2246
2247        if (!od->icq)
2248                return g_list_append(m, GAIM_AWAY_CUSTOM);
2249
2250        m = g_list_append(m, "Online");
2251        m = g_list_append(m, "Away");
2252        m = g_list_append(m, "Do Not Disturb");
2253        m = g_list_append(m, "Not Available");
2254        m = g_list_append(m, "Occupied");
2255        m = g_list_append(m, "Free For Chat");
2256        m = g_list_append(m, "Invisible");
2257
2258        return m;
2259}
2260
2261static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2262{
2263        struct gaim_connection *gc = sess->aux_data;
2264        gchar who[16];
2265        GString *str;
2266        va_list ap;
2267        struct aim_icq_info *info;
2268
2269        va_start(ap, fr);
2270        info = va_arg(ap, struct aim_icq_info *);
2271        va_end(ap);
2272
2273        if (!info->uin)
2274                return 0;
2275
2276        str = g_string_sized_new(100);
2277        g_snprintf(who, sizeof(who), "%u", info->uin);
2278
2279        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2280                                info->nick ? info->nick : "-");
2281        info_string_append(str, "\n", _("First Name"), info->first);
2282        info_string_append(str, "\n", _("Last Name"), info->last);
2283                info_string_append(str, "\n", _("Email Address"), info->email);
2284        if (info->numaddresses && info->email2) {
2285                int i;
2286                for (i = 0; i < info->numaddresses; i++) {
2287                                        info_string_append(str, "\n", _("Email Address"), info->email2[i]);
2288                }
2289        }
2290        info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
2291        info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
2292        if (info->birthyear || info->birthmonth || info->birthday) {
2293                char date[30];
2294                struct tm tm;
2295                tm.tm_mday = (int)info->birthday;
2296                tm.tm_mon = (int)info->birthmonth-1;
2297                tm.tm_year = (int)info->birthyear%100;
2298                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2299                info_string_append(str, "\n", _("Birthday"), date);
2300        }
2301        if (info->age) {
2302                char age[5];
2303                g_snprintf(age, sizeof(age), "%hhd", info->age);
2304                info_string_append(str, "\n", _("Age"), age);
2305        }
2306                info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
2307        if (info->info && info->info[0]) {
2308                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2309                                                info->info, _("End of Additional Information"));
2310        }
2311        g_string_sprintfa(str, "\n");
2312        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2313                g_string_sprintfa(str, "%s:", _("Home Address"));
2314                info_string_append(str, "\n", _("Address"), info->homeaddr);
2315                info_string_append(str, "\n", _("City"), info->homecity);
2316                info_string_append(str, "\n", _("State"), info->homestate); 
2317                                info_string_append(str, "\n", _("Zip Code"), info->homezip);
2318                g_string_sprintfa(str, "\n");
2319        }
2320        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2321                g_string_sprintfa(str, "%s:", _("Work Address"));
2322                info_string_append(str, "\n", _("Address"), info->workaddr);
2323                info_string_append(str, "\n", _("City"), info->workcity);
2324                info_string_append(str, "\n", _("State"), info->workstate);
2325                                info_string_append(str, "\n", _("Zip Code"), info->workzip);
2326                g_string_sprintfa(str, "\n");
2327        }
2328        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2329                g_string_sprintfa(str, "%s:", _("Work Information"));
2330                info_string_append(str, "\n", _("Company"), info->workcompany);
2331                info_string_append(str, "\n", _("Division"), info->workdivision);
2332                info_string_append(str, "\n", _("Position"), info->workposition);
2333                if (info->workwebpage && info->workwebpage[0]) {
2334                        info_string_append(str, "\n", _("Web Page"), info->workwebpage);
2335                }
2336                g_string_sprintfa(str, "\n");
2337        }
2338
2339                serv_got_crap(gc, "%s\n%s", _("User Info"), str->str);
2340        g_string_free(str, TRUE);
2341
2342        return 1;
2343
2344}
2345
2346static char *oscar_encoding_extract(const char *encoding)
2347{
2348        char *ret = NULL;
2349        char *begin, *end;
2350
2351        g_return_val_if_fail(encoding != NULL, NULL);
2352
2353        /* Make sure encoding begins with charset= */
2354        if (strncmp(encoding, "text/plain; charset=", 20) &&
2355                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2356                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2357        {
2358                return NULL;
2359        }
2360
2361        begin = strchr(encoding, '"');
2362        end = strrchr(encoding, '"');
2363
2364        if ((begin == NULL) || (end == NULL) || (begin >= end))
2365                return NULL;
2366
2367        ret = g_strndup(begin+1, (end-1) - begin);
2368
2369        return ret;
2370}
2371
2372static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2373{
2374        char *utf8 = g_new0(char, 8192);
2375
2376        if ((encoding == NULL) || encoding[0] == '\0') {
2377                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2378        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2379                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2380        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2381                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2382        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2383                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2384        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2385                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2386                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2387                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2388        }
2389
2390        /*
2391         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2392         * we have been unable to convert the text to utf-8 from the encoding
2393         * that was specified.  So we assume it's UTF-8 and hope for the best.
2394         */
2395        if (*utf8 == 0) {
2396            strncpy(utf8, text, textlen);
2397        }
2398
2399        return utf8;
2400}
2401
2402static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2403{
2404        struct gaim_connection *gc = sess->aux_data;
2405        va_list ap;
2406        aim_userinfo_t *userinfo;
2407        guint16 infotype;
2408        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2409        guint16 text_length;
2410        char *utf8 = NULL;
2411
2412        va_start(ap, fr);
2413        userinfo = va_arg(ap, aim_userinfo_t *);
2414        infotype = va_arg(ap, int);
2415        text_encoding = va_arg(ap, char*);
2416        text = va_arg(ap, char*);
2417        text_length = va_arg(ap, int);
2418        va_end(ap);
2419
2420        if(text_encoding)
2421                extracted_encoding = oscar_encoding_extract(text_encoding);
2422        if(infotype == AIM_GETINFO_GENERALINFO) {
2423                /*Display idle time*/
2424                char buff[256];
2425                struct tm idletime;
2426                if(userinfo->idletime) {
2427                        memset(&idletime, 0, sizeof(struct tm));
2428                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2429                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2430                        idletime.tm_min = userinfo->idletime % 60;
2431                        idletime.tm_sec = 0;
2432                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
2433                        serv_got_crap(gc, "%s: %s", _("Idle Time"), buff);
2434                }
2435               
2436                if(text) {
2437                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2438                        serv_got_crap(gc, "%s\n%s", _("User Info"), utf8);
2439                } else {
2440                        serv_got_crap(gc, _("No user info available."));
2441                }
2442        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2443                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2444                serv_got_crap(gc, "%s\n%s", _("Away Message"), utf8);
2445        }
2446
2447        g_free(utf8);
2448   
2449        return 1;
2450}
2451
2452int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)
2453{
2454        struct gaim_connection * gc = sess->aux_data;
2455        va_list ap;
2456        guint16 type1, type2;
2457        char * sn;
2458
2459        va_start(ap, fr);
2460        type1 = va_arg(ap, int);
2461        sn = va_arg(ap, char*);
2462        type2 = va_arg(ap, int);
2463        va_end(ap);
2464   
2465        if(type2 == 0x0002) {
2466                /* User is typing */
2467                serv_got_typing(gc, sn, 0, 1);
2468        } 
2469        else if (type2 == 0x0001) {
2470                /* User has typed something, but is not actively typing (stale) */
2471                serv_got_typing(gc, sn, 0, 2);
2472        }
2473        else {
2474                /* User has stopped typing */
2475                serv_got_typing(gc, sn, 0, 0);
2476        }       
2477       
2478        return 1;
2479}
2480
2481static char *oscar_get_status_string( struct gaim_connection *gc, int number )
2482{
2483        struct oscar_data *od = gc->proto_data;
2484       
2485        if( ! number & UC_UNAVAILABLE )
2486        {
2487                return( NULL );
2488        }
2489        else if( od->icq )
2490        {
2491                number >>= 7;
2492                if( number & AIM_ICQ_STATE_DND )
2493                        return( "Do Not Disturb" );
2494                else if( number & AIM_ICQ_STATE_OUT )
2495                        return( "Not Available" );
2496                else if( number & AIM_ICQ_STATE_BUSY )
2497                        return( "Occupied" );
2498                else if( number & AIM_ICQ_STATE_INVISIBLE )
2499                        return( "Invisible" );
2500                else
2501                        return( "Away" );
2502        }
2503        else
2504        {
2505                return( "Away" );
2506        }
2507}
2508
2509int oscar_send_typing(struct gaim_connection *gc, char * who, int typing)
2510{
2511        struct oscar_data *od = gc->proto_data;
2512        return( aim_im_sendmtn(od->sess, 1, who, typing ? 0x0002 : 0x0000) );
2513}
2514
2515int oscar_chat_send(struct gaim_connection * gc, int id, char *message)
2516{
2517        struct oscar_data * od = (struct oscar_data*)gc->proto_data;
2518        struct chat_connection * ccon;
2519        int ret;
2520        guint8 len = strlen(message);
2521        guint16 flags;
2522        char *s;
2523       
2524        if(!(ccon = find_oscar_chat(gc, id)))
2525                return -1;
2526               
2527        for (s = message; *s; s++)
2528                if (*s & 128)
2529                        break;
2530       
2531        flags = AIM_CHATFLAGS_NOREFLECT;
2532       
2533        /* Message contains high ASCII chars, time for some translation! */
2534        if (*s) {
2535                s = g_malloc(BUF_LONG);
2536                /* Try if we can put it in an ISO8859-1 string first.
2537                   If we can't, fall back to UTF16. */
2538                if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
2539                        flags |= AIM_CHATFLAGS_ISO_8859_1;
2540                        len = ret;
2541                } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
2542                        flags |= AIM_CHATFLAGS_UNICODE;
2543                        len = ret;
2544                } else {
2545                        /* OOF, translation failed... Oh well.. */
2546                        g_free( s );
2547                        s = message;
2548                }
2549        } else {
2550                s = message;
2551        }
2552               
2553        ret = aim_chat_send_im(od->sess, ccon->conn, flags, s, len);
2554               
2555        if (s != message) {     
2556                g_free(s);
2557  }
2558 
2559  return (ret >= 0);
2560}
2561
2562void oscar_chat_invite(struct gaim_connection * gc, int id, char *message, char *who)
2563{
2564        struct oscar_data * od = (struct oscar_data *)gc->proto_data;
2565        struct chat_connection *ccon = find_oscar_chat(gc, id);
2566       
2567        if (ccon == NULL)
2568                return;
2569       
2570        aim_chat_invite(od->sess, od->conn, who, message ? message : "",
2571                                        ccon->exchange, ccon->name, 0x0);
2572}
2573
2574void oscar_chat_kill(struct gaim_connection *gc, struct chat_connection *cc)
2575{
2576        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2577
2578        /* Notify the conversation window that we've left the chat */
2579        serv_got_chat_left(gc, cc->id);
2580
2581        /* Destroy the chat_connection */
2582        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
2583        if (cc->inpa > 0)
2584                b_event_remove(cc->inpa);
2585        aim_conn_kill(od->sess, &cc->conn);
2586        g_free(cc->name);
2587        g_free(cc->show);
2588        g_free(cc);
2589}
2590
2591void oscar_chat_leave(struct gaim_connection * gc, int id)
2592{
2593        struct chat_connection * ccon = find_oscar_chat(gc, id);
2594
2595        if(ccon == NULL)
2596                return;
2597
2598        oscar_chat_kill(gc, ccon);
2599}
2600
2601int oscar_chat_join(struct gaim_connection * gc, char * name)
2602{
2603    struct oscar_data * od = (struct oscar_data *)gc->proto_data;
2604       
2605        aim_conn_t * cur;
2606
2607        if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
2608       
2609                return (aim_chatnav_createroom(od->sess, cur, name, 4) == 0);
2610       
2611        } else {
2612                struct create_room * cr = g_new0(struct create_room, 1);
2613                cr->exchange = 4;
2614                cr->name = g_strdup(name);
2615                od->create_rooms = g_slist_append(od->create_rooms, cr);
2616                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
2617                return 1;
2618        }
2619}
2620
2621int oscar_chat_open(struct gaim_connection * gc, char *who)
2622{
2623        struct oscar_data * od = (struct oscar_data *)gc->proto_data;
2624        int ret;
2625        static int chat_id = 0;
2626        char * chatname;
2627       
2628        chatname = g_strdup_printf("%s%d", gc->username, chat_id++);
2629 
2630        ret = oscar_chat_join(gc, chatname);
2631
2632        aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);
2633
2634        g_free(chatname);
2635       
2636        return ret;
2637}
2638
2639void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
2640{
2641        oscar_chat_join(inv->gc, inv->name);
2642        g_free(inv->name);
2643        g_free(inv);
2644}
2645
2646void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
2647{
2648        g_free(inv->name);
2649        g_free(inv);
2650}
2651
2652void oscar_init() 
2653{
2654        struct prpl *ret = g_new0(struct prpl, 1);
2655        ret->name = "oscar";
2656        ret->away_states = oscar_away_states;
2657        ret->login = oscar_login;
2658        ret->close = oscar_close;
2659        ret->send_im = oscar_send_im;
2660        ret->get_info = oscar_get_info;
2661        ret->set_away = oscar_set_away;
2662        ret->get_away = oscar_get_away;
2663        ret->add_buddy = oscar_add_buddy;
2664        ret->remove_buddy = oscar_remove_buddy;
2665        ret->chat_send = oscar_chat_send;
2666        ret->chat_invite = oscar_chat_invite;
2667        ret->chat_leave = oscar_chat_leave;
2668        ret->chat_open = oscar_chat_open;
2669        ret->add_permit = oscar_add_permit;
2670        ret->add_deny = oscar_add_deny;
2671        ret->rem_permit = oscar_rem_permit;
2672        ret->rem_deny = oscar_rem_deny;
2673        ret->set_permit_deny = oscar_set_permit_deny;
2674        ret->keepalive = oscar_keepalive;
2675        ret->cmp_buddynames = aim_sncmp;
2676        ret->get_status_string = oscar_get_status_string;
2677        ret->send_typing = oscar_send_typing;
2678
2679        register_protocol(ret);
2680}
Note: See TracBrowser for help on using the repository browser.