source: libfaim/oscar.c @ f36222f

barnowl_perlaimdebianowlrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since f36222f was 07ab1cb, checked in by James M. Kretchmar <kretch@mit.edu>, 21 years ago
Reverted some error messages to makemsg's
  • Property mode set to 100644
File size: 199.3 KB
Line 
1/*
2 * gaim
3 *
4 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 * Some code copyright (C) 1999-2001, Eric Warmenhoven
6 * Some code copyright (C) 2001-2003, Sean Egan
7 * Some code copyright (C) 2001-2003, Mark Doliner <thekingant@users.sourceforge.net>
8 *
9 * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx>
10 * Some libfaim code copyright (C) 2001-2003 Mark Doliner <thekingant@users.sourceforge.net>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 *
26 */
27#include "internal.h"
28
29#include "account.h"
30#include "accountopt.h"
31#include "buddyicon.h"
32#include "conversation.h"
33#include "debug.h"
34#include "ft.h"
35#include "imgstore.h"
36#include "multi.h"
37#include "notify.h"
38#include "privacy.h"
39#include "prpl.h"
40#include "proxy.h"
41#include "request.h"
42#include "util.h"
43#include "html.h"
44
45#include "aim.h"
46#include "md5.h"
47
48/* XXX CORE/UI */
49#include "gtkinternal.h"
50#include "gaim.h"
51
52
53#define UC_AOL          0x02
54#define UC_ADMIN        0x04
55#define UC_UNCONFIRMED  0x08
56#define UC_NORMAL       0x10
57#define UC_AB           0x20
58#define UC_WIRELESS     0x40
59#define UC_HIPTOP       0x80
60
61#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
62
63static GaimPlugin *my_protocol = NULL;
64
65static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_INTEROPERATE;
66static int caps_icq = AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_ICQUTF8 | AIM_CAPS_INTEROPERATE;
67
68static fu8_t features_aim[] = {0x01, 0x01, 0x01, 0x02};
69static fu8_t features_icq[] = {0x01, 0x06};
70
71struct oscar_data {
72        aim_session_t *sess;
73        aim_conn_t *conn;
74
75        guint cnpa;
76        guint paspa;
77        guint emlpa;
78        guint icopa;
79
80        gboolean iconconnecting;
81        gboolean set_icon;
82
83        GSList *create_rooms;
84
85        gboolean conf;
86        gboolean reqemail;
87        gboolean setemail;
88        char *email;
89        gboolean setnick;
90        char *newsn;
91        gboolean chpass;
92        char *oldp;
93        char *newp;
94       
95        GSList *oscar_chats;
96        GSList *direct_ims;
97        GSList *file_transfers;
98        GHashTable *buddyinfo;
99        GSList *requesticon;
100
101        gboolean killme;
102        gboolean icq;
103        GSList *evilhack;
104        guint icontimer;
105        guint getblisttimer;
106
107        struct {
108                guint maxwatchers; /* max users who can watch you */
109                guint maxbuddies; /* max users you can watch */
110                guint maxgroups; /* max groups in server list */
111                guint maxpermits; /* max users on permit list */
112                guint maxdenies; /* max users on deny list */
113                guint maxsiglen; /* max size (bytes) of profile */
114                guint maxawaymsglen; /* max size (bytes) of posted away message */
115        } rights;
116};
117
118struct create_room {
119        char *name;
120        int exchange;
121};
122
123struct chat_connection {
124        char *name;
125        char *show; /* AOL did something funny to us */
126        fu16_t exchange;
127        fu16_t instance;
128        int fd; /* this is redundant since we have the conn below */
129        aim_conn_t *conn;
130        int inpa;
131        int id;
132        GaimConnection *gc; /* i hate this. */
133        GaimConversation *cnv; /* bah. */
134        int maxlen;
135        int maxvis;
136};
137
138struct direct_im {
139        GaimConnection *gc;
140        char name[80];
141        int watcher;
142        aim_conn_t *conn;
143        gboolean connected;
144};
145
146struct ask_direct {
147        GaimConnection *gc;
148        char *sn;
149        char ip[64];
150        fu8_t cookie[8];
151};
152
153/* Various PRPL-specific buddy info that we want to keep track of */
154struct buddyinfo {
155        time_t signon;
156        int caps;
157        gboolean typingnot;
158        gchar *availmsg;
159        fu32_t ipaddr;
160
161        unsigned long ico_me_len;
162        unsigned long ico_me_csum;
163        time_t ico_me_time;
164        gboolean ico_informed;
165
166        unsigned long ico_len;
167        unsigned long ico_csum;
168        time_t ico_time;
169        gboolean ico_need;
170
171        fu16_t iconcsumlen;
172        fu8_t *iconcsum;
173};
174
175struct name_data {
176        GaimConnection *gc;
177        gchar *name;
178        gchar *nick;
179};
180
181static char *msgerrreason[] = {
182        N_("Invalid error"),
183        N_("Invalid SNAC"),
184        N_("Rate to host"),
185        N_("Rate to client"),
186        N_("Not logged in"),
187        N_("Service unavailable"),
188        N_("Service not defined"),
189        N_("Obsolete SNAC"),
190        N_("Not supported by host"),
191        N_("Not supported by client"),
192        N_("Refused by client"),
193        N_("Reply too big"),
194        N_("Responses lost"),
195        N_("Request denied"),
196        N_("Busted SNAC payload"),
197        N_("Insufficient rights"),
198        N_("In local permit/deny"),
199        N_("Too evil (sender)"),
200        N_("Too evil (receiver)"),
201        N_("User temporarily unavailable"),
202        N_("No match"),
203        N_("List overflow"),
204        N_("Request ambiguous"),
205        N_("Queue full"),
206        N_("Not while on AOL")
207};
208static int msgerrreasonlen = 25;
209
210/* All the libfaim->gaim callback functions */
211static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
212static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
213static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
214static int gaim_info_change      (aim_session_t *, aim_frame_t *, ...);
215static int gaim_account_confirm  (aim_session_t *, aim_frame_t *, ...);
216static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
217static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
218static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
219static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
220static int gaim_parse_clientauto (aim_session_t *, aim_frame_t *, ...);
221static int gaim_parse_user_info  (aim_session_t *, aim_frame_t *, ...);
222static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
223static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
224static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
225static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
226static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
227static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
228static int gaim_email_parseupdate(aim_session_t *, aim_frame_t *, ...);
229static int gaim_icon_error       (aim_session_t *, aim_frame_t *, ...);
230static int gaim_icon_parseicon   (aim_session_t *, aim_frame_t *, ...);
231static int oscar_icon_req        (aim_session_t *, aim_frame_t *, ...);
232static int gaim_parse_msgack     (aim_session_t *, aim_frame_t *, ...);
233static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
234static int gaim_parse_evilnotify (aim_session_t *, aim_frame_t *, ...);
235static int gaim_parse_searcherror(aim_session_t *, aim_frame_t *, ...);
236static int gaim_parse_searchreply(aim_session_t *, aim_frame_t *, ...);
237static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
238static int gaim_connerr          (aim_session_t *, aim_frame_t *, ...);
239static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
240static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
241static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
242static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
243static int conninitdone_email    (aim_session_t *, aim_frame_t *, ...);
244static int conninitdone_icon     (aim_session_t *, aim_frame_t *, ...);
245static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
246static int gaim_parse_mtn        (aim_session_t *, aim_frame_t *, ...);
247static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
248static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
249static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
250static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
251static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
252static int gaim_memrequest       (aim_session_t *, aim_frame_t *, ...);
253static int gaim_selfinfo         (aim_session_t *, aim_frame_t *, ...);
254static int gaim_offlinemsg       (aim_session_t *, aim_frame_t *, ...);
255static int gaim_offlinemsgdone   (aim_session_t *, aim_frame_t *, ...);
256static int gaim_icqalias         (aim_session_t *, aim_frame_t *, ...);
257static int gaim_icqinfo          (aim_session_t *, aim_frame_t *, ...);
258static int gaim_popup            (aim_session_t *, aim_frame_t *, ...);
259#ifndef NOSSI
260static int gaim_ssi_parseerr     (aim_session_t *, aim_frame_t *, ...);
261static int gaim_ssi_parserights  (aim_session_t *, aim_frame_t *, ...);
262static int gaim_ssi_parselist    (aim_session_t *, aim_frame_t *, ...);
263static int gaim_ssi_parseack     (aim_session_t *, aim_frame_t *, ...);
264static int gaim_ssi_authgiven    (aim_session_t *, aim_frame_t *, ...);
265static int gaim_ssi_authrequest  (aim_session_t *, aim_frame_t *, ...);
266static int gaim_ssi_authreply    (aim_session_t *, aim_frame_t *, ...);
267static int gaim_ssi_gotadded     (aim_session_t *, aim_frame_t *, ...);
268#endif
269
270/* for DirectIM/image transfer */
271static int gaim_odc_initiate     (aim_session_t *, aim_frame_t *, ...);
272static int gaim_odc_incoming     (aim_session_t *, aim_frame_t *, ...);
273static int gaim_odc_typing       (aim_session_t *, aim_frame_t *, ...);
274static int gaim_odc_update_ui    (aim_session_t *, aim_frame_t *, ...);
275
276/* for file transfer */
277static int oscar_sendfile_estblsh(aim_session_t *, aim_frame_t *, ...);
278static int oscar_sendfile_prompt (aim_session_t *, aim_frame_t *, ...);
279static int oscar_sendfile_ack    (aim_session_t *, aim_frame_t *, ...);
280static int oscar_sendfile_done   (aim_session_t *, aim_frame_t *, ...);
281
282/* for icons */
283static gboolean gaim_icon_timerfunc(gpointer data);
284
285/* prpl actions - remove this at some point */
286static void oscar_set_info(GaimConnection *gc, const char *text);
287
288static void oscar_free_name_data(struct name_data *data) {
289        g_free(data->name);
290        g_free(data->nick);
291        g_free(data);
292}
293
294static void oscar_free_buddyinfo(void *data) {
295        struct buddyinfo *bi = data;
296        g_free(bi->availmsg);
297        g_free(bi->iconcsum);
298        g_free(bi);
299}
300
301static fu32_t oscar_encoding_check(const char *utf8)
302{
303        int i = 0;
304        fu32_t encodingflag = 0;
305
306        /* Determine how we can send this message.  Per the warnings elsewhere
307         * in this file, these little checks determine the simplest encoding
308         * we can use for a given message send using it. */
309        while (utf8[i]) {
310                if ((unsigned char)utf8[i] > 0x7f) {
311                        /* not ASCII! */
312                        encodingflag = AIM_IMFLAGS_ISO_8859_1;
313                        break;
314                }
315                i++;
316        }
317        while (utf8[i]) {
318                /* ISO-8859-1 is 0x00-0xbf in the first byte
319                 * followed by 0xc0-0xc3 in the second */
320                if ((unsigned char)utf8[i] < 0x80) {
321                        i++;
322                        continue;
323                } else if (((unsigned char)utf8[i] & 0xfc) == 0xc0 &&
324                           ((unsigned char)utf8[i + 1] & 0xc0) == 0x80) {
325                        i += 2;
326                        continue;
327                }
328                encodingflag = AIM_IMFLAGS_UNICODE;
329                break;
330        }
331
332        return encodingflag;
333}
334
335static fu32_t oscar_encoding_parse(const char *enc)
336{
337        char *charset;
338
339        /* If anything goes wrong, fall back on ASCII and print a message */
340        if (enc == NULL) {
341                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
342                                   "Encoding was null, that's odd\n");
343                return 0;
344        }
345        charset = strstr(enc, "charset=");
346        if (charset == NULL) {
347                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
348                                   "No charset specified for info, assuming ASCII\n");
349                return 0;
350        }
351        charset += 8;
352        if (!strcmp(charset, "\"us-ascii\"") || !strcmp(charset, "\"utf-8\"")) {
353                /* UTF-8 is our native charset, ASCII is a proper subset */
354                return 0;
355        } else if (!strcmp(charset, "\"iso-8859-1\"")) {
356                return AIM_IMFLAGS_ISO_8859_1;
357        } else if (!strcmp(charset, "\"unicode-2-0\"")) {
358                return AIM_IMFLAGS_UNICODE;
359        } else {
360                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
361                                   "Unrecognized character set '%s', using ASCII\n", charset);
362                return 0;
363        }
364}
365
366gchar *oscar_encoding_to_utf8(const char *encoding, char *text, int textlen)
367{
368        gchar *utf8 = NULL;
369        int flags = oscar_encoding_parse(encoding);
370
371        switch (flags) {
372        case 0:
373                utf8 = g_strndup(text, textlen);
374                break;
375        case AIM_IMFLAGS_ISO_8859_1:
376                utf8 = g_convert(text, textlen, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
377                break;
378        case AIM_IMFLAGS_UNICODE:
379                utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", NULL, NULL, NULL);
380                break;
381        }
382
383        return utf8;
384}
385
386static struct direct_im *find_direct_im(struct oscar_data *od, const char *who) {
387        GSList *d = od->direct_ims;
388        struct direct_im *m = NULL;
389
390        while (d) {
391                m = (struct direct_im *)d->data;
392                if (!aim_sncmp(who, m->name))
393                        return m;
394                d = d->next;
395        }
396
397        return NULL;
398}
399
400static char *extract_name(const char *name) {
401        char *tmp, *x;
402        int i, j;
403
404        if (!name)
405                return NULL;
406       
407        x = strchr(name, '-');
408
409        if (!x) return NULL;
410        x = strchr(++x, '-');
411        if (!x) return NULL;
412        tmp = g_strdup(++x);
413
414        for (i = 0, j = 0; x[i]; i++) {
415                char hex[3];
416                if (x[i] != '%') {
417                        tmp[j++] = x[i];
418                        continue;
419                }
420                strncpy(hex, x + ++i, 2); hex[2] = 0;
421                i++;
422                tmp[j++] = strtol(hex, NULL, 16);
423        }
424
425        tmp[j] = 0;
426        return tmp;
427}
428
429static struct chat_connection *find_oscar_chat(GaimConnection *gc, int id) {
430        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
431        struct chat_connection *c = NULL;
432
433        while (g) {
434                c = (struct chat_connection *)g->data;
435                if (c->id == id)
436                        break;
437                g = g->next;
438                c = NULL;
439        }
440
441        return c;
442}
443
444static struct chat_connection *find_oscar_chat_by_conn(GaimConnection *gc,
445                                                        aim_conn_t *conn) {
446        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
447        struct chat_connection *c = NULL;
448
449        while (g) {
450                c = (struct chat_connection *)g->data;
451                if (c->conn == conn)
452                        break;
453                g = g->next;
454                c = NULL;
455        }
456
457        return c;
458}
459
460static void gaim_odc_disconnect(aim_session_t *sess, aim_conn_t *conn) {
461        GaimConnection *gc = sess->aux_data;
462        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
463        GaimConversation *cnv;
464        struct direct_im *dim;
465        char *sn;
466        char buf[256];
467
468        sn = g_strdup(aim_odc_getsn(conn));
469
470        gaim_debug(GAIM_DEBUG_INFO, "oscar",
471                           "%s disconnected Direct IM.\n", sn);
472
473        dim = find_direct_im(od, sn);
474        od->direct_ims = g_slist_remove(od->direct_ims, dim);
475        gaim_input_remove(dim->watcher);
476
477        if (dim->connected)
478                g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), sn);
479        else 
480                g_snprintf(buf, sizeof buf, _("Direct IM with %s failed"), sn);
481
482        cnv = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc));
483        if (cnv)
484                gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
485
486        gaim_conversation_update_progress(cnv, 0);
487
488        g_free(dim); /* I guess? I don't see it anywhere else... -- mid */
489        g_free(sn);
490
491        return;
492}
493
494static void oscar_callback(gpointer data, gint source, GaimInputCondition condition) {
495        aim_conn_t *conn = (aim_conn_t *)data;
496        aim_session_t *sess = aim_conn_getsess(conn);
497        GaimConnection *gc = sess ? sess->aux_data : NULL;
498        struct oscar_data *od;
499
500        if (!gc) {
501                /* gc is null. we return, else we seg SIGSEG on next line. */
502                gaim_debug(GAIM_DEBUG_INFO, "oscar",
503                                   "oscar callback for closed connection (1).\n");
504                return;
505        }
506     
507        od = (struct oscar_data *)gc->proto_data;
508
509        if (!g_list_find(gaim_connections_get_all(), gc)) {
510                /* oh boy. this is probably bad. i guess the only thing we
511                 * can really do is return? */
512                gaim_debug(GAIM_DEBUG_INFO, "oscar",
513                                   "oscar callback for closed connection (2).\n");
514                gaim_debug(GAIM_DEBUG_MISC, "oscar", "gc = %p\n", gc);
515                return;
516        }
517
518        if (condition & GAIM_INPUT_READ) {
519                if (conn->type == AIM_CONN_TYPE_LISTENER) {
520                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
521                                           "got information on rendezvous listener\n");
522                        if (aim_handlerendconnect(od->sess, conn) < 0) {
523                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
524                                                   "connection error (rendezvous listener)\n");
525                                aim_conn_kill(od->sess, &conn);
526                        }
527                } else {
528                        if (aim_get_command(od->sess, conn) >= 0) {
529                                aim_rxdispatch(od->sess);
530                                if (od->killme) {
531                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar", "Waiting to be destroyed\n");
532                                        return;
533                                }
534                        } else {
535                                if ((conn->type == AIM_CONN_TYPE_BOS) ||
536                                           !(aim_getconn_type(od->sess, AIM_CONN_TYPE_BOS))) {
537                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
538                                                           "major connection error\n");
539                                        gaim_connection_error(gc, _("Disconnected."));
540                                } else if (conn->type == AIM_CONN_TYPE_CHAT) {
541                                        struct chat_connection *c = find_oscar_chat_by_conn(gc, conn);
542                                        char *buf;
543                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
544                                                           "disconnected from chat room %s\n", c->name);
545                                        c->conn = NULL;
546                                        if (c->inpa > 0)
547                                                gaim_input_remove(c->inpa);
548                                        c->inpa = 0;
549                                        c->fd = -1;
550                                        aim_conn_kill(od->sess, &conn);
551                                        buf = g_strdup_printf(_("You have been disconnected from chat room %s."), c->name);
552                                        gaim_notify_error(gc, NULL, buf, NULL);
553                                        g_free(buf);
554                                } else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
555                                        if (od->cnpa > 0)
556                                                gaim_input_remove(od->cnpa);
557                                        od->cnpa = 0;
558                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
559                                                           "removing chatnav input watcher\n");
560                                        while (od->create_rooms) {
561                                                struct create_room *cr = od->create_rooms->data;
562                                                g_free(cr->name);
563                                                od->create_rooms =
564                                                        g_slist_remove(od->create_rooms, cr);
565                                                g_free(cr);
566                                                gaim_notify_error(gc, NULL,
567                                                                                  _("Chat is currently unavailable"),
568                                                                                  NULL);
569                                        }
570                                        aim_conn_kill(od->sess, &conn);
571                                } else if (conn->type == AIM_CONN_TYPE_AUTH) {
572                                        if (od->paspa > 0)
573                                                gaim_input_remove(od->paspa);
574                                        od->paspa = 0;
575                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
576                                                           "removing authconn input watcher\n");
577                                        aim_conn_kill(od->sess, &conn);
578                                } else if (conn->type == AIM_CONN_TYPE_EMAIL) {
579                                        if (od->emlpa > 0)
580                                                gaim_input_remove(od->emlpa);
581                                        od->emlpa = 0;
582                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
583                                                           "removing email input watcher\n");
584                                        aim_conn_kill(od->sess, &conn);
585                                } else if (conn->type == AIM_CONN_TYPE_ICON) {
586                                        if (od->icopa > 0)
587                                                gaim_input_remove(od->icopa);
588                                        od->icopa = 0;
589                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
590                                                           "removing icon input watcher\n");
591                                        aim_conn_kill(od->sess, &conn);
592                                } else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
593                                        if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
594                                                gaim_odc_disconnect(od->sess, conn);
595                                        aim_conn_kill(od->sess, &conn);
596                                } else {
597                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
598                                                           "holy crap! generic connection error! %hu\n",
599                                                           conn->type);
600                                        aim_conn_kill(od->sess, &conn);
601                                }
602                        }
603                }
604        }
605}
606
607static void oscar_debug(aim_session_t *sess, int level, const char *format, va_list va) {
608        char *s = g_strdup_vprintf(format, va);
609        char buf[256];
610        char *t;
611        GaimConnection *gc = sess->aux_data;
612
613        g_snprintf(buf, sizeof(buf), "%s %d: ", gaim_account_get_username(gaim_connection_get_account(gc)), level);
614        t = g_strconcat(buf, s, NULL);
615        gaim_debug(GAIM_DEBUG_INFO, "oscar", t);
616        if (t[strlen(t)-1] != '\n')
617                gaim_debug(GAIM_DEBUG_INFO, NULL, "\n");
618        g_free(t);
619        g_free(s);
620}
621
622static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond)
623{
624        GaimConnection *gc = data;
625        struct oscar_data *od;
626        aim_session_t *sess;
627        aim_conn_t *conn;
628
629        if (!g_list_find(gaim_connections_get_all(), gc)) {
630                close(source);
631                return;
632        }
633
634        od = gc->proto_data;
635        sess = od->sess;
636        conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
637       
638        conn->fd = source;
639
640        if (source < 0) {
641                gaim_connection_error(gc, _("Couldn't connect to host"));
642                return;
643        }
644
645        aim_conn_completeconnect(sess, conn);
646        gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ, oscar_callback, conn);
647        gaim_debug(GAIM_DEBUG_INFO, "oscar",
648                           "Password sent, waiting for response\n");
649}
650
651static void oscar_login(GaimAccount *account) {
652        aim_session_t *sess;
653        aim_conn_t *conn;
654        char buf[256];
655        GaimConnection *gc = gaim_account_get_connection(account);
656        struct oscar_data *od = gc->proto_data = g_new0(struct oscar_data, 1);
657
658        gaim_debug(GAIM_DEBUG_MISC, "oscar", "oscar_login: gc = %p\n", gc);
659
660        if (isdigit(*(gaim_account_get_username(account)))) {
661                od->icq = TRUE;
662        } else {
663                gc->flags |= GAIM_CONNECTION_HTML;
664                gc->flags |= GAIM_CONNECTION_AUTO_RESP;
665        }
666        od->buddyinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, oscar_free_buddyinfo);
667
668        sess = g_new0(aim_session_t, 1);
669
670        aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
671        aim_setdebuggingcb(sess, oscar_debug);
672
673        /* we need an immediate queue because we don't use a while-loop to
674         * see if things need to be sent. */
675        aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
676        od->sess = sess;
677        sess->aux_data = gc;
678
679        conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
680        if (conn == NULL) {
681                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
682                                   "internal connection error\n");
683                gaim_connection_error(gc, _("Unable to login to AIM"));
684                return;
685        }
686
687        g_snprintf(buf, sizeof(buf), _("Signon: %s"), gaim_account_get_username(account));
688        gaim_connection_update_progress(gc, buf, 2, 5);
689
690        aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
691        aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
692        aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
693
694        conn->status |= AIM_CONN_STATUS_INPROGRESS;
695        if (gaim_proxy_connect(account, gaim_account_get_string(account, "server", FAIM_LOGIN_SERVER),
696                          gaim_account_get_int(account, "port", FAIM_LOGIN_PORT),
697                          oscar_login_connect, gc) < 0) {
698                gaim_connection_error(gc, _("Couldn't connect to host"));
699                return;
700        }
701        aim_request_login(sess, conn, gaim_account_get_username(account));
702}
703
704static void oscar_close(GaimConnection *gc) {
705        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
706
707        while (od->oscar_chats) {
708                struct chat_connection *n = od->oscar_chats->data;
709                if (n->inpa > 0)
710                        gaim_input_remove(n->inpa);
711                g_free(n->name);
712                g_free(n->show);
713                od->oscar_chats = g_slist_remove(od->oscar_chats, n);
714                g_free(n);
715        }
716        while (od->direct_ims) {
717                struct direct_im *n = od->direct_ims->data;
718                if (n->watcher > 0)
719                        gaim_input_remove(n->watcher);
720                od->direct_ims = g_slist_remove(od->direct_ims, n);
721                g_free(n);
722        }
723/* BBB */
724        while (od->file_transfers) {
725                GaimXfer *xfer;
726                xfer = (GaimXfer *)od->file_transfers->data;
727                gaim_xfer_destroy(xfer);
728        }
729        while (od->requesticon) {
730                char *sn = od->requesticon->data;
731                od->requesticon = g_slist_remove(od->requesticon, sn);
732                free(sn);
733        }
734        g_hash_table_destroy(od->buddyinfo);
735        while (od->evilhack) {
736                g_free(od->evilhack->data);
737                od->evilhack = g_slist_remove(od->evilhack, od->evilhack->data);
738        }
739        while (od->create_rooms) {
740                struct create_room *cr = od->create_rooms->data;
741                g_free(cr->name);
742                od->create_rooms = g_slist_remove(od->create_rooms, cr);
743                g_free(cr);
744        }
745        if (od->email)
746                g_free(od->email);
747        if (od->newp)
748                g_free(od->newp);
749        if (od->oldp)
750                g_free(od->oldp);
751        if (gc->inpa > 0)
752                gaim_input_remove(gc->inpa);
753        if (od->cnpa > 0)
754                gaim_input_remove(od->cnpa);
755        if (od->paspa > 0)
756                gaim_input_remove(od->paspa);
757        if (od->emlpa > 0)
758                gaim_input_remove(od->emlpa);
759        if (od->icopa > 0)
760                gaim_input_remove(od->icopa);
761        if (od->icontimer > 0)
762                g_source_remove(od->icontimer);
763        if (od->getblisttimer)
764                g_source_remove(od->getblisttimer);
765        aim_session_kill(od->sess);
766        g_free(od->sess);
767        od->sess = NULL;
768        g_free(gc->proto_data);
769        gc->proto_data = NULL;
770        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Signed off.\n");
771}
772
773static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) {
774        GaimConnection *gc = data;
775        struct oscar_data *od;
776        aim_session_t *sess;
777        aim_conn_t *bosconn;
778
779        if (!g_list_find(gaim_connections_get_all(), gc)) {
780                close(source);
781                return;
782        }
783
784        od = gc->proto_data;
785        sess = od->sess;
786        bosconn = od->conn;     
787        bosconn->fd = source;
788
789        if (source < 0) {
790                gaim_connection_error(gc, _("Could Not Connect"));
791                return;
792        }
793
794        aim_conn_completeconnect(sess, bosconn);
795        gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ, oscar_callback, bosconn);
796        gaim_connection_update_progress(gc,
797                        _("Connection established, cookie sent"), 4, 5);
798}
799
800/* BBB */
801/*
802 * This little area in oscar.c is the nexus of file transfer code,
803 * so I wrote a little explanation of what happens.  I am such a
804 * ninja.
805 *
806 * The series of events for a file send is:
807 *  -Create xfer and call gaim_xfer_request (this happens in oscar_ask_sendfile)
808 *  -User chooses a file and oscar_xfer_init is called.  It establishs a
809 *   listening socket, then asks the remote user to connect to us (and
810 *   gives them the file name, port, IP, etc.)
811 *  -They connect to us and we send them an AIM_CB_OFT_PROMPT (this happens
812 *   in oscar_sendfile_estblsh)
813 *  -They send us an AIM_CB_OFT_ACK and then we start sending data
814 *  -When we finish, they send us an AIM_CB_OFT_DONE and they close the
815 *   connection.
816 *  -We get drunk because file transfer kicks ass.
817 *
818 * The series of events for a file receive is:
819 *  -Create xfer and call gaim_xfer request (this happens in incomingim_chan2)
820 *  -Gaim user selects file to name and location to save file to and
821 *   oscar_xfer_init is called
822 *  -It connects to the remote user using the IP they gave us earlier
823 *  -After connecting, they send us an AIM_CB_OFT_PROMPT.  In reply, we send
824 *   them an AIM_CB_OFT_ACK.
825 *  -They begin to send us lots of raw data.
826 *  -When they finish sending data we send an AIM_CB_OFT_DONE and then close
827 *   the connectionn.
828 */
829static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition);
830
831/* XXX - This function is pretty ugly */
832static void oscar_xfer_init(GaimXfer *xfer)
833{
834        struct aim_oft_info *oft_info = xfer->data;
835        GaimConnection *gc = oft_info->sess->aux_data;
836        struct oscar_data *od = gc->proto_data;
837
838        if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
839                int i;
840
841                xfer->filename = g_path_get_basename(xfer->local_filename);
842                strncpy(oft_info->fh.name, xfer->filename, 64);
843                oft_info->fh.totsize = gaim_xfer_get_size(xfer);
844                oft_info->fh.size = gaim_xfer_get_size(xfer);
845                oft_info->fh.checksum = aim_oft_checksum_file(xfer->local_filename);
846
847                /*
848                 * First try the port specified earlier (5190).  If that fails,
849                 * increment by 1 and try again.
850                 */
851                aim_sendfile_listen(od->sess, oft_info);
852                for (i=0; (i<5 && !oft_info->conn); i++) {
853                        xfer->local_port = oft_info->port = oft_info->port + 1;
854                        aim_sendfile_listen(od->sess, oft_info);
855                }
856                gaim_debug(GAIM_DEBUG_MISC, "oscar",
857                                   "port is %d, ip is %s\n",
858                                   xfer->local_port, oft_info->clientip);
859                if (oft_info->conn) {
860                        xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
861                        aim_im_sendch2_sendfile_ask(od->sess, oft_info);
862                        aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED, oscar_sendfile_estblsh, 0);
863                } else {
864                        gaim_notify_error(gc, NULL, _("File Transfer Aborted"),
865                                                          _("Unable to establish listener socket."));
866                        /* XXX - The below line causes a crash because the transfer is canceled before the "Ok" callback on the file selection thing exists, I think */
867                        /* gaim_xfer_cancel_remote(xfer); */
868                }
869        } else if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
870                oft_info->conn = aim_newconn(od->sess, AIM_CONN_TYPE_RENDEZVOUS, NULL);
871                if (oft_info->conn) {
872                        oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
873                        aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_PROMPT, oscar_sendfile_prompt, 0);
874                        oft_info->conn->fd = xfer->fd = gaim_proxy_connect(gaim_connection_get_account(gc), xfer->remote_ip, xfer->remote_port, 
875                                                                      oscar_sendfile_connected, xfer);
876                        if (xfer->fd == -1) {
877                                gaim_notify_error(gc, NULL, _("File Transfer Aborted"),
878                                                                  _("Unable to establish file descriptor."));
879                                /* gaim_xfer_cancel_remote(xfer); */
880                        }
881                } else {
882                        gaim_notify_error(gc, NULL, _("File Transfer Aborted"),
883                                                          _("Unable to create new connection."));
884                        /* gaim_xfer_cancel_remote(xfer); */
885                        /* Try a different port? Ask them to connect to us? */
886                }
887
888        }
889}
890
891static void oscar_xfer_start(GaimXfer *xfer)
892{
893
894        gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_xfer_start\n");
895        /* I'm pretty sure we don't need to do jack here.  Nor Jill. */
896}
897
898static void oscar_xfer_end(GaimXfer *xfer)
899{
900        struct aim_oft_info *oft_info = xfer->data;
901        GaimConnection *gc = oft_info->sess->aux_data;
902        struct oscar_data *od = gc->proto_data;
903
904        gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_xfer_end\n");
905
906        if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
907                oft_info->fh.nrecvd = gaim_xfer_get_bytes_sent(xfer);
908                aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_DONE, oft_info);
909        }
910
911        aim_conn_kill(oft_info->sess, &oft_info->conn);
912        aim_oft_destroyinfo(oft_info);
913        xfer->data = NULL;
914        od->file_transfers = g_slist_remove(od->file_transfers, xfer);
915}
916
917static void oscar_xfer_cancel_send(GaimXfer *xfer)
918{
919        struct aim_oft_info *oft_info = xfer->data;
920        GaimConnection *gc = oft_info->sess->aux_data;
921        struct oscar_data *od = gc->proto_data;
922
923        gaim_debug(GAIM_DEBUG_INFO, "oscar",
924                           "AAA - in oscar_xfer_cancel_send\n");
925
926        aim_im_sendch2_sendfile_cancel(oft_info->sess, oft_info);
927
928        aim_conn_kill(oft_info->sess, &oft_info->conn);
929        aim_oft_destroyinfo(oft_info);
930        xfer->data = NULL;
931        od->file_transfers = g_slist_remove(od->file_transfers, xfer);
932}
933
934static void oscar_xfer_cancel_recv(GaimXfer *xfer)
935{
936        struct aim_oft_info *oft_info = xfer->data;
937        GaimConnection *gc = oft_info->sess->aux_data;
938        struct oscar_data *od = gc->proto_data;
939
940        gaim_debug(GAIM_DEBUG_INFO, "oscar",
941                           "AAA - in oscar_xfer_cancel_recv\n");
942
943        aim_im_sendch2_sendfile_cancel(oft_info->sess, oft_info);
944
945        aim_conn_kill(oft_info->sess, &oft_info->conn);
946        aim_oft_destroyinfo(oft_info);
947        xfer->data = NULL;
948        od->file_transfers = g_slist_remove(od->file_transfers, xfer);
949}
950
951static void oscar_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size)
952{
953        struct aim_oft_info *oft_info = xfer->data;
954
955        if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
956                /*
957                 * If we're done sending, intercept the socket from the core ft code
958                 * and wait for the other guy to send the "done" OFT packet.
959                 */
960                if (gaim_xfer_get_bytes_remaining(xfer) <= 0) {
961                        gaim_input_remove(xfer->watcher);
962                        xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
963                        xfer->fd = 0;
964                        gaim_xfer_set_completed(xfer, TRUE);
965                }
966        } else if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
967                /* Update our rolling checksum.  Like Walmart, yo. */
968                oft_info->fh.recvcsum = aim_oft_checksum_chunk(buffer, size, oft_info->fh.recvcsum);
969        }
970}
971
972static GaimXfer *oscar_find_xfer_by_cookie(GSList *fts, const char *ck)
973{
974        GaimXfer *xfer;
975        struct aim_oft_info *oft_info;
976
977        while (fts) {
978                xfer = fts->data;
979                oft_info = xfer->data;
980
981                if (oft_info && !strcmp(ck, oft_info->cookie))
982                        return xfer;
983
984                fts = g_slist_next(fts);
985        }
986
987        return NULL;
988}
989
990static GaimXfer *oscar_find_xfer_by_conn(GSList *fts, aim_conn_t *conn)
991{
992        GaimXfer *xfer;
993        struct aim_oft_info *oft_info;
994
995        while (fts) {
996                xfer = fts->data;
997                oft_info = xfer->data;
998
999                if (oft_info && (conn == oft_info->conn))
1000                        return xfer;
1001
1002                fts = g_slist_next(fts);
1003        }
1004
1005        return NULL;
1006}
1007
1008static void oscar_ask_sendfile(GaimConnection *gc, const char *destsn) {
1009        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
1010        GaimXfer *xfer;
1011        struct aim_oft_info *oft_info;
1012
1013        /* You want to send a file to someone else, you're so generous */
1014
1015        /* Build the file transfer handle */
1016        xfer = gaim_xfer_new(gaim_connection_get_account(gc), GAIM_XFER_SEND, destsn);
1017        xfer->local_port = 5190;
1018
1019        /* Create the oscar-specific data */
1020        oft_info = aim_oft_createinfo(od->sess, NULL, destsn, xfer->local_ip, xfer->local_port, 0, 0, NULL);
1021        xfer->data = oft_info;
1022
1023         /* Setup our I/O op functions */
1024        gaim_xfer_set_init_fnc(xfer, oscar_xfer_init);
1025        gaim_xfer_set_start_fnc(xfer, oscar_xfer_start);
1026        gaim_xfer_set_end_fnc(xfer, oscar_xfer_end);
1027        gaim_xfer_set_cancel_send_fnc(xfer, oscar_xfer_cancel_send);
1028        gaim_xfer_set_cancel_recv_fnc(xfer, oscar_xfer_cancel_recv);
1029        gaim_xfer_set_ack_fnc(xfer, oscar_xfer_ack);
1030
1031        /* Keep track of this transfer for later */
1032        od->file_transfers = g_slist_append(od->file_transfers, xfer);
1033
1034        /* Now perform the request */
1035        gaim_xfer_request(xfer);
1036}
1037
1038static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
1039        GaimConnection *gc = sess->aux_data;
1040        struct oscar_data *od = gc->proto_data;
1041        GaimAccount *account = gc->account;
1042        aim_conn_t *bosconn;
1043        char *host; int port;
1044        int i, rc;
1045        va_list ap;
1046        struct aim_authresp_info *info;
1047
1048        port = gaim_account_get_int(account, "port", FAIM_LOGIN_PORT);
1049
1050        va_start(ap, fr);
1051        info = va_arg(ap, struct aim_authresp_info *);
1052        va_end(ap);
1053
1054        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1055                           "inside auth_resp (Screen name: %s)\n", info->sn);
1056
1057        if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) {
1058                char buf[256];
1059                switch (info->errorcode) {
1060                case 0x05:
1061                        /* Incorrect nick/password */
1062                        gc->wants_to_die = TRUE;
1063                        gaim_connection_error(gc, _("Incorrect nickname or password."));
1064                        break;
1065                case 0x11:
1066                        /* Suspended account */
1067                        gc->wants_to_die = TRUE;
1068                        gaim_connection_error(gc, _("Your account is currently suspended."));
1069                        break;
1070                case 0x14:
1071                        /* service temporarily unavailable */
1072                        gaim_connection_error(gc, _("The AOL Instant Messenger service is temporarily unavailable."));
1073                        break;
1074                case 0x18:
1075                        /* connecting too frequently */
1076                        gc->wants_to_die = TRUE;
1077                        gaim_connection_error(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."));
1078                        break;
1079                case 0x1c:
1080                        /* client too old */
1081                        gc->wants_to_die = TRUE;
1082                        g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), GAIM_WEBSITE);
1083                        gaim_connection_error(gc, buf);
1084                        break;
1085                default:
1086                        gaim_connection_error(gc, _("Authentication failed"));
1087                        break;
1088                }
1089                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1090                                   "Login Error Code 0x%04hx\n", info->errorcode);
1091                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1092                                   "Error URL: %s\n", info->errorurl);
1093                od->killme = TRUE;
1094                return 1;
1095        }
1096
1097
1098        gaim_debug(GAIM_DEBUG_MISC, "oscar",
1099                           "Reg status: %hu\n", info->regstatus);
1100
1101        if (info->email) {
1102                gaim_debug(GAIM_DEBUG_MISC, "oscar", "Email: %s\n", info->email);
1103        } else {
1104                gaim_debug(GAIM_DEBUG_MISC, "oscar", "Email is NULL\n");
1105        }
1106       
1107        gaim_debug(GAIM_DEBUG_MISC, "oscar", "BOSIP: %s\n", info->bosip);
1108        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1109                           "Closing auth connection...\n");
1110        aim_conn_kill(sess, &fr->conn);
1111
1112        bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL);
1113        if (bosconn == NULL) {
1114                gaim_connection_error(gc, _("Internal Error"));
1115                od->killme = TRUE;
1116                return 0;
1117        }
1118
1119        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1120        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0);
1121        aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, gaim_bosrights, 0);
1122        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
1123        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
1124        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0);
1125        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
1126        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
1127        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
1128        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
1129        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
1130        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
1131        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_CLIENTAUTORESP, gaim_parse_clientauto, 0);
1132        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
1133        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0);
1134        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0);
1135        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, gaim_parse_searchreply, 0);
1136        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
1137        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parse_mtn, 0);
1138        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_user_info, 0);
1139        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0);
1140        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
1141        aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, gaim_icbm_param_info, 0);
1142        aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, gaim_parse_genericerr, 0);
1143        aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, gaim_parse_genericerr, 0);
1144        aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, gaim_parse_genericerr, 0);
1145        aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, gaim_memrequest, 0);
1146        aim_conn_addhandler(sess, bosconn, 0x0001, 0x000f, gaim_selfinfo, 0);
1147        aim_conn_addhandler(sess, bosconn, 0x0001, 0x0021, oscar_icon_req,0);
1148        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
1149        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
1150        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_POP, 0x0002, gaim_popup, 0);
1151        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS, gaim_icqalias, 0);
1152        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0);
1153#ifndef NOSSI
1154        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ERROR, gaim_ssi_parseerr, 0);
1155        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
1156        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
1157        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_NOLIST, gaim_ssi_parselist, 0);
1158        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);
1159        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTH, gaim_ssi_authgiven, 0);
1160        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREQ, gaim_ssi_authrequest, 0);
1161        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREP, gaim_ssi_authreply, 0);
1162        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADDED, gaim_ssi_gotadded, 0);
1163#endif
1164
1165        ((struct oscar_data *)gc->proto_data)->conn = bosconn;
1166        for (i = 0; i < (int)strlen(info->bosip); i++) {
1167                if (info->bosip[i] == ':') {
1168                        port = atoi(&(info->bosip[i+1]));
1169                        break;
1170                }
1171        }
1172        host = g_strndup(info->bosip, i);
1173        bosconn->status |= AIM_CONN_STATUS_INPROGRESS;
1174        rc = gaim_proxy_connect(gc->account, host, port, oscar_bos_connect, gc);
1175        g_free(host);
1176        if (rc < 0) {
1177                gaim_connection_error(gc, _("Could Not Connect"));
1178                od->killme = TRUE;
1179                return 0;
1180        }
1181        aim_sendcookie(sess, bosconn, info->cookielen, info->cookie);
1182        gaim_input_remove(gc->inpa);
1183
1184        return 1;
1185}
1186
1187struct pieceofcrap {
1188        GaimConnection *gc;
1189        unsigned long offset;
1190        unsigned long len;
1191        char *modname;
1192        int fd;
1193        aim_conn_t *conn;
1194        unsigned int inpa;
1195};
1196
1197static void damn_you(gpointer data, gint source, GaimInputCondition c)
1198{
1199        struct pieceofcrap *pos = data;
1200        struct oscar_data *od = pos->gc->proto_data;
1201        char in = '\0';
1202        int x = 0;
1203        unsigned char m[17];
1204
1205        while (read(pos->fd, &in, 1) == 1) {
1206                if (in == '\n')
1207                        x++;
1208                else if (in != '\r')
1209                        x = 0;
1210                if (x == 2)
1211                        break;
1212                in = '\0';
1213        }
1214        if (in != '\n') {
1215                char buf[256];
1216                g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  You may want to use TOC until "
1217                        "this is fixed.  Check %s for updates."), GAIM_WEBSITE);
1218                gaim_notify_warning(pos->gc, NULL,
1219                                                        _("Gaim was Unable to get a valid AIM login hash."),
1220                                                        buf);
1221                gaim_input_remove(pos->inpa);
1222                close(pos->fd);
1223                g_free(pos);
1224                return;
1225        }
1226        read(pos->fd, m, 16);
1227        m[16] = '\0';
1228        gaim_debug(GAIM_DEBUG_MISC, "oscar", "Sending hash: ");
1229        for (x = 0; x < 16; x++)
1230                gaim_debug(GAIM_DEBUG_MISC, NULL, "%02hhx ", (unsigned char)m[x]);
1231
1232        gaim_debug(GAIM_DEBUG_MISC, NULL, "\n");
1233        gaim_input_remove(pos->inpa);
1234        close(pos->fd);
1235        aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
1236        g_free(pos);
1237}
1238
1239static void straight_to_hell(gpointer data, gint source, GaimInputCondition cond) {
1240        struct pieceofcrap *pos = data;
1241        gchar *buf;
1242
1243        pos->fd = source;
1244
1245        if (source < 0) {
1246                buf = g_strdup_printf(_("You may be disconnected shortly.  You may want to use TOC until "
1247                        "this is fixed.  Check %s for updates."), GAIM_WEBSITE);
1248                gaim_notify_warning(pos->gc, NULL,
1249                                                        _("Gaim was Unable to get a valid AIM login hash."),
1250                                                        buf);
1251                g_free(buf);
1252                if (pos->modname)
1253                        g_free(pos->modname);
1254                g_free(pos);
1255                return;
1256        }
1257
1258        buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
1259                        pos->offset, pos->len, pos->modname ? pos->modname : "");
1260        write(pos->fd, buf, strlen(buf));
1261        g_free(buf);
1262        if (pos->modname)
1263                g_free(pos->modname);
1264        pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos);
1265        return;
1266}
1267
1268/* size of icbmui.ocm, the largest module in AIM 3.5 */
1269#define AIM_MAX_FILE_SIZE 98304
1270
1271int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
1272        va_list ap;
1273        struct pieceofcrap *pos;
1274        fu32_t offset, len;
1275        char *modname;
1276
1277        va_start(ap, fr);
1278        offset = va_arg(ap, fu32_t);
1279        len = va_arg(ap, fu32_t);
1280        modname = va_arg(ap, char *);
1281        va_end(ap);
1282
1283        gaim_debug(GAIM_DEBUG_MISC, "oscar",
1284                           "offset: %u, len: %u, file: %s\n",
1285                           offset, len, (modname ? modname : "aim.exe"));
1286
1287        if (len == 0) {
1288                gaim_debug(GAIM_DEBUG_MISC, "oscar", "len is 0, hashing NULL\n");
1289                aim_sendmemblock(sess, fr->conn, offset, len, NULL,
1290                                AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
1291                return 1;
1292        }
1293        /* uncomment this when you're convinced it's right. remember, it's been wrong before.
1294        if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
1295                char *buf;
1296                int i = 8;
1297                if (modname)
1298                        i += strlen(modname);
1299                buf = g_malloc(i);
1300                i = 0;
1301                if (modname) {
1302                        memcpy(buf, modname, strlen(modname));
1303                        i += strlen(modname);
1304                }
1305                buf[i++] = offset & 0xff;
1306                buf[i++] = (offset >> 8) & 0xff;
1307                buf[i++] = (offset >> 16) & 0xff;
1308                buf[i++] = (offset >> 24) & 0xff;
1309                buf[i++] = len & 0xff;
1310                buf[i++] = (len >> 8) & 0xff;
1311                buf[i++] = (len >> 16) & 0xff;
1312                buf[i++] = (len >> 24) & 0xff;
1313                gaim_debug(GAIM_DEBUG_MISC, "oscar", "len + offset is invalid, "
1314                           "hashing request\n");
1315                aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
1316                g_free(buf);
1317                return 1;
1318        }
1319        */
1320
1321        pos = g_new0(struct pieceofcrap, 1);
1322        pos->gc = sess->aux_data;
1323        pos->conn = fr->conn;
1324
1325        pos->offset = offset;
1326        pos->len = len;
1327        pos->modname = modname ? g_strdup(modname) : NULL;
1328
1329        if (gaim_proxy_connect(pos->gc->account, "gaim.sourceforge.net", 80, straight_to_hell, pos) != 0) {
1330                char buf[256];
1331                if (pos->modname)
1332                        g_free(pos->modname);
1333                g_free(pos);
1334                g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  You may want to use TOC until "
1335                        "this is fixed.  Check %s for updates."), GAIM_WEBSITE);
1336                gaim_notify_warning(pos->gc, NULL,
1337                                                        _("Gaim was Unable to get a valid login hash."),
1338                                                        buf);
1339        }
1340
1341        return 1;
1342}
1343
1344static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {
1345        char *key;
1346        va_list ap;
1347        GaimConnection *gc = sess->aux_data;
1348        GaimAccount *account = gaim_connection_get_account(gc);
1349        GaimAccount *ac = gaim_connection_get_account(gc);
1350        struct oscar_data *od = gc->proto_data;
1351
1352        va_start(ap, fr);
1353        key = va_arg(ap, char *);
1354        va_end(ap);
1355
1356        if (od->icq) {
1357                struct client_info_s info = CLIENTINFO_ICQ_KNOWNGOOD;
1358                aim_send_login(sess, fr->conn, gaim_account_get_username(ac),
1359                                           gaim_account_get_password(account), &info, key);
1360        } else {
1361#if 0
1362                struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b};
1363#endif
1364                struct client_info_s info = CLIENTINFO_AIM_KNOWNGOOD;
1365                aim_send_login(sess, fr->conn, gaim_account_get_username(ac),
1366                                           gaim_account_get_password(account), &info, key);
1367        }
1368
1369        return 1;
1370}
1371
1372static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {
1373        GaimConnection *gc = sess->aux_data;
1374        struct chat_connection *chatcon;
1375        static int id = 1;
1376
1377        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, 0x0001, gaim_parse_genericerr, 0);
1378        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
1379        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
1380        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
1381        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
1382
1383        aim_clientready(sess, fr->conn);
1384
1385        chatcon = find_oscar_chat_by_conn(gc, fr->conn);
1386        chatcon->id = id;
1387        chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
1388
1389        return 1;
1390}
1391
1392static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
1393
1394        aim_conn_addhandler(sess, fr->conn, 0x000d, 0x0001, gaim_parse_genericerr, 0);
1395        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
1396
1397        aim_clientready(sess, fr->conn);
1398
1399        aim_chatnav_reqrights(sess, fr->conn);
1400
1401        return 1;
1402}
1403
1404static int conninitdone_email(aim_session_t *sess, aim_frame_t *fr, ...) {
1405
1406        aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0);
1407        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_EML, AIM_CB_EML_MAILSTATUS, gaim_email_parseupdate, 0);
1408
1409        aim_email_sendcookies(sess, fr->conn);
1410        aim_email_activate(sess, fr->conn);
1411        aim_clientready(sess, fr->conn);
1412
1413        return 1;
1414}
1415
1416static int conninitdone_icon(aim_session_t *sess, aim_frame_t *fr, ...) {
1417        GaimConnection *gc = sess->aux_data;
1418        struct oscar_data *od = gc->proto_data;
1419
1420        aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0);
1421        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_ERROR, gaim_icon_error, 0);
1422        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_RESPONSE, gaim_icon_parseicon, 0);
1423
1424        aim_clientready(sess, fr->conn);
1425
1426        od->iconconnecting = FALSE;
1427
1428        if (od->icontimer)
1429                g_source_remove(od->icontimer);
1430        od->icontimer = g_timeout_add(100, gaim_icon_timerfunc, gc);
1431
1432        return 1;
1433}
1434
1435static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) {
1436        GaimConnection *gc = data;
1437        struct oscar_data *od;
1438        aim_session_t *sess;
1439        aim_conn_t *tstconn;
1440
1441        if (!g_list_find(gaim_connections_get_all(), gc)) {
1442                close(source);
1443                return;
1444        }
1445
1446        od = gc->proto_data;
1447        sess = od->sess;
1448        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
1449        tstconn->fd = source;
1450
1451        if (source < 0) {
1452                aim_conn_kill(sess, &tstconn);
1453                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1454                                   "unable to connect to chatnav server\n");
1455                return;
1456        }
1457
1458        aim_conn_completeconnect(sess, tstconn);
1459        od->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1460        gaim_debug(GAIM_DEBUG_INFO, "oscar", "chatnav: connected\n");
1461}
1462
1463static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond)
1464{
1465        GaimConnection *gc = data;
1466        struct oscar_data *od;
1467        aim_session_t *sess;
1468        aim_conn_t *tstconn;
1469
1470        if (!g_list_find(gaim_connections_get_all(), gc)) {
1471                close(source);
1472                return;
1473        }
1474
1475        od = gc->proto_data;
1476        sess = od->sess;
1477        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
1478        tstconn->fd = source;
1479
1480        if (source < 0) {
1481                aim_conn_kill(sess, &tstconn);
1482                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1483                                   "unable to connect to authorizer\n");
1484                return;
1485        }
1486
1487        aim_conn_completeconnect(sess, tstconn);
1488        od->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1489        gaim_debug(GAIM_DEBUG_INFO, "oscar", "admin: connected\n");
1490}
1491
1492static void oscar_chat_connect(gpointer data, gint source, GaimInputCondition cond)
1493{
1494        struct chat_connection *ccon = data;
1495        GaimConnection *gc = ccon->gc;
1496        struct oscar_data *od;
1497        aim_session_t *sess;
1498        aim_conn_t *tstconn;
1499
1500        if (!g_list_find(gaim_connections_get_all(), gc)) {
1501                close(source);
1502                g_free(ccon->show);
1503                g_free(ccon->name);
1504                g_free(ccon);
1505                return;
1506        }
1507
1508        od = gc->proto_data;
1509        sess = od->sess;
1510        tstconn = ccon->conn;
1511        tstconn->fd = source;
1512
1513        if (source < 0) {
1514                aim_conn_kill(sess, &tstconn);
1515                g_free(ccon->show);
1516                g_free(ccon->name);
1517                g_free(ccon);
1518                return;
1519        }
1520
1521        aim_conn_completeconnect(sess, ccon->conn);
1522        ccon->inpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1523        od->oscar_chats = g_slist_append(od->oscar_chats, ccon);
1524}
1525
1526static void oscar_email_connect(gpointer data, gint source, GaimInputCondition cond) {
1527        GaimConnection *gc = data;
1528        struct oscar_data *od;
1529        aim_session_t *sess;
1530        aim_conn_t *tstconn;
1531
1532        if (!g_list_find(gaim_connections_get_all(), gc)) {
1533                close(source);
1534                return;
1535        }
1536
1537        od = gc->proto_data;
1538        sess = od->sess;
1539        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_EMAIL);
1540        tstconn->fd = source;
1541
1542        if (source < 0) {
1543                aim_conn_kill(sess, &tstconn);
1544                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1545                                   "unable to connect to email server\n");
1546                return;
1547        }
1548
1549        aim_conn_completeconnect(sess, tstconn);
1550        od->emlpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1551        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1552                           "email: connected\n");
1553}
1554
1555static void oscar_icon_connect(gpointer data, gint source, GaimInputCondition cond) {
1556        GaimConnection *gc = data;
1557        struct oscar_data *od;
1558        aim_session_t *sess;
1559        aim_conn_t *tstconn;
1560
1561        if (!g_list_find(gaim_connections_get_all(), gc)) {
1562                close(source);
1563                return;
1564        }
1565
1566        od = gc->proto_data;
1567        sess = od->sess;
1568        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_ICON);
1569        tstconn->fd = source;
1570
1571        if (source < 0) {
1572                aim_conn_kill(sess, &tstconn);
1573                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1574                                   "unable to connect to icon server\n");
1575                return;
1576        }
1577
1578        aim_conn_completeconnect(sess, tstconn);
1579        od->icopa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1580        gaim_debug(GAIM_DEBUG_INFO, "oscar", "icon: connected\n");
1581}
1582
1583/* Hrmph. I don't know how to make this look better. --mid */
1584static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
1585        GaimConnection *gc = sess->aux_data;
1586        GaimAccount *account = gaim_connection_get_account(gc);
1587        aim_conn_t *tstconn;
1588        int i;
1589        char *host;
1590        int port;
1591        va_list ap;
1592        struct aim_redirect_data *redir;
1593
1594        port = gaim_account_get_int(account, "port", FAIM_LOGIN_PORT);
1595
1596        va_start(ap, fr);
1597        redir = va_arg(ap, struct aim_redirect_data *);
1598        va_end(ap);
1599
1600        for (i = 0; i < (int)strlen(redir->ip); i++) {
1601                if (redir->ip[i] == ':') {
1602                        port = atoi(&(redir->ip[i+1]));
1603                        break;
1604                }
1605        }
1606        host = g_strndup(redir->ip, i);
1607
1608        switch(redir->group) {
1609        case 0x7: /* Authorizer */
1610                gaim_debug(GAIM_DEBUG_INFO, "oscar",
1611                                   "Reconnecting with authorizor...\n");
1612                tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
1613                if (tstconn == NULL) {
1614                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1615                                           "unable to reconnect with authorizer\n");
1616                        g_free(host);
1617                        return 1;
1618                }
1619                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1620                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0);
1621
1622                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
1623                if (gaim_proxy_connect(account, host, port, oscar_auth_connect, gc) != 0) {
1624                        aim_conn_kill(sess, &tstconn);
1625                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1626                                           "unable to reconnect with authorizer\n");
1627                        g_free(host);
1628                        return 1;
1629                }
1630                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1631        break;
1632
1633        case 0xd: /* ChatNav */
1634                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL);
1635                if (tstconn == NULL) {
1636                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1637                                           "unable to connect to chatnav server\n");
1638                        g_free(host);
1639                        return 1;
1640                }
1641                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1642                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0);
1643
1644                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
1645                if (gaim_proxy_connect(account, host, port, oscar_chatnav_connect, gc) != 0) {
1646                        aim_conn_kill(sess, &tstconn);
1647                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1648                                           "unable to connect to chatnav server\n");
1649                        g_free(host);
1650                        return 1;
1651                }
1652                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1653        break;
1654
1655        case 0xe: { /* Chat */
1656                struct chat_connection *ccon;
1657
1658                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
1659                if (tstconn == NULL) {
1660                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1661                                           "unable to connect to chat server\n");
1662                        g_free(host);
1663                        return 1;
1664                }
1665
1666                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1667                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0);
1668
1669                ccon = g_new0(struct chat_connection, 1);
1670                ccon->conn = tstconn;
1671                ccon->gc = gc;
1672                ccon->fd = -1;
1673                ccon->name = g_strdup(redir->chat.room);
1674                ccon->exchange = redir->chat.exchange;
1675                ccon->instance = redir->chat.instance;
1676                ccon->show = extract_name(redir->chat.room);
1677
1678                ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
1679                if (gaim_proxy_connect(account, host, port, oscar_chat_connect, ccon) != 0) {
1680                        aim_conn_kill(sess, &tstconn);
1681                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1682                                           "unable to connect to chat server\n");
1683                        g_free(host);
1684                        g_free(ccon->show);
1685                        g_free(ccon->name);
1686                        g_free(ccon);
1687                        return 1;
1688                }
1689                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1690                gaim_debug(GAIM_DEBUG_INFO, "oscar",
1691                                   "Connected to chat room %s exchange %hu\n",
1692                                   ccon->name, ccon->exchange);
1693        } break;
1694
1695        case 0x0010: { /* icon */
1696                if (!(tstconn = aim_newconn(sess, AIM_CONN_TYPE_ICON, NULL))) {
1697                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1698                                           "unable to connect to icon server\n");
1699                        g_free(host);
1700                        return 1;
1701                }
1702                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1703                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_icon, 0);
1704
1705                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
1706                if (gaim_proxy_connect(account, host, port, oscar_icon_connect, gc) != 0) {
1707                        aim_conn_kill(sess, &tstconn);
1708                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1709                                           "unable to connect to icon server\n");
1710                        g_free(host);
1711                        return 1;
1712                }
1713                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1714        } break;
1715
1716        case 0x0018: { /* email */
1717                if (!(tstconn = aim_newconn(sess, AIM_CONN_TYPE_EMAIL, NULL))) {
1718                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1719                                           "unable to connect to email server\n");
1720                        g_free(host);
1721                        return 1;
1722                }
1723                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1724                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_email, 0);
1725
1726                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
1727                if (gaim_proxy_connect(account, host, port, oscar_email_connect, gc) != 0) {
1728                        aim_conn_kill(sess, &tstconn);
1729                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1730                                           "unable to connect to email server\n");
1731                        g_free(host);
1732                        return 1;
1733                }
1734                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1735        } break;
1736
1737        default: /* huh? */
1738                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
1739                                   "got redirect for unknown service 0x%04hx\n",
1740                                   redir->group);
1741                break;
1742        }
1743
1744        g_free(host);
1745        return 1;
1746}
1747
1748static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {
1749        GaimConnection *gc = sess->aux_data;
1750        struct oscar_data *od = gc->proto_data;
1751        struct buddyinfo *bi;
1752        time_t time_idle = 0, signon = 0;
1753        int type = 0;
1754        int caps = 0;
1755        va_list ap;
1756        aim_userinfo_t *info;
1757
1758        va_start(ap, fr);
1759        info = va_arg(ap, aim_userinfo_t *);
1760        va_end(ap);
1761
1762        if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
1763                caps = info->capabilities;
1764        if (info->flags & AIM_FLAG_ACTIVEBUDDY)
1765                type |= UC_AB;
1766        if (caps & AIM_CAPS_HIPTOP)
1767                type |= UC_HIPTOP;
1768
1769        if (info->present & AIM_USERINFO_PRESENT_FLAGS) {
1770                if (info->flags & AIM_FLAG_UNCONFIRMED)
1771                        type |= UC_UNCONFIRMED;
1772                if (info->flags & AIM_FLAG_ADMINISTRATOR)
1773                        type |= UC_ADMIN;
1774                if (info->flags & AIM_FLAG_AOL)
1775                        type |= UC_AOL;
1776                if (info->flags & AIM_FLAG_FREE)
1777                        type |= UC_NORMAL;
1778                if (info->flags & AIM_FLAG_AWAY)
1779                        type |= UC_UNAVAILABLE;
1780                if (info->flags & AIM_FLAG_WIRELESS)
1781                        type |= UC_WIRELESS;
1782        }
1783        if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
1784                type = (info->icqinfo.status << 16);
1785                if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
1786                      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
1787                        type |= UC_UNAVAILABLE;
1788                }
1789        }
1790
1791        if (caps & AIM_CAPS_ICQ)
1792                caps ^= AIM_CAPS_ICQ;
1793
1794        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
1795                time(&time_idle);
1796                time_idle -= info->idletime*60;
1797        }
1798
1799        if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
1800                signon = info->onlinesince;
1801        else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
1802                signon = time(NULL) - info->sessionlen;
1803
1804        if (!aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), info->sn))
1805                gaim_connection_set_display_name(gc, info->sn);
1806
1807        bi = g_hash_table_lookup(od->buddyinfo, normalize(info->sn));
1808        if (!bi) {
1809                bi = g_new0(struct buddyinfo, 1);
1810                g_hash_table_insert(od->buddyinfo, g_strdup(normalize(info->sn)), bi);
1811        }
1812        bi->signon = info->onlinesince ? info->onlinesince : (info->sessionlen + time(NULL));
1813        if (caps)
1814                bi->caps = caps;
1815        bi->typingnot = FALSE;
1816        bi->ico_informed = FALSE;
1817        bi->ipaddr = info->icqinfo.ipaddr;
1818
1819        /* Available message stuff */
1820        free(bi->availmsg);
1821        if (info->availmsg)
1822                if (info->availmsg_encoding) {
1823                        gchar *enc = g_strdup_printf("charset=\"%s\"", info->availmsg_encoding);
1824                        bi->availmsg = oscar_encoding_to_utf8(enc, info->availmsg, info->availmsg_len);
1825                        g_free(enc);
1826                } else {
1827                        /* No explicit encoding means utf8.  Yay. */
1828                        bi->availmsg = g_strdup(info->availmsg);
1829                }
1830        else
1831                bi->availmsg = NULL;
1832
1833        /* Server stored icon stuff */
1834        if (info->iconcsumlen) {
1835                char *b16, *saved_b16;
1836                GaimBuddy *b;
1837
1838                free(bi->iconcsum);
1839                bi->iconcsum = malloc(info->iconcsumlen);
1840                memcpy(bi->iconcsum, info->iconcsum, info->iconcsumlen);
1841                bi->iconcsumlen = info->iconcsumlen;
1842                b16 = tobase16(bi->iconcsum, bi->iconcsumlen);
1843                b = gaim_find_buddy(gc->account, info->sn);
1844                saved_b16 = gaim_buddy_get_setting(b, "icon_checksum");
1845                if (!b16 || !saved_b16 || strcmp(b16, saved_b16)) {
1846                        GSList *cur = od->requesticon;
1847                        while (cur && aim_sncmp((char *)cur->data, info->sn))
1848                                cur = cur->next;
1849                        if (!cur) {
1850                                od->requesticon = g_slist_append(od->requesticon, strdup(normalize(info->sn)));
1851                                if (od->icontimer)
1852                                        g_source_remove(od->icontimer);
1853                                od->icontimer = g_timeout_add(500, gaim_icon_timerfunc, gc);
1854                        }
1855                }
1856                g_free(saved_b16);
1857                g_free(b16);
1858        }
1859
1860        serv_got_update(gc, info->sn, 1, (info->warnlevel/10.0) + 0.5, signon, time_idle, type);
1861
1862        return 1;
1863}
1864
1865static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
1866        GaimConnection *gc = sess->aux_data;
1867        struct oscar_data *od = gc->proto_data;
1868        va_list ap;
1869        aim_userinfo_t *info;
1870
1871        va_start(ap, fr);
1872        info = va_arg(ap, aim_userinfo_t *);
1873        va_end(ap);
1874
1875        serv_got_update(gc, info->sn, 0, 0, 0, 0, 0);
1876
1877        g_hash_table_remove(od->buddyinfo, normalize(info->sn));
1878
1879        return 1;
1880}
1881
1882static void cancel_direct_im(struct ask_direct *d) {
1883        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Freeing DirectIM prompts.\n");
1884
1885        g_free(d->sn);
1886        g_free(d);
1887}
1888
1889static void oscar_odc_callback(gpointer data, gint source, GaimInputCondition condition) {
1890        struct direct_im *dim = data;
1891        GaimConnection *gc = dim->gc;
1892        struct oscar_data *od = gc->proto_data;
1893        GaimConversation *cnv;
1894        char buf[256];
1895        struct sockaddr name;
1896        socklen_t name_len = 1;
1897       
1898        if (!g_list_find(gaim_connections_get_all(), gc)) {
1899                g_free(dim);
1900                return;
1901        }
1902
1903        if (source < 0) {
1904                g_free(dim);
1905                return;
1906        }
1907
1908        dim->conn->fd = source;
1909        aim_conn_completeconnect(od->sess, dim->conn);
1910        cnv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, dim->name);
1911
1912        /* This is the best way to see if we're connected or not */
1913        if (getpeername(source, &name, &name_len) == 0) {
1914                g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name);
1915                dim->connected = TRUE;
1916                gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
1917        }
1918        od->direct_ims = g_slist_append(od->direct_ims, dim);
1919       
1920        dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
1921}
1922
1923/* BBB */
1924/*
1925 * This is called after a remote AIM user has connected to us.  We
1926 * want to do some voodoo with the socket file descriptors, add a
1927 * callback or two, and then send the AIM_CB_OFT_PROMPT.
1928 */
1929static int oscar_sendfile_estblsh(aim_session_t *sess, aim_frame_t *fr, ...) {
1930        GaimConnection *gc = sess->aux_data;
1931        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
1932        GaimXfer *xfer;
1933        struct aim_oft_info *oft_info;
1934        va_list ap;
1935        aim_conn_t *conn, *listenerconn;
1936
1937        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1938                           "AAA - in oscar_sendfile_estblsh\n");
1939        va_start(ap, fr);
1940        conn = va_arg(ap, aim_conn_t *);
1941        listenerconn = va_arg(ap, aim_conn_t *);
1942        va_end(ap);
1943
1944        if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, listenerconn)))
1945                return 1;
1946
1947        if (!(oft_info = xfer->data))
1948                return 1;
1949
1950        /* Stop watching listener conn; watch transfer conn instead */
1951        gaim_input_remove(xfer->watcher);
1952        aim_conn_kill(sess, &listenerconn);
1953
1954        oft_info->conn = conn;
1955        xfer->fd = oft_info->conn->fd;
1956
1957        aim_conn_addhandler(sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK, oscar_sendfile_ack, 0);
1958        aim_conn_addhandler(sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE, oscar_sendfile_done, 0);
1959        xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
1960
1961        /* Inform the other user that we are connected and ready to transfer */
1962        aim_oft_sendheader(sess, AIM_CB_OFT_PROMPT, oft_info);
1963
1964        return 0;
1965}
1966
1967/*
1968 * This is the gaim callback passed to gaim_proxy_connect when connecting to another AIM
1969 * user in order to transfer a file.
1970 */
1971static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition) {
1972        GaimXfer *xfer;
1973        struct aim_oft_info *oft_info;
1974
1975        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1976                           "AAA - in oscar_sendfile_connected\n");
1977        if (!(xfer = data))
1978                return;
1979        if (!(oft_info = xfer->data))
1980                return;
1981        if (source < 0)
1982                return;
1983
1984        xfer->fd = source;
1985        oft_info->conn->fd = source;
1986
1987        aim_conn_completeconnect(oft_info->sess, oft_info->conn);
1988        xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
1989
1990        /* Inform the other user that we are connected and ready to transfer */
1991        aim_im_sendch2_sendfile_accept(oft_info->sess, oft_info);
1992
1993        return;
1994}
1995
1996/*
1997 * This is called when a buddy sends us some file info.  This happens when they
1998 * are sending a file to you, and you have just established a connection to them.
1999 * You should send them the exact same info except use the real cookie.  We also
2000 * get like totally ready to like, receive the file, kay?
2001 */
2002static int oscar_sendfile_prompt(aim_session_t *sess, aim_frame_t *fr, ...) {
2003        GaimConnection *gc = sess->aux_data;
2004        struct oscar_data *od = gc->proto_data;
2005        GaimXfer *xfer;
2006        struct aim_oft_info *oft_info;
2007        va_list ap;
2008        aim_conn_t *conn;
2009        fu8_t *cookie;
2010        struct aim_fileheader_t *fh;
2011
2012        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2013                           "AAA - in oscar_sendfile_prompt\n");
2014        va_start(ap, fr);
2015        conn = va_arg(ap, aim_conn_t *);
2016        cookie = va_arg(ap, fu8_t *);
2017        fh = va_arg(ap, struct aim_fileheader_t *);
2018        va_end(ap);
2019
2020        if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn)))
2021                return 1;
2022
2023        if (!(oft_info = xfer->data))
2024                return 1;
2025
2026        /* We want to stop listening with a normal thingy */
2027        gaim_input_remove(xfer->watcher);
2028        xfer->watcher = 0;
2029
2030        /* They sent us some information about the file they're sending */
2031        memcpy(&oft_info->fh, fh, sizeof(*fh));
2032
2033        /* Fill in the cookie */
2034        memcpy(&oft_info->fh.bcookie, oft_info->cookie, 8);
2035
2036        /* XXX - convert the name from UTF-8 to UCS-2 if necessary, and pass the encoding to the call below */
2037        aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_ACK, oft_info);
2038        gaim_xfer_start(xfer, xfer->fd, NULL, 0);
2039
2040        return 0;
2041}
2042
2043/*
2044 * We are sending a file to someone else.  They have just acknowledged our
2045 * prompt, so we want to start sending data like there's no tomorrow.
2046 */
2047static int oscar_sendfile_ack(aim_session_t *sess, aim_frame_t *fr, ...) {
2048        GaimConnection *gc = sess->aux_data;
2049        struct oscar_data *od = gc->proto_data;
2050        GaimXfer *xfer;
2051        va_list ap;
2052        aim_conn_t *conn;
2053        fu8_t *cookie;
2054        struct aim_fileheader_t *fh;
2055
2056        gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_sendfile_ack\n");
2057        va_start(ap, fr);
2058        conn = va_arg(ap, aim_conn_t *);
2059        cookie = va_arg(ap, fu8_t *);
2060        fh = va_arg(ap, struct aim_fileheader_t *);
2061        va_end(ap);
2062
2063        if (!(xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie)))
2064                return 1;
2065
2066        /* We want to stop listening with a normal thingy */
2067        gaim_input_remove(xfer->watcher);
2068        xfer->watcher = 0;
2069
2070        gaim_xfer_start(xfer, xfer->fd, NULL, 0);
2071
2072        return 0;
2073}
2074
2075/*
2076 * We just sent a file to someone.  They said they got it and everything,
2077 * so we can close our direct connection and what not.
2078 */
2079static int oscar_sendfile_done(aim_session_t *sess, aim_frame_t *fr, ...) {
2080        GaimConnection *gc = sess->aux_data;
2081        struct oscar_data *od = gc->proto_data;
2082        GaimXfer *xfer;
2083        va_list ap;
2084        aim_conn_t *conn;
2085        fu8_t *cookie;
2086        struct aim_fileheader_t *fh;
2087
2088        gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_sendfile_done\n");
2089        va_start(ap, fr);
2090        conn = va_arg(ap, aim_conn_t *);
2091        cookie = va_arg(ap, fu8_t *);
2092        fh = va_arg(ap, struct aim_fileheader_t *);
2093        va_end(ap);
2094
2095        if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn)))
2096                return 1;
2097
2098        xfer->fd = conn->fd;
2099        gaim_xfer_end(xfer);
2100
2101        return 0;
2102}
2103
2104static void accept_direct_im(struct ask_direct *d) {
2105        GaimConnection *gc = d->gc;
2106        struct oscar_data *od;
2107        struct direct_im *dim;
2108        char *host; int port = 4443;
2109        int i, rc;
2110
2111        if (!g_list_find(gaim_connections_get_all(), gc)) {
2112                cancel_direct_im(d);
2113                return;
2114        }
2115
2116        od = (struct oscar_data *)gc->proto_data;
2117        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Accepted DirectIM.\n");
2118
2119        dim = find_direct_im(od, d->sn);
2120        if (dim) {
2121                cancel_direct_im(d); /* 40 */
2122                return;
2123        }
2124        dim = g_new0(struct direct_im, 1);
2125        dim->gc = d->gc;
2126        g_snprintf(dim->name, sizeof dim->name, "%s", d->sn);
2127
2128        dim->conn = aim_odc_connect(od->sess, d->sn, NULL, d->cookie);
2129        if (!dim->conn) {
2130                g_free(dim);
2131                cancel_direct_im(d);
2132                return;
2133        }
2134
2135        aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
2136                                gaim_odc_incoming, 0);
2137        aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
2138                                gaim_odc_typing, 0);
2139        aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER,
2140                                gaim_odc_update_ui, 0);
2141        for (i = 0; i < (int)strlen(d->ip); i++) {
2142                if (d->ip[i] == ':') {
2143                        port = atoi(&(d->ip[i+1]));
2144                        break;
2145                }
2146        }
2147        host = g_strndup(d->ip, i);
2148        dim->conn->status |= AIM_CONN_STATUS_INPROGRESS;
2149        rc = gaim_proxy_connect(gc->account, host, port, oscar_odc_callback, dim);
2150        g_free(host);
2151        if (rc < 0) {
2152                aim_conn_kill(od->sess, &dim->conn);
2153                g_free(dim);
2154                cancel_direct_im(d);
2155                return;
2156        }
2157
2158        cancel_direct_im(d);
2159
2160        return;
2161}
2162
2163static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
2164        GaimConnection *gc = sess->aux_data;
2165        struct oscar_data *od = gc->proto_data;
2166        char *tmp;
2167        GaimImFlags flags = 0;
2168        gsize convlen;
2169        GError *err = NULL;
2170        struct buddyinfo *bi;
2171        const char *iconfile;
2172
2173        bi = g_hash_table_lookup(od->buddyinfo, normalize(userinfo->sn));
2174        if (!bi) {
2175                bi = g_new0(struct buddyinfo, 1);
2176                g_hash_table_insert(od->buddyinfo, g_strdup(normalize(userinfo->sn)), bi);
2177        }
2178
2179        if (args->icbmflags & AIM_IMFLAGS_AWAY)
2180                flags |= GAIM_IM_AUTO_RESP;
2181
2182        if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT)
2183                bi->typingnot = TRUE;
2184        else
2185                bi->typingnot = FALSE;
2186
2187        if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) {
2188                gaim_debug(GAIM_DEBUG_MISC, "oscar",
2189                                   "%s has an icon\n", userinfo->sn);
2190                if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) {
2191                        bi->ico_need = TRUE;
2192                        bi->ico_len = args->iconlen;
2193                        bi->ico_csum = args->iconsum;
2194                        bi->ico_time = args->iconstamp;
2195                }
2196        }
2197
2198        if ((iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc))) && 
2199            (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)) {
2200                FILE *file;
2201                struct stat st;
2202
2203                if (!stat(iconfile, &st)) {
2204                        char *buf = g_malloc(st.st_size);
2205                        file = fopen(iconfile, "rb");
2206                        if (file) {
2207                                int len = fread(buf, 1, st.st_size, file);
2208                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
2209                                                   "Sending buddy icon to %s (%d bytes, "
2210                                                   "%lu reported)\n",
2211                                                   userinfo->sn, len, st.st_size);
2212                                aim_im_sendch2_icon(sess, userinfo->sn, buf, st.st_size,
2213                                        st.st_mtime, aimutil_iconsum(buf, st.st_size));
2214                                fclose(file);
2215                        } else
2216                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2217                                                   "Can't open buddy icon file!\n");
2218                        g_free(buf);
2219                } else
2220                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2221                                           "Can't stat buddy icon file!\n");
2222        }
2223
2224        gaim_debug(GAIM_DEBUG_MISC, "oscar",
2225                           "Character set is %hu %hu\n", args->charset, args->charsubset);
2226        if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
2227                /* This message is marked as UNICODE, so we have to
2228                 * convert it to utf-8 before handing it to the gaim core.
2229                 * This conversion should *never* fail, if it does it
2230                 * means that either the incoming ICBM is corrupted or
2231                 * there is something we don't understand about it.
2232                 * For the record, AIM Unicode is big-endian UCS-2 */
2233
2234                gaim_debug(GAIM_DEBUG_INFO, "oscar", "Received UNICODE IM\n");
2235
2236                if (!args->msg || !args->msglen)
2237                        return 1;
2238
2239                tmp = g_convert(args->msg, args->msglen, "UTF-8", "UCS-2BE", NULL, &convlen, &err);
2240                if (err) {
2241                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2242                                           "Unicode IM conversion: %s\n", err->message);
2243                        tmp = strdup(_("(There was an error receiving this message)"));
2244                        g_error_free(err);
2245                }
2246        } else {
2247                /* This will get executed for both AIM_IMFLAGS_ISO_8859_1 and
2248                 * unflagged messages, which are ASCII.  That's OK because
2249                 * ASCII is a strict subset of ISO-8859-1; this should
2250                 * help with compatibility with old, broken versions of
2251                 * gaim (everything before 0.60) and other broken clients
2252                 * that will happily send ISO-8859-1 without marking it as
2253                 * such */
2254                if (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)
2255                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2256                                           "Received ISO-8859-1 IM\n");
2257
2258                if (!args->msg || !args->msglen)
2259                        return 1;
2260
2261                tmp = g_convert(args->msg, args->msglen, "UTF-8", "ISO-8859-1", NULL, &convlen, &err);
2262                if (err) {
2263                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2264                                           "ISO-8859-1 IM conversion: %s\n", err->message);
2265                        tmp = strdup(_("(There was an error receiving this message)"));
2266                        g_error_free(err);
2267                }
2268        }
2269
2270        /* strip_linefeed(tmp); */
2271        serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL));
2272        g_free(tmp);
2273
2274        return 1;
2275}
2276
2277static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
2278        GaimConnection *gc = sess->aux_data;
2279        struct oscar_data *od = gc->proto_data;
2280        const char *username = gaim_account_get_username(gaim_connection_get_account(gc));
2281
2282        if (!args)
2283                return 0;
2284
2285        gaim_debug(GAIM_DEBUG_MISC, "oscar",
2286                           "rendezvous with %s, status is %hu\n",
2287                           userinfo->sn, args->status);
2288
2289        if (args->reqclass & AIM_CAPS_CHAT) {
2290                char *name;
2291                GHashTable *components;
2292
2293                if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange || !args->msg)
2294                        return 1;
2295                components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
2296                                g_free);
2297                name = extract_name(args->info.chat.roominfo.name);
2298                g_hash_table_replace(components, g_strdup("room"), g_strdup(name ? name : args->info.chat.roominfo.name));
2299                g_hash_table_replace(components, g_strdup("exchange"), g_strdup_printf("%d", args->info.chat.roominfo.exchange));
2300                serv_got_chat_invite(gc,
2301                                     name ? name : args->info.chat.roominfo.name,
2302                                     userinfo->sn,
2303                                     args->msg,
2304                                     components);
2305                if (name)
2306                        g_free(name);
2307        } else if (args->reqclass & AIM_CAPS_SENDFILE) {
2308/* BBB */
2309                if (args->status == AIM_RENDEZVOUS_PROPOSE) {
2310                        /* Someone wants to send a file (or files) to us */
2311                        GaimXfer *xfer;
2312                        struct aim_oft_info *oft_info;
2313
2314                        if (!args->cookie || !args->port || !args->verifiedip || 
2315                            !args->info.sendfile.filename || !args->info.sendfile.totsize || 
2316                            !args->info.sendfile.totfiles || !args->reqclass) {
2317                                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2318                                                   "%s tried to send you a file with incomplete "
2319                                                   "information.\n", userinfo->sn);
2320                                if (args->proxyip)
2321                                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2322                                                           "IP for a proxy server was given.  Gaim "
2323                                                           "does not support this yet.\n");
2324                                return 1;
2325                        }
2326
2327                        if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR) {
2328                                /* last char of the ft req is a star, they are sending us a
2329                                 * directory -- remove the star and trailing slash so we dont save
2330                                 * directories that look like 'dirname\*'  -- arl */
2331                                char *tmp = strrchr(args->info.sendfile.filename, '\\');
2332                                if (tmp && (tmp[1] == '*')) {
2333                                        tmp[0] = '\0';
2334                                }
2335                        }
2336
2337                        /* Build the file transfer handle */
2338                        xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, userinfo->sn);
2339                        xfer->remote_ip = g_strdup(args->verifiedip);
2340                        xfer->remote_port = args->port;
2341                        gaim_xfer_set_filename(xfer, args->info.sendfile.filename);
2342                        gaim_xfer_set_size(xfer, args->info.sendfile.totsize);
2343
2344                        /* Create the oscar-specific data */
2345                        oft_info = aim_oft_createinfo(od->sess, args->cookie, userinfo->sn, args->clientip, xfer->remote_port, 0, 0, NULL);
2346                        if (args->proxyip)
2347                                oft_info->proxyip = g_strdup(args->proxyip);
2348                        if (args->verifiedip)
2349                                oft_info->verifiedip = g_strdup(args->verifiedip);
2350                        xfer->data = oft_info;
2351
2352                         /* Setup our I/O op functions */
2353                        gaim_xfer_set_init_fnc(xfer, oscar_xfer_init);
2354                        gaim_xfer_set_start_fnc(xfer, oscar_xfer_start);
2355                        gaim_xfer_set_end_fnc(xfer, oscar_xfer_end);
2356                        gaim_xfer_set_cancel_send_fnc(xfer, oscar_xfer_cancel_send);
2357                        gaim_xfer_set_cancel_recv_fnc(xfer, oscar_xfer_cancel_recv);
2358                        gaim_xfer_set_ack_fnc(xfer, oscar_xfer_ack);
2359
2360                        /*
2361                         * XXX - Should do something with args->msg, args->encoding, and args->language
2362                         *       probably make it apply to all ch2 messages.
2363                         */
2364
2365                        /* Keep track of this transfer for later */
2366                        od->file_transfers = g_slist_append(od->file_transfers, xfer);
2367
2368                        /* Now perform the request */
2369                        gaim_xfer_request(xfer);
2370                } else if (args->status == AIM_RENDEZVOUS_CANCEL) {
2371                        /* The other user wants to cancel a file transfer */
2372                        GaimXfer *xfer;
2373                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2374                                           "AAA - File transfer canceled by remote user\n");
2375                        if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, args->cookie)))
2376                                gaim_xfer_cancel_remote(xfer);
2377                } else if (args->status == AIM_RENDEZVOUS_ACCEPT) {
2378                        /*
2379                         * This gets sent by the receiver of a file
2380                         * as they connect directly to us.  If we don't
2381                         * get this, then maybe a third party connected
2382                         * to us, and we shouldn't send them anything.
2383                         */
2384                } else {
2385                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2386                                           "unknown rendezvous status!\n");
2387                }
2388        } else if (args->reqclass & AIM_CAPS_GETFILE) {
2389        } else if (args->reqclass & AIM_CAPS_VOICE) {
2390        } else if (args->reqclass & AIM_CAPS_BUDDYICON) {
2391                gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc),
2392                                                                          userinfo->sn, args->info.icon.icon,
2393                                                                          args->info.icon.length);
2394        } else if (args->reqclass & AIM_CAPS_DIRECTIM) {
2395                struct ask_direct *d = g_new0(struct ask_direct, 1);
2396                char buf[256];
2397
2398                if (!args->verifiedip) {
2399                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2400                                           "directim kill blocked (%s)\n", userinfo->sn);
2401                        return 1;
2402                }
2403
2404                gaim_debug(GAIM_DEBUG_INFO, "oscar",
2405                                   "%s received direct im request from %s (%s)\n",
2406                                   username, userinfo->sn, args->verifiedip);
2407
2408                d->gc = gc;
2409                d->sn = g_strdup(userinfo->sn);
2410                strncpy(d->ip, args->verifiedip, sizeof(d->ip));
2411                memcpy(d->cookie, args->cookie, 8);
2412                g_snprintf(buf, sizeof buf, _("%s has just asked to directly connect to %s"), userinfo->sn, username);
2413
2414                gaim_request_action(gc, NULL, buf,
2415                                                        _("This requires a direct connection between "
2416                                                          "the two computers and is necessary for IM "
2417                                                          "Images.  Because your IP address will be "
2418                                                          "revealed, this may be considered a privacy "
2419                                                          "risk."), 0, d, 2,
2420                                                        _("Connect"), G_CALLBACK(accept_direct_im),
2421                                                        _("Cancel"), G_CALLBACK(cancel_direct_im));
2422        } else {
2423                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2424                                   "Unknown reqclass %hu\n", args->reqclass);
2425        }
2426
2427        return 1;
2428}
2429
2430/*
2431 * Authorization Functions
2432 * Most of these are callbacks from dialogs.  They're used by both
2433 * methods of authorization (SSI and old-school channel 4 ICBM)
2434 */
2435/* When you ask other people for authorization */
2436static void gaim_auth_request(struct name_data *data, char *msg) {
2437        GaimConnection *gc = data->gc;
2438
2439        if (g_list_find(gaim_connections_get_all(), gc)) {
2440                struct oscar_data *od = gc->proto_data;
2441                GaimBuddy *buddy = gaim_find_buddy(gc->account, data->name);
2442                GaimGroup *group = gaim_find_buddys_group(buddy);
2443                if (buddy && group) {
2444                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2445                                           "ssi: adding buddy %s to group %s\n",
2446                                           buddy->name, group->name);
2447                        aim_ssi_sendauthrequest(od->sess, data->name, msg ? msg : _("Please authorize me so I can add you to my buddy list."));
2448                        if (!aim_ssi_itemlist_finditem(od->sess->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY))
2449                                aim_ssi_addbuddy(od->sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 1);
2450                }
2451        }
2452}
2453
2454static void gaim_auth_request_msgprompt(struct name_data *data) {
2455        gaim_request_input(data->gc, NULL, _("Authorization Request Message:"),
2456                                           NULL, _("Please authorize me!"), TRUE, FALSE,
2457                                           _("OK"), G_CALLBACK(gaim_auth_request),
2458                                           _("Cancel"), G_CALLBACK(oscar_free_name_data),
2459                                           data);
2460}
2461
2462static void gaim_auth_dontrequest(struct name_data *data) {
2463        GaimConnection *gc = data->gc;
2464
2465        if (g_list_find(gaim_connections_get_all(), gc)) {
2466                /* struct oscar_data *od = gc->proto_data; */
2467                /* XXX - Take the buddy out of our buddy list */
2468        }
2469
2470        oscar_free_name_data(data);
2471}
2472
2473static void gaim_auth_sendrequest(GaimConnection *gc, const char *name) {
2474        struct name_data *data = g_new(struct name_data, 1);
2475        GaimBuddy *buddy;
2476        gchar *dialog_msg, *nombre;
2477
2478        buddy = gaim_find_buddy(gc->account, name);
2479        if (buddy && (gaim_get_buddy_alias_only(buddy)))
2480                nombre = g_strdup_printf("%s (%s)", name, gaim_get_buddy_alias_only(buddy));
2481        else
2482                nombre = NULL;
2483
2484        dialog_msg = g_strdup_printf(_("The user %s requires authorization before being added to a buddy list.  Do you want to send an authorization request?"), (nombre ? nombre : name));
2485        data->gc = gc;
2486        data->name = g_strdup(name);
2487        data->nick = NULL;
2488
2489        gaim_request_action(gc, NULL, _("Request Authorization"), dialog_msg,
2490                                                0, data, 2,
2491                                                _("Request Authorization"),
2492                                                G_CALLBACK(gaim_auth_request_msgprompt),
2493                                                _("Cancel"), G_CALLBACK(gaim_auth_dontrequest));
2494
2495        g_free(dialog_msg);
2496        g_free(nombre);
2497}
2498
2499/* When other people ask you for authorization */
2500static void gaim_auth_grant(struct name_data *data) {
2501        GaimConnection *gc = data->gc;
2502
2503        if (g_list_find(gaim_connections_get_all(), gc)) {
2504                struct oscar_data *od = gc->proto_data;
2505#ifdef NOSSI
2506                GaimBuddy *buddy;
2507                gchar message;
2508                message = 0;
2509                buddy = gaim_find_buddy(gc->account, data->name);
2510                aim_im_sendch4(od->sess, data->name, AIM_ICQMSG_AUTHGRANTED, &message);
2511                show_got_added(gc, NULL, data->name, (buddy ? gaim_get_buddy_alias_only(buddy) : NULL), NULL);
2512#else
2513                aim_ssi_sendauthreply(od->sess, data->name, 0x01, NULL);
2514#endif
2515        }
2516
2517        oscar_free_name_data(data);
2518}
2519
2520/* When other people ask you for authorization */
2521static void gaim_auth_dontgrant(struct name_data *data, char *msg) {
2522        GaimConnection *gc = data->gc;
2523
2524        if (g_list_find(gaim_connections_get_all(), gc)) {
2525                struct oscar_data *od = gc->proto_data;
2526#ifdef NOSSI
2527                aim_im_sendch4(od->sess, data->name, AIM_ICQMSG_AUTHDENIED, msg ? msg : _("No reason given."));
2528#else
2529                aim_ssi_sendauthreply(od->sess, data->name, 0x00, msg ? msg : _("No reason given."));
2530#endif
2531        }
2532}
2533
2534static void gaim_auth_dontgrant_msgprompt(struct name_data *data) {
2535        gaim_request_input(data->gc, NULL, _("Authorization Denied Message:"),
2536                                           NULL, _("No reason given."), TRUE, FALSE,
2537                                           _("OK"), G_CALLBACK(gaim_auth_dontgrant),
2538                                           _("Cancel"), G_CALLBACK(oscar_free_name_data),
2539                                           data);
2540}
2541
2542/* When someone sends you contacts  */
2543static void gaim_icq_contactadd(struct name_data *data) {
2544        GaimConnection *gc = data->gc;
2545
2546        if (g_list_find(gaim_connections_get_all(), gc)) {
2547                show_add_buddy(gc, data->name, NULL, data->nick);
2548        }
2549
2550        oscar_free_name_data(data);
2551}
2552
2553static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args, time_t t) {
2554        GaimConnection *gc = sess->aux_data;
2555        gchar **msg1, **msg2;
2556        GError *err = NULL;
2557        int i, numtoks;
2558
2559        if (!args->type || !args->msg || !args->uin)
2560                return 1;
2561
2562        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2563                           "Received a channel 4 message of type 0x%02hhx.\n", args->type);
2564
2565        /* Split up the message at the delimeter character, then convert each string to UTF-8 */
2566        msg1 = g_strsplit(args->msg, "\376", 0);
2567        for (numtoks=0; msg1[numtoks]; numtoks++);
2568        msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *));
2569        for (i=0; msg1[i]; i++) {
2570                strip_linefeed(msg1[i]);
2571                msg2[i] = g_convert(msg1[i], strlen(msg1[i]), "UTF-8", "ISO-8859-1", NULL, NULL, &err);
2572                if (err) {
2573                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2574                                           "Error converting a string from ISO-8859-1 to "
2575                                           "UTF-8 in oscar ICBM channel 4 parsing\n");
2576                        g_error_free(err);
2577                }
2578        }
2579        msg2[i] = NULL;
2580
2581        switch (args->type) {
2582                case 0x01: { /* MacICQ message or basic offline message */
2583                        if (i >= 1) {
2584                                gchar *uin = g_strdup_printf("%u", args->uin);
2585                                if (t) { /* This is an offline message */
2586                                        /* I think this timestamp is in UTC, or something */
2587                                        serv_got_im(gc, uin, msg2[0], 0, t);
2588                                } else { /* This is a message from MacICQ/Miranda */
2589                                        serv_got_im(gc, uin, msg2[0], 0, time(NULL));
2590                                }
2591                                g_free(uin);
2592                        }
2593                } break;
2594
2595                case 0x04: { /* Someone sent you a URL */
2596                        if (i >= 2) {
2597                                gchar *uin = g_strdup_printf("%u", args->uin);
2598                                gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>", msg2[1], msg2[0]);
2599                                serv_got_im(gc, uin, message, 0, time(NULL));
2600                                g_free(uin);
2601                                g_free(message);
2602                        }
2603                } break;
2604
2605                case 0x06: { /* Someone requested authorization */
2606                        if (i >= 6) {
2607                                struct name_data *data = g_new(struct name_data, 1);
2608                                gchar *dialog_msg = g_strdup_printf(_("The user %u wants to add you to their buddy list for the following reason:\n%s"), args->uin, msg2[5] ? msg2[5] : _("No reason given."));
2609                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
2610                                                   "Received an authorization request from UIN %u\n",
2611                                                   args->uin);
2612                                data->gc = gc;
2613                                data->name = g_strdup_printf("%u", args->uin);
2614                                data->nick = NULL;
2615
2616                                gaim_request_action(gc, NULL, _("Authorization Request"),
2617                                                                        dialog_msg, 0, data, 2,
2618                                                                        _("Authorize"),
2619                                                                        G_CALLBACK(gaim_auth_grant),
2620                                                                        _("Deny"),
2621                                                                        G_CALLBACK(gaim_auth_dontgrant_msgprompt));
2622                                g_free(dialog_msg);
2623                        }
2624                } break;
2625
2626                case 0x07: { /* Someone has denied you authorization */
2627                        if (i >= 1) {
2628                                gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your contact list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given."));
2629                                gaim_notify_info(gc, NULL, _("ICQ authorization denied."),
2630                                                                 dialog_msg);
2631                                g_free(dialog_msg);
2632                        }
2633                } break;
2634
2635                case 0x08: { /* Someone has granted you authorization */
2636                        gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your contact list."), args->uin);
2637                        gaim_notify_info(gc, NULL, "ICQ authorization accepted.",
2638                                                         dialog_msg);
2639                        g_free(dialog_msg);
2640                } break;
2641
2642                case 0x09: { /* Message from the Godly ICQ server itself, I think */
2643                        if (i >= 5) {
2644                                gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
2645                                gaim_notify_info(gc, NULL, "ICQ Server Message", dialog_msg);
2646                                g_free(dialog_msg);
2647                        }
2648                } break;
2649
2650                case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
2651                        if (i >= 6) {
2652                                gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
2653                                gaim_notify_info(gc, NULL, "ICQ Page", dialog_msg);
2654                                g_free(dialog_msg);
2655                        }
2656                } break;
2657
2658                case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
2659                        if (i >= 6) {
2660                                gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]);
2661                                gaim_notify_info(gc, NULL, "ICQ Email", dialog_msg);
2662                                g_free(dialog_msg);
2663                        }
2664                } break;
2665
2666                case 0x12: {
2667                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
2668                        /* Someone added you to their contact list? */
2669                } break;
2670
2671                case 0x13: { /* Someone has sent you some ICQ contacts */
2672                        int i, num;
2673                        gchar **text;
2674                        text = g_strsplit(args->msg, "\376", 0);
2675                        if (text) {
2676                                num = 0;
2677                                for (i=0; i<strlen(text[0]); i++)
2678                                        num = num*10 + text[0][i]-48;
2679                                for (i=0; i<num; i++) {
2680                                        struct name_data *data = g_new(struct name_data, 1);
2681                                        gchar *message = g_strdup_printf(_("ICQ user %u has sent you a contact: %s (%s)"), args->uin, text[i*2+2], text[i*2+1]);
2682                                        data->gc = gc;
2683                                        data->name = g_strdup(text[i*2+1]);
2684                                        data->nick = g_strdup(text[i*2+2]);
2685
2686                                        gaim_request_action(gc, NULL, message,
2687                                                                                _("Do you want to add this contact "
2688                                                                                  "to your Buddy List?"),
2689                                                                                0, data, 2,
2690                                                                                _("Add"), G_CALLBACK(gaim_icq_contactadd),
2691                                                                                _("Decline"), G_CALLBACK(oscar_free_name_data));
2692                                        g_free(message);
2693                                }
2694                                g_strfreev(text);
2695                        }
2696                } break;
2697
2698                case 0x1a: { /* Someone has sent you a greeting card or requested contacts? */
2699                        /* This is boring and silly. */
2700                } break;
2701
2702                default: {
2703                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2704                                           "Received a channel 4 message of unknown type "
2705                                           "(type 0x%02hhx).\n", args->type);
2706                } break;
2707        }
2708
2709        g_strfreev(msg1);
2710        g_strfreev(msg2);
2711
2712        return 1;
2713}
2714
2715static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
2716        fu16_t channel;
2717        int ret = 0;
2718        aim_userinfo_t *userinfo;
2719        va_list ap;
2720
2721        va_start(ap, fr);
2722        channel = (fu16_t)va_arg(ap, unsigned int);
2723        userinfo = va_arg(ap, aim_userinfo_t *);
2724
2725        switch (channel) {
2726                case 1: { /* standard message */
2727                        struct aim_incomingim_ch1_args *args;
2728                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
2729                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
2730                } break;
2731
2732                case 2: { /* rendevous */
2733                        struct aim_incomingim_ch2_args *args;
2734                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
2735                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
2736                } break;
2737
2738                case 4: { /* ICQ */
2739                        struct aim_incomingim_ch4_args *args;
2740                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
2741                        ret = incomingim_chan4(sess, fr->conn, userinfo, args, 0);
2742                } break;
2743
2744                default: {
2745                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2746                                           "ICBM received on unsupported channel (channel "
2747                                           "0x%04hx).", channel);
2748                } break;
2749        }
2750
2751        va_end(ap);
2752
2753        return ret;
2754}
2755
2756static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
2757        char *buf;
2758        va_list ap;
2759        fu16_t chan, nummissed, reason;
2760        aim_userinfo_t *userinfo;
2761
2762        va_start(ap, fr);
2763        chan = (fu16_t)va_arg(ap, unsigned int);
2764        userinfo = va_arg(ap, aim_userinfo_t *);
2765        nummissed = (fu16_t)va_arg(ap, unsigned int);
2766        reason = (fu16_t)va_arg(ap, unsigned int);
2767        va_end(ap);
2768
2769        switch(reason) {
2770                case 0: /* Invalid (0) */
2771                        buf = g_strdup_printf(
2772                                   ngettext(
2773                                   "You missed %hu message from %s because it was invalid.",
2774                                   "You missed %hu messages from %s because they were invalid.",
2775                                   nummissed),
2776                                   nummissed,
2777                                   userinfo->sn);
2778                        break;
2779                case 1: /* Message too large */
2780                        buf = g_strdup_printf(
2781                                   ngettext(
2782                                   "You missed %hu message from %s because it was too large.",
2783                                   "You missed %hu messages from %s because they were too large.",
2784                                   nummissed),
2785                                   nummissed,
2786                                   userinfo->sn);
2787                        break;
2788                case 2: /* Rate exceeded */
2789                        buf = g_strdup_printf(
2790                                   ngettext(
2791                                   "You missed %hu message from %s because the rate limit has been exceeded.",
2792                                   "You missed %hu messages from %s because the rate limit has been exceeded.",
2793                                   nummissed),
2794                                   nummissed,
2795                                   userinfo->sn);
2796                        break;
2797                case 3: /* Evil Sender */
2798                        buf = g_strdup_printf(
2799                                   ngettext(
2800                                   "You missed %hu message from %s because he/she was too evil.",
2801                                   "You missed %hu messages from %s because he/she was too evil.",
2802                                   nummissed),
2803                                   nummissed,
2804                                   userinfo->sn);
2805                        break;
2806                case 4: /* Evil Receiver */
2807                        buf = g_strdup_printf(
2808                                   ngettext(
2809                                   "You missed %hu message from %s because you are too evil.",
2810                                   "You missed %hu messages from %s because you are too evil.",
2811                                   nummissed),
2812                                   nummissed,
2813                                   userinfo->sn);
2814                        break;
2815                default:
2816                        buf = g_strdup_printf(
2817                                   ngettext(
2818                                   "You missed %hu message from %s for an unknown reason.",
2819                                   "You missed %hu messages from %s for an unknown reason.",
2820                                   nummissed),
2821                                   nummissed,
2822                                   userinfo->sn);
2823                        break;
2824        }
2825        gaim_notify_error(sess->aux_data, NULL, buf, NULL);
2826        g_free(buf);
2827
2828        return 1;
2829}
2830
2831static char *gaim_icq_status(int state) {
2832        /* Make a cute little string that shows the status of the dude or dudet */
2833        if (state & AIM_ICQ_STATE_CHAT)
2834                return g_strdup_printf(_("Free For Chat"));
2835        else if (state & AIM_ICQ_STATE_DND)
2836                return g_strdup_printf(_("Do Not Disturb"));
2837        else if (state & AIM_ICQ_STATE_OUT)
2838                return g_strdup_printf(_("Not Available"));
2839        else if (state & AIM_ICQ_STATE_BUSY)
2840                return g_strdup_printf(_("Occupied"));
2841        else if (state & AIM_ICQ_STATE_AWAY)
2842                return g_strdup_printf(_("Away"));
2843        else if (state & AIM_ICQ_STATE_WEBAWARE)
2844                return g_strdup_printf(_("Web Aware"));
2845        else if (state & AIM_ICQ_STATE_INVISIBLE)
2846                return g_strdup_printf(_("Invisible"));
2847        else
2848                return g_strdup_printf(_("Online"));
2849}
2850
2851static int gaim_parse_clientauto_ch2(aim_session_t *sess, const char *who, fu16_t reason, const char *cookie) {
2852        GaimConnection *gc = sess->aux_data;
2853        struct oscar_data *od = gc->proto_data;
2854
2855/* BBB */
2856        switch (reason) {
2857                case 3: { /* Decline sendfile. */
2858                        GaimXfer *xfer;
2859                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2860                                           "AAA - Other user declined file transfer\n");
2861                        if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie)))
2862                                gaim_xfer_cancel_remote(xfer);
2863                } break;
2864
2865                default: {
2866                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2867                                           "Received an unknown rendezvous client auto-response "
2868                                           "from %s.  Type 0x%04hx\n", who, reason);
2869                }
2870
2871        }
2872
2873        return 0;
2874}
2875
2876static int gaim_parse_clientauto_ch4(aim_session_t *sess, char *who, fu16_t reason, fu32_t state, char *msg) {
2877        GaimConnection *gc = sess->aux_data;
2878
2879        switch(reason) {
2880                case 0x0003: { /* Reply from an ICQ status message request */
2881                        char *status_msg = gaim_icq_status(state);
2882                        char *dialog_msg, **splitmsg;
2883                        struct oscar_data *od = gc->proto_data;
2884                        GSList *l = od->evilhack;
2885                        gboolean evilhack = FALSE;
2886
2887                        /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
2888                        splitmsg = g_strsplit(msg, "\r\n", 0);
2889
2890                        /* If who is in od->evilhack, then we're just getting the away message, otherwise this
2891                         * will just get appended to the info box (which is already showing). */
2892                        while (l) {
2893                                char *x = l->data;
2894                                if (!strcmp(x, normalize(who))) {
2895                                        evilhack = TRUE;
2896                                        g_free(x);
2897                                        od->evilhack = g_slist_remove(od->evilhack, x);
2898                                        break;
2899                                }
2900                                l = l->next;
2901                        }
2902
2903                        if (evilhack)
2904                                dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<HR>%s"), who, status_msg, g_strjoinv("<BR>", splitmsg));
2905                        else
2906                                dialog_msg = g_strdup_printf(_("<B>Status:</B> %s<HR>%s"), status_msg, g_strjoinv("<BR>", splitmsg));
2907                        g_show_info_text(gc, who, 2, dialog_msg, NULL);
2908
2909                        g_free(status_msg);
2910                        g_free(dialog_msg);
2911                        g_strfreev(splitmsg);
2912                } break;
2913
2914                default: {
2915                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2916                                           "Received an unknown client auto-response from %s.  "
2917                                           "Type 0x%04hx\n", who, reason);
2918                } break;
2919        } /* end of switch */
2920
2921        return 0;
2922}
2923
2924static int gaim_parse_clientauto(aim_session_t *sess, aim_frame_t *fr, ...) {
2925        va_list ap;
2926        fu16_t chan, reason;
2927        char *who;
2928
2929        va_start(ap, fr);
2930        chan = (fu16_t)va_arg(ap, unsigned int);
2931        who = va_arg(ap, char *);
2932        reason = (fu16_t)va_arg(ap, unsigned int);
2933
2934        if (chan == 0x0002) { /* File transfer declined */
2935                char *cookie = va_arg(ap, char *);
2936                return gaim_parse_clientauto_ch2(sess, who, reason, cookie);
2937        } else if (chan == 0x0004) { /* ICQ message */
2938                fu32_t state = 0;
2939                char *msg = NULL;
2940                if (reason == 0x0003) {
2941                        state = va_arg(ap, fu32_t);
2942                        msg = va_arg(ap, char *);
2943                }
2944                return gaim_parse_clientauto_ch4(sess, who, reason, state, msg);
2945        }
2946
2947        va_end(ap);
2948
2949        return 1;
2950}
2951
2952static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
2953        va_list ap;
2954        fu16_t reason;
2955        char *m;
2956
2957        va_start(ap, fr);
2958        reason = (fu16_t) va_arg(ap, unsigned int);
2959        va_end(ap);
2960
2961        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2962                           "snac threw error (reason 0x%04hx: %s)\n", reason,
2963                           (reason < msgerrreasonlen) ? msgerrreason[reason] : "unknown");
2964
2965        m = g_strdup_printf(_("SNAC threw error: %s\n"),
2966                        reason < msgerrreasonlen ? _(msgerrreason[reason]) : _("Unknown error"));
2967        gaim_notify_error(sess->aux_data, NULL, m, NULL);
2968        g_free(m);
2969
2970        return 1;
2971}
2972
2973static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
2974#if 0
2975        GaimConnection *gc = sess->aux_data;
2976        struct oscar_data *od = gc->proto_data;
2977        GaimXfer *xfer;
2978#endif
2979        va_list ap;
2980        fu16_t reason;
2981        char *data, *buf;
2982
2983        va_start(ap, fr);
2984        reason = (fu16_t)va_arg(ap, unsigned int);
2985        data = va_arg(ap, char *);
2986        va_end(ap);
2987
2988        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2989                           "Message error with data %s and reason %hu\n", data, reason);
2990
2991/* BBB */
2992#if 0
2993        /* If this was a file transfer request, data is a cookie */
2994        if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, data))) {
2995                gaim_xfer_cancel_remote(xfer);
2996                return 1;
2997        }
2998#endif
2999
3000        /* Data is assumed to be the destination sn */
3001        buf = g_strdup_printf(_("Your message to %s did not get sent:"), data);
3002        gaim_notify_error(sess->aux_data, NULL, buf,
3003                                          (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("No reason given."));
3004        g_free(buf);
3005
3006        return 1;
3007}
3008
3009static int gaim_parse_mtn(aim_session_t *sess, aim_frame_t *fr, ...) {
3010        GaimConnection *gc = sess->aux_data;
3011        va_list ap;
3012        fu16_t type1, type2;
3013        char *sn;
3014
3015        va_start(ap, fr);
3016        type1 = (fu16_t) va_arg(ap, unsigned int);
3017        sn = va_arg(ap, char *);
3018        type2 = (fu16_t) va_arg(ap, unsigned int);
3019        va_end(ap);
3020
3021        switch (type2) {
3022                case 0x0000: { /* Text has been cleared */
3023                        serv_got_typing_stopped(gc, sn);
3024                } break;
3025
3026                case 0x0001: { /* Paused typing */
3027                        serv_got_typing(gc, sn, 0, GAIM_TYPED);
3028                } break;
3029
3030                case 0x0002: { /* Typing */
3031                        serv_got_typing(gc, sn, 0, GAIM_TYPING);
3032                } break;
3033
3034                default: {
3035                        gaim_debug(GAIM_DEBUG_ERROR, "oscar", "Received unknown typing notification message from %s.  Type1 is 0x%04x and type2 is 0x%04hx.\n", sn, type1, type2);
3036                } break;
3037        }
3038
3039        return 1;
3040}
3041
3042static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
3043        char *buf;
3044        va_list ap;
3045        fu16_t reason;
3046        char *destn;
3047
3048        va_start(ap, fr);
3049        reason = (fu16_t) va_arg(ap, unsigned int);
3050        destn = va_arg(ap, char *);
3051        va_end(ap);
3052
3053        buf = g_strdup_printf(_("User information for %s unavailable:"), destn);
3054        gaim_notify_error(sess->aux_data, NULL, buf,
3055                                          (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("No reason given."));
3056        g_free(buf);
3057
3058        return 1;
3059}
3060
3061#if 0
3062static char *images(int flags) {
3063        static char buf[1024];
3064        g_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
3065                        (flags & AIM_FLAG_ACTIVEBUDDY) ? "<IMG SRC=\"ab_icon.gif\">" : "",
3066                        (flags & AIM_FLAG_UNCONFIRMED) ? "<IMG SRC=\"dt_icon.gif\">" : "",
3067                        (flags & AIM_FLAG_AOL) ? "<IMG SRC=\"aol_icon.gif\">" : "",
3068                        (flags & AIM_FLAG_ICQ) ? "<IMG SRC=\"icq_icon.gif\">" : "",
3069                        (flags & AIM_FLAG_ADMINISTRATOR) ? "<IMG SRC=\"admin_icon.gif\">" : "",
3070                        (flags & AIM_FLAG_FREE) ? "<IMG SRC=\"free_icon.gif\">" : "",
3071                        (flags & AIM_FLAG_WIRELESS) ? "<IMG SRC=\"wireless_icon.gif\">" : "");
3072        return buf;
3073}
3074#endif
3075
3076static char *caps_string(guint caps)
3077{
3078        static char buf[512], *tmp;
3079        int count = 0, i = 0;
3080        guint bit = 1;
3081
3082        if (!caps) {
3083                return NULL;
3084        } else while (bit <= AIM_CAPS_LAST) {
3085                if (bit & caps) {
3086                        switch (bit) {
3087                        case AIM_CAPS_BUDDYICON:
3088                                tmp = _("Buddy Icon");
3089                                break;
3090                        case AIM_CAPS_VOICE:
3091                                tmp = _("Voice");
3092                                break;
3093                        case AIM_CAPS_DIRECTIM:
3094                                tmp = _("Direct IM");
3095                                break;
3096                        case AIM_CAPS_CHAT:
3097                                tmp = _("Chat");
3098                                break;
3099                        case AIM_CAPS_GETFILE:
3100                                tmp = _("Get File");
3101                                break;
3102                        case AIM_CAPS_SENDFILE:
3103                                tmp = _("Send File");
3104                                break;
3105                        case AIM_CAPS_GAMES:
3106                        case AIM_CAPS_GAMES2:
3107                                tmp = _("Games");
3108                                break;
3109                        case AIM_CAPS_SAVESTOCKS:
3110                                tmp = _("Add-Ins");
3111                                break;
3112                        case AIM_CAPS_SENDBUDDYLIST:
3113                                tmp = _("Send Buddy List");
3114                                break;
3115                        case AIM_CAPS_ICQ:
3116                                tmp = _("EveryBuddy Bug");
3117                                break;
3118                        case AIM_CAPS_APINFO:
3119                                tmp = _("AP User");
3120                                break;
3121                        case AIM_CAPS_ICQRTF:
3122                                tmp = _("ICQ RTF");
3123                                break;
3124                        case AIM_CAPS_EMPTY:
3125                                tmp = _("Nihilist");
3126                                break;
3127                        case AIM_CAPS_ICQSERVERRELAY:
3128                                tmp = _("ICQ Server Relay");
3129                                break;
3130                        case AIM_CAPS_ICQUTF8OLD:
3131                                tmp = _("Old ICQ UTF8");
3132                                break;
3133                        case AIM_CAPS_TRILLIANCRYPT:
3134                                tmp = _("Trillian Encryption");
3135                                break;
3136                        case AIM_CAPS_ICQUTF8:
3137                                tmp = _("ICQ UTF8");
3138                                break;
3139                        case AIM_CAPS_HIPTOP:
3140                                tmp = _("Hiptop");
3141                                break;
3142                        case AIM_CAPS_SECUREIM:
3143                                tmp = _("Secure IM");
3144                                break;
3145                        default:
3146                                tmp = NULL;
3147                                break;
3148                        }
3149                        if (tmp)
3150                                i += g_snprintf(buf + i, sizeof(buf) - i, "%s%s", (count ? ", " : ""),
3151                                                tmp);
3152                        count++;
3153                }
3154                bit <<= 1;
3155        }
3156        return buf; 
3157}
3158
3159static int gaim_parse_user_info(aim_session_t *sess, aim_frame_t *fr, ...) {
3160        GaimConnection *gc = sess->aux_data;
3161        struct oscar_data *od = gc->proto_data;
3162        gchar *header;
3163        GSList *l = od->evilhack;
3164        gboolean evilhack = FALSE;
3165        gchar *membersince = NULL, *onlinesince = NULL, *idle = NULL;
3166        va_list ap;
3167        aim_userinfo_t *info;
3168        fu16_t infotype;
3169        char *text_enc = NULL, *text = NULL, *utf8 = NULL;
3170        int text_len;
3171        const char *username = gaim_account_get_username(gaim_connection_get_account(gc));
3172
3173        va_start(ap, fr);
3174        info = va_arg(ap, aim_userinfo_t *);
3175        infotype = (fu16_t) va_arg(ap, unsigned int);
3176        text_enc = va_arg(ap, char *);
3177        text = va_arg(ap, char *);
3178        text_len = va_arg(ap, int);
3179        va_end(ap);
3180
3181        if (text_len > 0) {
3182                if (!(utf8 = oscar_encoding_to_utf8(text_enc, text, text_len))) {
3183                        utf8 = g_strdup(_("<i>Unable to display information because it was sent in an unknown encoding.</i>"));
3184                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
3185                                           "Encountered an unknown encoding while parsing userinfo\n");
3186                }
3187        }
3188
3189        if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) {
3190                onlinesince = g_strdup_printf(_("Online Since : <b>%s</b><br>\n"),
3191                                        asctime(localtime((time_t *)&info->onlinesince)));
3192        }
3193
3194        if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
3195                membersince = g_strdup_printf(_("Member Since : <b>%s</b><br>\n"),
3196                                        asctime(localtime((time_t *)&info->membersince)));
3197        }
3198
3199        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
3200                gchar *itime = sec_to_text(info->idletime*60);
3201                idle = g_strdup_printf(_("Idle : <b>%s</b>"), itime);
3202                g_free(itime);
3203        } else
3204                idle = g_strdup(_("Idle: <b>Active</b>"));
3205
3206        header = g_strdup_printf(_("Username : <b>%s</b>  %s <br>\n"
3207                        "Warning Level : <b>%d %%</b><br>\n"
3208                        "%s"
3209                        "%s"
3210                        "%s\n"
3211                        "<hr>\n"),
3212                        info->sn,
3213                        /* images(info->flags), */
3214                        "",
3215                        (int)((info->warnlevel/10.0) + 0.5),
3216                        onlinesince ? onlinesince : "",
3217                        membersince ? membersince : "",
3218                        idle ? idle : "");
3219
3220        g_free(onlinesince);
3221        g_free(membersince);
3222        g_free(idle);
3223
3224        while (l) {
3225                char *x = l->data;
3226                if (!strcmp(x, normalize(info->sn))) {
3227                        evilhack = TRUE;
3228                        g_free(x);
3229                        od->evilhack = g_slist_remove(od->evilhack, x);
3230                        break;
3231                }
3232                l = l->next;
3233        }
3234
3235        if (infotype == AIM_GETINFO_AWAYMESSAGE) {
3236                if (evilhack) {
3237                        g_show_info_text(gc, info->sn, 2,
3238                                         header,
3239                                         (utf8 && *utf8) ? away_subs(utf8, username) :
3240                                         _("<i>User has no away message</i>"), NULL);
3241                } else {
3242                        g_show_info_text(gc, info->sn, 0,
3243                                         header,
3244                                         (utf8 && *utf8) ? away_subs(utf8, username) : NULL,
3245                                         (utf8 && *utf8) ? "<hr>" : NULL,
3246                                         NULL);
3247                }
3248        } else if (infotype == AIM_GETINFO_CAPABILITIES) {
3249                g_show_info_text(gc, info->sn, 2,
3250                                header,
3251                                "<i>", _("Client Capabilities: "),
3252                                caps_string(info->capabilities),
3253                                "</i>",
3254                                NULL);
3255        } else {
3256                g_show_info_text(gc, info->sn, 1,
3257                                 (utf8 && *utf8) ? away_subs(utf8, username) : _("<i>No Information Provided</i>"),
3258                                 NULL);
3259        }
3260
3261        g_free(header);
3262        g_free(utf8);
3263
3264        return 1;
3265}
3266
3267static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
3268        char *msg;
3269        fu16_t id;
3270        va_list ap;
3271
3272        va_start(ap, fr);
3273        id  = (fu16_t) va_arg(ap, unsigned int);
3274        msg = va_arg(ap, char *);
3275        va_end(ap);
3276
3277        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3278                           "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id);
3279        if (id < 4)
3280                gaim_notify_warning(sess->aux_data, NULL,
3281                                                        _("Your AIM connection may be lost."), NULL);
3282
3283        return 1;
3284}
3285
3286static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
3287        va_list ap;
3288        fu16_t type;
3289        GaimConnection *gc = sess->aux_data;
3290        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
3291
3292        va_start(ap, fr);
3293        type = (fu16_t) va_arg(ap, unsigned int);
3294
3295        switch(type) {
3296                case 0x0002: {
3297                        fu8_t maxrooms;
3298                        struct aim_chat_exchangeinfo *exchanges;
3299                        int exchangecount, i;
3300
3301                        maxrooms = (fu8_t) va_arg(ap, unsigned int);
3302                        exchangecount = va_arg(ap, int);
3303                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
3304
3305                        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3306                                           "chat info: Chat Rights:\n");
3307                        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3308                                           "chat info: \tMax Concurrent Rooms: %hhd\n", maxrooms);
3309                        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3310                                           "chat info: \tExchange List: (%d total)\n", exchangecount);
3311                        for (i = 0; i < exchangecount; i++)
3312                                gaim_debug(GAIM_DEBUG_MISC, "oscar",
3313                                                   "chat info: \t\t%hu    %s\n",
3314                                                   exchanges[i].number, exchanges[i].name ? exchanges[i].name : "");
3315                        while (od->create_rooms) {
3316                                struct create_room *cr = od->create_rooms->data;
3317                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
3318                                                   "creating room %s\n", cr->name);
3319                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
3320                                g_free(cr->name);
3321                                od->create_rooms = g_slist_remove(od->create_rooms, cr);
3322                                g_free(cr);
3323                        }
3324                        }
3325                        break;
3326                case 0x0008: {
3327                        char *fqcn, *name, *ck;
3328                        fu16_t instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
3329                        fu8_t createperms;
3330                        fu32_t createtime;
3331
3332                        fqcn = va_arg(ap, char *);
3333                        instance = (fu16_t)va_arg(ap, unsigned int);
3334                        exchange = (fu16_t)va_arg(ap, unsigned int);
3335                        flags = (fu16_t)va_arg(ap, unsigned int);
3336                        createtime = va_arg(ap, fu32_t);
3337                        maxmsglen = (fu16_t)va_arg(ap, unsigned int);
3338                        maxoccupancy = (fu16_t)va_arg(ap, unsigned int);
3339                        createperms = (fu8_t)va_arg(ap, unsigned int);
3340                        unknown = (fu16_t)va_arg(ap, unsigned int);
3341                        name = va_arg(ap, char *);
3342                        ck = va_arg(ap, char *);
3343
3344                        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3345                                           "created room: %s %hu %hu %hu %u %hu %hu %hhu %hu %s %s\n",
3346                                        fqcn,
3347                                        exchange, instance, flags,
3348                                        createtime,
3349                                        maxmsglen, maxoccupancy, createperms, unknown,
3350                                        name, ck);
3351                        aim_chat_join(od->sess, od->conn, exchange, ck, instance);
3352                        }
3353                        break;
3354                default:
3355                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
3356                                           "chatnav info: unknown type (%04hx)\n", type);
3357                        break;
3358        }
3359
3360        va_end(ap);
3361
3362        return 1;
3363}
3364
3365static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
3366        va_list ap;
3367        int count, i;
3368        aim_userinfo_t *info;
3369        GaimConnection *g = sess->aux_data;
3370
3371        struct chat_connection *c = NULL;
3372
3373        va_start(ap, fr);
3374        count = va_arg(ap, int);
3375        info  = va_arg(ap, aim_userinfo_t *);
3376        va_end(ap);
3377
3378        c = find_oscar_chat_by_conn(g, fr->conn);
3379        if (!c)
3380                return 1;
3381
3382        for (i = 0; i < count; i++)
3383                gaim_chat_add_user(GAIM_CHAT(c->cnv), info[i].sn, NULL);
3384
3385        return 1;
3386}
3387
3388static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
3389        va_list ap;
3390        int count, i;
3391        aim_userinfo_t *info;
3392        GaimConnection *g = sess->aux_data;
3393
3394        struct chat_connection *c = NULL;
3395
3396        va_start(ap, fr);
3397        count = va_arg(ap, int);
3398        info  = va_arg(ap, aim_userinfo_t *);
3399        va_end(ap);
3400
3401        c = find_oscar_chat_by_conn(g, fr->conn);
3402        if (!c)
3403                return 1;
3404
3405        for (i = 0; i < count; i++)
3406                gaim_chat_remove_user(GAIM_CHAT(c->cnv), info[i].sn, NULL);
3407
3408        return 1;
3409}
3410
3411static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
3412        va_list ap;
3413        aim_userinfo_t *userinfo;
3414        struct aim_chat_roominfo *roominfo;
3415        char *roomname;
3416        int usercount;
3417        char *roomdesc;
3418        fu16_t unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
3419        fu32_t creationtime;
3420        GaimConnection *gc = sess->aux_data;
3421        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
3422
3423        va_start(ap, fr);
3424        roominfo = va_arg(ap, struct aim_chat_roominfo *);
3425        roomname = va_arg(ap, char *);
3426        usercount= va_arg(ap, int);
3427        userinfo = va_arg(ap, aim_userinfo_t *);
3428        roomdesc = va_arg(ap, char *);
3429        unknown_c9 = (fu16_t)va_arg(ap, unsigned int);
3430        creationtime = va_arg(ap, fu32_t);
3431        maxmsglen = (fu16_t)va_arg(ap, unsigned int);
3432        unknown_d2 = (fu16_t)va_arg(ap, unsigned int);
3433        unknown_d5 = (fu16_t)va_arg(ap, unsigned int);
3434        maxvisiblemsglen = (fu16_t)va_arg(ap, unsigned int);
3435        va_end(ap);
3436
3437        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3438                           "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n",
3439                           maxmsglen, maxvisiblemsglen);
3440
3441        ccon->maxlen = maxmsglen;
3442        ccon->maxvis = maxvisiblemsglen;
3443
3444        return 1;
3445}
3446
3447static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
3448        GaimConnection *gc = sess->aux_data;
3449        va_list ap;
3450        aim_userinfo_t *info;
3451        char *msg;
3452        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
3453
3454        va_start(ap, fr);
3455        info = va_arg(ap, aim_userinfo_t *);
3456        msg = va_arg(ap, char *);
3457        va_end(ap);
3458
3459        serv_got_chat_in(gc, ccon->id, info->sn, 0, msg, time((time_t)NULL));
3460
3461        return 1;
3462}
3463
3464static int gaim_email_parseupdate(aim_session_t *sess, aim_frame_t *fr, ...) {
3465        va_list ap;
3466        GaimConnection *gc = sess->aux_data;
3467        struct aim_emailinfo *emailinfo;
3468        int havenewmail;
3469
3470        va_start(ap, fr);
3471        emailinfo = va_arg(ap, struct aim_emailinfo *);
3472        havenewmail = va_arg(ap, int);
3473        va_end(ap);
3474
3475        if (emailinfo && gaim_account_get_check_mail(gc->account)) {
3476                gchar *to = g_strdup_printf("%s@%s", gaim_account_get_username(gaim_connection_get_account(gc)), emailinfo->domain);
3477                if (emailinfo->unread && havenewmail)
3478                        gaim_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL, (const char **)&to, (const char **)&emailinfo->url, NULL, NULL);
3479                g_free(to);
3480        }
3481
3482        return 1;
3483}
3484
3485static int gaim_icon_error(aim_session_t *sess, aim_frame_t *fr, ...) {
3486        GaimConnection *gc = sess->aux_data;
3487        struct oscar_data *od = gc->proto_data;
3488        char *sn;
3489
3490        sn = od->requesticon->data;
3491        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3492                           "removing %s from hash table\n", sn);
3493        od->requesticon = g_slist_remove(od->requesticon, sn);
3494        free(sn);
3495
3496        if (od->icontimer)
3497                g_source_remove(od->icontimer);
3498        od->icontimer = g_timeout_add(500, gaim_icon_timerfunc, gc);
3499
3500        return 1;
3501}
3502
3503static int gaim_icon_parseicon(aim_session_t *sess, aim_frame_t *fr, ...) {
3504        GaimConnection *gc = sess->aux_data;
3505        struct oscar_data *od = gc->proto_data;
3506        GSList *cur;
3507        va_list ap;
3508        char *sn;
3509        fu8_t *iconcsum, *icon;
3510        fu16_t iconcsumlen, iconlen;
3511
3512        va_start(ap, fr);
3513        sn = va_arg(ap, char *);
3514        iconcsum = va_arg(ap, fu8_t *);
3515        iconcsumlen = va_arg(ap, int);
3516        icon = va_arg(ap, fu8_t *);
3517        iconlen = va_arg(ap, int);
3518        va_end(ap);
3519
3520        if (iconlen > 0) {
3521                char *b16;
3522                GaimBuddy *b = gaim_find_buddy(gc->account, sn);
3523                gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc),
3524                                                                          sn, icon, iconlen);
3525                b16 = tobase16(iconcsum, iconcsumlen);
3526                if (b16) {
3527                        gaim_buddy_set_setting(b, "icon_checksum", b16);
3528                        gaim_blist_save();
3529                        free(b16);
3530                }
3531        }
3532
3533        cur = od->requesticon;
3534        while (cur) {
3535                char *cursn = cur->data;
3536                if (!aim_sncmp(cursn, sn)) {
3537                        od->requesticon = g_slist_remove(od->requesticon, cursn);
3538                        free(cursn);
3539                        cur = od->requesticon;
3540                } else
3541                        cur = cur->next;
3542        }
3543
3544        if (od->icontimer)
3545                g_source_remove(od->icontimer);
3546        od->icontimer = g_timeout_add(250, gaim_icon_timerfunc, gc);
3547
3548        return 1;
3549}
3550
3551static gboolean gaim_icon_timerfunc(gpointer data) {
3552        GaimConnection *gc = data;
3553        struct oscar_data *od = gc->proto_data;
3554        struct buddyinfo *bi;
3555        aim_conn_t *conn;
3556
3557        conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_ICON);
3558        if (!conn) {
3559                if (!od->iconconnecting) {
3560                        aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_ICON);
3561                        od->iconconnecting = TRUE;
3562                }
3563                return FALSE;
3564        }
3565
3566        if (od->set_icon) {
3567                struct stat st;
3568                const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc));
3569                if (iconfile == NULL) {
3570                        /* Set an empty icon, or something */
3571                } else if (!stat(iconfile, &st)) {
3572                        char *buf = g_malloc(st.st_size);
3573                        FILE *file = fopen(iconfile, "rb");
3574                        if (file) {
3575                                fread(buf, 1, st.st_size, file);
3576                                fclose(file);
3577                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
3578                                           "Uploading icon to icon server\n");
3579                                aim_bart_upload(od->sess, buf, st.st_size);
3580                        } else
3581                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
3582                                           "Can't open buddy icon file!\n");
3583                        g_free(buf);
3584                } else {
3585                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
3586                                   "Can't stat buddy icon file!\n");
3587                }
3588                od->set_icon = FALSE;
3589        }
3590
3591        if (!od->requesticon) {
3592                gaim_debug(GAIM_DEBUG_MISC, "oscar",
3593                                   "no more icons to request\n");
3594                return FALSE;
3595        }
3596
3597        bi = g_hash_table_lookup(od->buddyinfo, (char *)od->requesticon->data);
3598        if (bi && (bi->iconcsumlen > 0)) {
3599                aim_bart_request(od->sess, od->requesticon->data, bi->iconcsum, bi->iconcsumlen);
3600                return FALSE;
3601        } else {
3602                char *sn = od->requesticon->data;
3603                od->requesticon = g_slist_remove(od->requesticon, sn);
3604                free(sn);
3605        }
3606
3607        return TRUE;
3608}
3609
3610/*
3611 * Recieved in response to an IM sent with the AIM_IMFLAGS_ACK option.
3612 */
3613static int gaim_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...) {
3614        va_list ap;
3615        fu16_t type;
3616        char *sn;
3617
3618        va_start(ap, fr);
3619        type = (fu16_t) va_arg(ap, unsigned int);
3620        sn = va_arg(ap, char *);
3621        va_end(ap);
3622
3623        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Sent message to %s.\n", sn);
3624
3625        return 1;
3626}
3627
3628static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
3629        static const char *codes[5] = {
3630                "invalid",
3631                "change",
3632                "warning",
3633                "limit",
3634                "limit cleared",
3635        };
3636        va_list ap;
3637        fu16_t code, rateclass;
3638        fu32_t windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
3639
3640        va_start(ap, fr); 
3641        code = (fu16_t)va_arg(ap, unsigned int);
3642        rateclass= (fu16_t)va_arg(ap, unsigned int);
3643        windowsize = va_arg(ap, fu32_t);
3644        clear = va_arg(ap, fu32_t);
3645        alert = va_arg(ap, fu32_t);
3646        limit = va_arg(ap, fu32_t);
3647        disconnect = va_arg(ap, fu32_t);
3648        currentavg = va_arg(ap, fu32_t);
3649        maxavg = va_arg(ap, fu32_t);
3650        va_end(ap);
3651
3652        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3653                           "rate %s (param ID 0x%04hx): curavg = %u, maxavg = %u, alert at %u, "
3654                     "clear warning at %u, limit at %u, disconnect at %u (window size = %u)\n",
3655                     (code < 5) ? codes[code] : codes[0],
3656                     rateclass,
3657                     currentavg, maxavg,
3658                     alert, clear,
3659                     limit, disconnect,
3660                     windowsize);
3661
3662        /* XXX fix these values */
3663        if (code == AIM_RATE_CODE_CHANGE) {
3664                if (currentavg >= clear)
3665                        aim_conn_setlatency(fr->conn, 0);
3666        } else if (code == AIM_RATE_CODE_WARNING) {
3667                aim_conn_setlatency(fr->conn, windowsize/4);
3668        } else if (code == AIM_RATE_CODE_LIMIT) {
3669                gaim_notify_error(sess->aux_data, NULL, _("Rate limiting error."),
3670                                                  _("The last action you attempted could not be "
3671                                                        "performed because you are over the rate limit. "
3672                                                        "Please wait 10 seconds and try again."));
3673                aim_conn_setlatency(fr->conn, windowsize/2);
3674        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
3675                aim_conn_setlatency(fr->conn, 0);
3676        }
3677
3678        return 1;
3679}
3680
3681static int gaim_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...) {
3682        va_list ap;
3683        fu16_t newevil;
3684        aim_userinfo_t *userinfo;
3685        GaimConnection *gc = sess->aux_data;
3686
3687        va_start(ap, fr);
3688        newevil = (fu16_t) va_arg(ap, unsigned int);
3689        userinfo = va_arg(ap, aim_userinfo_t *);
3690        va_end(ap);
3691
3692        serv_got_eviled(gc, (userinfo && userinfo->sn[0]) ? userinfo->sn : NULL, (newevil/10.0) + 0.5);
3693
3694        return 1;
3695}
3696
3697static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
3698        va_list ap;
3699        aim_userinfo_t *info;
3700        GaimConnection *gc = sess->aux_data;
3701
3702        va_start(ap, fr);
3703        info = va_arg(ap, aim_userinfo_t *);
3704        va_end(ap);
3705
3706        gc->evil = (info->warnlevel/10.0) + 0.5;
3707
3708        if (info->onlinesince)
3709                gc->login_time_official = info->onlinesince;
3710
3711        return 1;
3712}
3713
3714static int gaim_connerr(aim_session_t *sess, aim_frame_t *fr, ...) {
3715        GaimConnection *gc = sess->aux_data;
3716        struct oscar_data *od = gc->proto_data;
3717        va_list ap;
3718        fu16_t code;
3719        char *msg;
3720
3721        va_start(ap, fr);
3722        code = (fu16_t)va_arg(ap, int);
3723        msg = va_arg(ap, char *);
3724        va_end(ap);
3725
3726        gaim_debug(GAIM_DEBUG_INFO, "oscar",
3727                           "Disconnected.  Code is 0x%04x and msg is %s\n", code, msg);
3728        if ((fr) && (fr->conn) && (fr->conn->type == AIM_CONN_TYPE_BOS)) {
3729                if (code == 0x0001) {
3730                        gc->wants_to_die = TRUE;
3731                        gaim_connection_error(gc, _("You have been disconnected because you have signed on with this screen name at another location."));
3732                } else {
3733                        gaim_connection_error(gc, _("You have been signed off for an unknown reason."));
3734                }
3735                od->killme = TRUE;
3736        }
3737
3738        return 1;
3739}
3740
3741static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
3742
3743        aim_reqpersonalinfo(sess, fr->conn);
3744
3745#ifndef NOSSI
3746        gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: requesting ssi list\n");
3747        aim_ssi_reqrights(sess);
3748        aim_ssi_reqdata(sess);
3749#endif
3750
3751        aim_bos_reqlocaterights(sess, fr->conn);
3752        aim_bos_reqbuddyrights(sess, fr->conn);
3753        aim_im_reqparams(sess);
3754        aim_bos_reqrights(sess, fr->conn); /* XXX - Don't call this with ssi? */
3755
3756#ifdef NOSSI
3757        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
3758        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE | AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
3759#endif
3760
3761        return 1;
3762}
3763
3764static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
3765        GaimConnection *gc = sess->aux_data;
3766        struct oscar_data *od = gc->proto_data;
3767
3768        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0003, gaim_info_change, 0);
3769        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0005, gaim_info_change, 0);
3770        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0007, gaim_account_confirm, 0);
3771
3772        aim_clientready(sess, fr->conn);
3773        gaim_debug(GAIM_DEBUG_INFO, "oscar", "connected to admin\n");
3774
3775        if (od->chpass) {
3776                gaim_debug(GAIM_DEBUG_INFO, "oscar", "changing password\n");
3777                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
3778                g_free(od->oldp);
3779                od->oldp = NULL;
3780                g_free(od->newp);
3781                od->newp = NULL;
3782                od->chpass = FALSE;
3783        }
3784        if (od->setnick) {
3785                gaim_debug(GAIM_DEBUG_INFO, "oscar", "formatting screenname\n");
3786                aim_admin_setnick(sess, fr->conn, od->newsn);
3787                g_free(od->newsn);
3788                od->newsn = NULL;
3789                od->setnick = FALSE;
3790        }
3791        if (od->conf) {
3792                gaim_debug(GAIM_DEBUG_INFO, "oscar", "confirming account\n");
3793                aim_admin_reqconfirm(sess, fr->conn);
3794                od->conf = FALSE;
3795        }
3796        if (od->reqemail) {
3797                gaim_debug(GAIM_DEBUG_INFO, "oscar", "requesting email\n");
3798                aim_admin_getinfo(sess, fr->conn, 0x0011);
3799                od->reqemail = FALSE;
3800        }
3801        if (od->setemail) {
3802                gaim_debug(GAIM_DEBUG_INFO, "oscar", "setting email\n");
3803                aim_admin_setemail(sess, fr->conn, od->email);
3804                g_free(od->email);
3805                od->email = NULL;
3806                od->setemail = FALSE;
3807        }
3808
3809        return 1;
3810}
3811
3812static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
3813        struct aim_icbmparameters *params;
3814        va_list ap;
3815
3816        va_start(ap, fr);
3817        params = va_arg(ap, struct aim_icbmparameters *);
3818        va_end(ap);
3819
3820        /* XXX - evidently this crashes on solaris. i have no clue why
3821        gaim_debug(GAIM_DEBUG_MISC, "oscar", "ICBM Parameters: maxchannel = %hu, default flags = 0x%08lx, max msg len = %hu, "
3822                        "max sender evil = %f, max receiver evil = %f, min msg interval = %u\n",
3823                        params->maxchan, params->flags, params->maxmsglen,
3824                        ((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0,
3825                        params->minmsginterval);
3826        */
3827
3828        /* Maybe senderwarn and recverwarn should be user preferences... */
3829        params->flags = 0x0000000b;
3830        params->maxmsglen = 8000;
3831        params->minmsginterval = 0;
3832
3833        aim_im_setparams(sess, params);
3834
3835        return 1;
3836}
3837
3838static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
3839{
3840        GaimConnection *gc = sess->aux_data;
3841        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
3842        va_list ap;
3843        fu16_t maxsiglen;
3844
3845        va_start(ap, fr);
3846        maxsiglen = (fu16_t) va_arg(ap, int);
3847        va_end(ap);
3848
3849        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3850                           "locate rights: max sig len = %d\n", maxsiglen);
3851
3852        od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen;
3853
3854        if (od->icq)
3855                aim_bos_setprofile(sess, fr->conn, NULL, NULL, 0, NULL, NULL, 0, caps_icq);
3856        else
3857                oscar_set_info(gc, gc->account->user_info);
3858
3859        return 1;
3860}
3861
3862static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
3863        va_list ap;
3864        fu16_t maxbuddies, maxwatchers;
3865        GaimConnection *gc = sess->aux_data;
3866        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
3867
3868        va_start(ap, fr);
3869        maxbuddies = (fu16_t) va_arg(ap, unsigned int);
3870        maxwatchers = (fu16_t) va_arg(ap, unsigned int);
3871        va_end(ap);
3872
3873        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3874                           "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers);
3875
3876        od->rights.maxbuddies = (guint)maxbuddies;
3877        od->rights.maxwatchers = (guint)maxwatchers;
3878
3879        return 1;
3880}
3881
3882static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
3883        fu16_t maxpermits, maxdenies;
3884        va_list ap;
3885        GaimConnection *gc = sess->aux_data;
3886        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
3887
3888        va_start(ap, fr);
3889        maxpermits = (fu16_t) va_arg(ap, unsigned int);
3890        maxdenies = (fu16_t) va_arg(ap, unsigned int);
3891        va_end(ap);
3892
3893        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3894                           "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies);
3895
3896        od->rights.maxpermits = (guint)maxpermits;
3897        od->rights.maxdenies = (guint)maxdenies;
3898
3899        gaim_connection_set_state(gc, GAIM_CONNECTED);
3900        serv_finish_login(gc);
3901
3902        gaim_debug(GAIM_DEBUG_INFO, "oscar", "buddy list loaded\n");
3903
3904        aim_clientready(sess, fr->conn);
3905        aim_srv_setavailmsg(sess, NULL);
3906        aim_bos_setidle(sess, fr->conn, 0);
3907
3908        if (od->icq) {
3909                aim_icq_reqofflinemsgs(sess);
3910                aim_icq_hideip(sess);
3911        }
3912
3913        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
3914        if (sess->authinfo->email)
3915                aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_EMAIL);
3916
3917        return 1;
3918}
3919
3920static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
3921        va_list ap;
3922        struct aim_icq_offlinemsg *msg;
3923        struct aim_incomingim_ch4_args args;
3924        time_t t;
3925
3926        va_start(ap, fr);
3927        msg = va_arg(ap, struct aim_icq_offlinemsg *);
3928        va_end(ap);
3929
3930        gaim_debug(GAIM_DEBUG_INFO, "oscar",
3931                           "Received offline message.  Converting to channel 4 ICBM...\n");
3932        args.uin = msg->sender;
3933        args.type = msg->type;
3934        args.flags = msg->flags;
3935        args.msglen = msg->msglen;
3936        args.msg = msg->msg;
3937        t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
3938        incomingim_chan4(sess, fr->conn, NULL, &args, t);
3939
3940        return 1;
3941}
3942
3943static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
3944{
3945        aim_icq_ackofflinemsgs(sess);
3946        return 1;
3947}
3948
3949static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
3950{
3951        GaimConnection *gc = sess->aux_data;
3952        gchar *buf, *tmp, *utf8;
3953        gchar who[16];
3954        GaimBuddy *buddy;
3955        gchar *primary;
3956        va_list ap;
3957        struct aim_icq_info *info;
3958
3959        va_start(ap, fr);
3960        info = va_arg(ap, struct aim_icq_info *);
3961        va_end(ap);
3962
3963        if (!info->uin)
3964                return 0;
3965
3966        g_snprintf(who, sizeof(who), "%u", info->uin);
3967        buf = g_strdup_printf("<b>%s:</b> %s", _("UIN"), who);
3968        if (info->nick && info->nick[0] && (utf8 = gaim_try_conv_to_utf8(info->nick))) {
3969                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Nick"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
3970        }
3971        if (info->first && info->first[0] && (utf8 = gaim_try_conv_to_utf8(info->first))) {
3972                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("First Name"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
3973        }
3974        if (info->last && info->last[0] && (utf8 = gaim_try_conv_to_utf8(info->last))) {
3975                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Last Name"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
3976        }
3977        if (info->email && info->email[0] && (utf8 = gaim_try_conv_to_utf8(info->email))) {
3978                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Email Address"), ":</b> <a href=\"mailto:", utf8, "\">", utf8, "</a>", NULL);  g_free(tmp); g_free(utf8);
3979        }
3980        if (info->numaddresses && info->email2) {
3981                int i;
3982                for (i = 0; i < info->numaddresses; i++) {
3983                        if (info->email2[i] && info->email2[i][0] && (utf8 = gaim_try_conv_to_utf8(info->email2[i]))) {
3984                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Email Address"), ":</b> <a href=\"mailto:", utf8, "\">", utf8, "</a>", NULL);  g_free(tmp); g_free(utf8);
3985                        }
3986                }
3987        }
3988        if (info->mobile && info->mobile[0] && (utf8 = gaim_try_conv_to_utf8(info->mobile))) {
3989                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Mobile Phone"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
3990        }
3991        if (info->gender) {
3992                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Gender"), ":</b> ", info->gender==1 ? _("Female") : _("Male"), NULL);  g_free(tmp);
3993        }
3994        if (info->birthyear || info->birthmonth || info->birthday) {
3995                char date[30];
3996                struct tm tm;
3997                tm.tm_mday = (int)info->birthday;
3998                tm.tm_mon = (int)info->birthmonth-1;
3999                tm.tm_year = (int)info->birthyear-1900;
4000                strftime(date, sizeof(date), "%x", &tm);
4001                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Birthday"), ":</b> ", date, NULL);  g_free(tmp);
4002        }
4003        if (info->age) {
4004                char age[5];
4005                snprintf(age, sizeof(age), "%hhd", info->age);
4006                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Age"), ":</b> ", age, NULL);  g_free(tmp);
4007        }
4008        if (info->personalwebpage && info->personalwebpage[0] && (utf8 = gaim_try_conv_to_utf8(info->personalwebpage))) {
4009                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Personal Web Page"), ":</b> <a href=\"", utf8, "\">", utf8, "</a>", NULL);  g_free(tmp); g_free(utf8);
4010        }
4011        if (info->info && info->info[0] && (utf8 = gaim_try_conv_to_utf8(info->info))) {
4012                tmp = buf;  buf = g_strconcat(tmp, "<hr><b>", _("Additional Information"), ":</b><br>", utf8, NULL);  g_free(tmp); g_free(utf8);
4013        }
4014        tmp = buf;  buf = g_strconcat(tmp, "<hr>\n", NULL);  g_free(tmp);
4015        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
4016                tmp = buf;  buf = g_strconcat(tmp, "<b>", _("Home Address"), ":</b>", NULL);  g_free(tmp);
4017                if (info->homeaddr && info->homeaddr[0] && (utf8 = gaim_try_conv_to_utf8(info->homeaddr))) {
4018                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Address"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4019                }
4020                if (info->homecity && info->homecity[0] && (utf8 = gaim_try_conv_to_utf8(info->homecity))) {
4021                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("City"), ":</b> ", utf8,  NULL);  g_free(tmp); g_free(utf8);
4022                }
4023                if (info->homestate && info->homestate[0] && (utf8 = gaim_try_conv_to_utf8(info->homestate))) {
4024                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("State"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4025                }
4026                if (info->homezip && info->homezip[0] && (utf8 = gaim_try_conv_to_utf8(info->homezip))) {
4027                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Zip Code"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4028                }
4029                tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp);
4030        }
4031        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
4032                tmp = buf;  buf = g_strconcat(tmp, "<b>", _("Work Address"), ":</b>", NULL);  g_free(tmp);
4033                if (info->workaddr && info->workaddr[0] && (utf8 = gaim_try_conv_to_utf8(info->workaddr))) {
4034                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Address"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4035                }
4036                if (info->workcity && info->workcity[0] && (utf8 = gaim_try_conv_to_utf8(info->workcity))) {
4037                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("City"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4038                }
4039                if (info->workstate && info->workstate[0] && (utf8 = gaim_try_conv_to_utf8(info->workstate))) {
4040                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("State"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4041                }
4042                if (info->workzip && info->workzip[0] && (utf8 = gaim_try_conv_to_utf8(info->workzip))) {
4043                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Zip Code"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4044                }
4045                tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp);
4046        }
4047        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
4048                tmp = buf;  buf = g_strconcat(tmp, "<b>", _("Work Information"), ":</b>", NULL);  g_free(tmp);
4049                if (info->workcompany && info->workcompany[0] && (utf8 = gaim_try_conv_to_utf8(info->workcompany))) {
4050                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Company"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4051                }
4052                if (info->workdivision && info->workdivision[0] && (utf8 = gaim_try_conv_to_utf8(info->workdivision))) {
4053                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Division"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4054                }
4055                if (info->workposition && info->workposition[0] && (utf8 = gaim_try_conv_to_utf8(info->workposition))) {
4056                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Position"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4057                }
4058                if (info->workwebpage && info->workwebpage[0] && (utf8 = gaim_try_conv_to_utf8(info->workwebpage))) {
4059                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Web Page"), ":</b> <a href=\"", utf8, "\">", utf8, "</a>", NULL);  g_free(tmp); g_free(utf8);
4060                }
4061                tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp);
4062        }
4063
4064        buddy = gaim_find_buddy(gaim_connection_get_account(gc), who);
4065        primary = g_strdup_printf(_("ICQ Info for %s"), gaim_get_buddy_alias(buddy));
4066        gaim_notify_formatted(gc, NULL, primary, NULL, buf, NULL, NULL);
4067        g_free(primary);
4068        g_free(buf);
4069
4070        return 1;
4071}
4072
4073static int gaim_icqalias(aim_session_t *sess, aim_frame_t *fr, ...)
4074{
4075        GaimConnection *gc = sess->aux_data;
4076        gchar who[16], *utf8;
4077        GaimBuddy *b;
4078        va_list ap;
4079        struct aim_icq_info *info;
4080
4081        va_start(ap, fr);
4082        info = va_arg(ap, struct aim_icq_info *);
4083        va_end(ap);
4084
4085        if (info->uin && info->nick && info->nick[0] && (utf8 = gaim_try_conv_to_utf8(info->nick))) {
4086                g_snprintf(who, sizeof(who), "%u", info->uin);
4087                serv_got_alias(gc, who, utf8);
4088                if ((b = gaim_find_buddy(gc->account, who))) {
4089                        gaim_buddy_set_setting(b, "servernick", utf8);
4090                        gaim_blist_save();
4091                }
4092                g_free(utf8);
4093        }
4094
4095        return 1;
4096}
4097
4098static int gaim_popup(aim_session_t *sess, aim_frame_t *fr, ...)
4099{
4100        char *msg, *url;
4101        fu16_t wid, hei, delay;
4102        va_list ap;
4103
4104        va_start(ap, fr);
4105        msg = va_arg(ap, char *);
4106        url = va_arg(ap, char *);
4107        wid = (fu16_t) va_arg(ap, int);
4108        hei = (fu16_t) va_arg(ap, int);
4109        delay = (fu16_t) va_arg(ap, int);
4110        va_end(ap);
4111
4112        serv_got_popup(msg, url, wid, hei);
4113
4114        return 1;
4115}
4116
4117static int gaim_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...) {
4118        GaimConnection *gc = sess->aux_data;
4119        gchar *secondary;
4120        GString *text;
4121        int i, num;
4122        va_list ap;
4123        char *email, *SNs;
4124
4125        va_start(ap, fr);
4126        email = va_arg(ap, char *);
4127        num = va_arg(ap, int);
4128        SNs = va_arg(ap, char *);
4129        va_end(ap);
4130
4131        secondary = g_strdup_printf(_("The following screennames are associated with %s"), email);
4132        text = g_string_new("");
4133        for (i = 0; i < num; i++)
4134                g_string_append_printf(text, "%s<br>", &SNs[i * (MAXSNLEN + 1)]);
4135        gaim_notify_formatted(gc, NULL, _("Search Results"), secondary, text->str, NULL, NULL);
4136
4137        g_free(secondary);
4138        g_string_free(text, TRUE);
4139
4140        return 1;
4141}
4142
4143static int gaim_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...) {
4144        va_list ap;
4145        char *email;
4146        char *buf;
4147
4148        va_start(ap, fr);
4149        email = va_arg(ap, char *);
4150        va_end(ap);
4151
4152        buf = g_strdup_printf(_("No results found for email address %s"), email);
4153        gaim_notify_error(sess->aux_data, NULL, buf, NULL);
4154        g_free(buf);
4155
4156        return 1;
4157}
4158
4159static int gaim_account_confirm(aim_session_t *sess, aim_frame_t *fr, ...) {
4160        GaimConnection *gc = sess->aux_data;
4161        fu16_t status;
4162        va_list ap;
4163        char msg[256];
4164
4165        va_start(ap, fr);
4166        status = (fu16_t) va_arg(ap, unsigned int); /* status code of confirmation request */
4167        va_end(ap);
4168
4169        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4170                           "account confirmation returned status 0x%04x (%s)\n", status,
4171                        status ? "unknown" : "email sent");
4172        if (!status) {
4173                g_snprintf(msg, sizeof(msg), _("You should receive an email asking to confirm %s."),
4174                                gaim_account_get_username(gaim_connection_get_account(gc)));
4175                gaim_notify_info(gc, NULL, _("Account Confirmation Requested"), msg);
4176        }
4177
4178        return 1;
4179}
4180
4181static int gaim_info_change(aim_session_t *sess, aim_frame_t *fr, ...) {
4182        GaimConnection *gc = sess->aux_data;
4183        va_list ap;
4184        fu16_t perms, err;
4185        char *url, *sn, *email;
4186        int change;
4187
4188        va_start(ap, fr);
4189        change = va_arg(ap, int);
4190        perms = (fu16_t) va_arg(ap, unsigned int);
4191        err = (fu16_t) va_arg(ap, unsigned int);
4192        url = va_arg(ap, char *);
4193        sn = va_arg(ap, char *);
4194        email = va_arg(ap, char *);
4195        va_end(ap);
4196
4197        gaim_debug(GAIM_DEBUG_MISC, "oscar",
4198                           "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, sn=%s, email=%s\n",
4199                change ? "change" : "request", perms, err, url, sn, email);
4200
4201        if (err && url) {
4202                char *dialog_msg;
4203                char *dialog_top = g_strdup_printf(_("Error Changing Account Info"));
4204                switch (err) {
4205                        case 0x0001: {
4206                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name differs from the original."), err);
4207                        } break;
4208                        case 0x0006: {
4209                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name ends in a space."), err);
4210                        } break;
4211                        case 0x000b: {
4212                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name is too long."), err);
4213                        } break;
4214                        case 0x001d: {
4215                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because there is already a request pending for this screen name."), err);
4216                        } break;
4217                        case 0x0021: {
4218                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address has too many screen names associated with it."), err);
4219                        } break;
4220                        case 0x0023: {
4221                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err);
4222                        } break;
4223                        default: {
4224                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err);
4225                        } break;
4226                }
4227                gaim_notify_error(gc, NULL, dialog_top, dialog_msg);
4228                g_free(dialog_top);
4229                g_free(dialog_msg);
4230                return 1;
4231        }
4232
4233        if (sn) {
4234                char *dialog_msg = g_strdup_printf(_("Your screen name is currently formatted as follows:\n%s"), sn);
4235                gaim_notify_info(gc, NULL, _("Account Info"), dialog_msg);
4236                g_free(dialog_msg);
4237        }
4238
4239        if (email) {
4240                char *dialog_msg = g_strdup_printf(_("The email address for %s is %s"), 
4241                                                   gaim_account_get_username(gaim_connection_get_account(gc)), email);
4242                gaim_notify_info(gc, NULL, _("Account Info"), dialog_msg);
4243                g_free(dialog_msg);
4244        }
4245
4246        return 1;
4247}
4248
4249static void oscar_keepalive(GaimConnection *gc) {
4250        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4251        aim_flap_nop(od->sess, od->conn);
4252}
4253
4254static int oscar_send_typing(GaimConnection *gc, const char *name, int typing) {
4255        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4256        struct direct_im *dim = find_direct_im(od, name);
4257        if (dim)
4258                if (typing == GAIM_TYPING)
4259                        aim_odc_send_typing(od->sess, dim->conn, 0x0002);
4260                else if (typing == GAIM_TYPED)
4261                        aim_odc_send_typing(od->sess, dim->conn, 0x0001);
4262                else
4263                        aim_odc_send_typing(od->sess, dim->conn, 0x0000);
4264        else {
4265                /* Don't send if this turkey is in our deny list */
4266                GSList *list;
4267                for (list=gc->account->deny; (list && aim_sncmp(name, list->data)); list=list->next);
4268                if (!list) {
4269                        struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, normalize(name));
4270                        if (bi && bi->typingnot) {
4271                                if (typing == GAIM_TYPING)
4272                                        aim_im_sendmtn(od->sess, 0x0001, name, 0x0002);
4273                                else if (typing == GAIM_TYPED)
4274                                        aim_im_sendmtn(od->sess, 0x0001, name, 0x0001);
4275                                else
4276                                        aim_im_sendmtn(od->sess, 0x0001, name, 0x0000);
4277                        }
4278                }
4279        }
4280        return 0;
4281}
4282static void oscar_ask_direct_im(GaimConnection *gc, const char *name);
4283static int gaim_odc_send_im(aim_session_t *, aim_conn_t *, const char *, GaimImFlags);
4284
4285static int oscar_send_im(GaimConnection *gc, const char *name, const char *message, GaimImFlags imflags) {
4286        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4287        struct direct_im *dim = find_direct_im(od, name);
4288        int ret = 0;
4289        GError *err = NULL;
4290        const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc));
4291        char *tmpmsg = NULL;
4292
4293        if (dim && dim->connected) {
4294                /* If we're directly connected, send a direct IM */
4295                ret = gaim_odc_send_im(od->sess, dim->conn, message, imflags);
4296        } else if (imflags & GAIM_IM_IMAGES) {
4297                /* Trying to send an IM image outside of a direct connection. */
4298                oscar_ask_direct_im(gc, name);
4299                ret = -ENOTCONN;
4300        } else {
4301                struct buddyinfo *bi;
4302                struct aim_sendimext_args args;
4303                struct stat st;
4304                gsize len;
4305
4306                bi = g_hash_table_lookup(od->buddyinfo, normalize(name));
4307                if (!bi) {
4308                        bi = g_new0(struct buddyinfo, 1);
4309                        g_hash_table_insert(od->buddyinfo, g_strdup(normalize(name)), bi);
4310                }
4311
4312                args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES;
4313                if (od->icq) {
4314                        args.features = features_icq;
4315                        args.featureslen = sizeof(features_icq);
4316                        args.flags |= AIM_IMFLAGS_OFFLINE;
4317                } else {
4318                        args.features = features_aim;
4319                        args.featureslen = sizeof(features_aim);
4320
4321                        if (imflags & GAIM_IM_AUTO_RESP)
4322                                args.flags |= AIM_IMFLAGS_AWAY;
4323                }
4324
4325                if (bi->ico_need) {
4326                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4327                                           "Sending buddy icon request with message\n");
4328                        args.flags |= AIM_IMFLAGS_BUDDYREQ;
4329                        bi->ico_need = FALSE;
4330                }
4331
4332                if (iconfile && !stat(iconfile, &st)) {
4333                        FILE *file = fopen(iconfile, "r");
4334                        if (file) {
4335                                char *buf = g_malloc(st.st_size);
4336                                fread(buf, 1, st.st_size, file);
4337                                fclose(file);
4338
4339                                args.iconlen   = st.st_size;
4340                                args.iconsum   = aimutil_iconsum(buf, st.st_size);
4341                                args.iconstamp = st.st_mtime;
4342
4343                                if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time))
4344                                        bi->ico_informed = FALSE;
4345
4346                                if (!bi->ico_informed) {
4347                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4348                                                           "Claiming to have a buddy icon\n");
4349                                        args.flags |= AIM_IMFLAGS_HASICON;
4350                                        bi->ico_me_len = args.iconlen;
4351                                        bi->ico_me_csum = args.iconsum;
4352                                        bi->ico_me_time = args.iconstamp;
4353                                        bi->ico_informed = TRUE;
4354                                }
4355
4356                                g_free(buf);
4357                        }
4358                }
4359
4360                args.destsn = name;
4361
4362                /* For ICQ send newlines as CR/LF, for AIM send newlines as <BR> */
4363                if (isdigit(name[0]))
4364                        tmpmsg = add_cr(message);
4365                else
4366                        tmpmsg = strdup_withhtml(message);
4367                len = strlen(tmpmsg);
4368
4369                args.flags |= oscar_encoding_check(tmpmsg);
4370                if (args.flags & AIM_IMFLAGS_UNICODE) {
4371                        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Sending Unicode IM\n");
4372                        args.charset = 0x0002;
4373                        args.charsubset = 0x0000;
4374                        args.msg = g_convert(tmpmsg, len, "UCS-2BE", "UTF-8", NULL, &len, &err);
4375                        if (err) {
4376                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4377                                                   "Error converting a unicode message: %s\n", err->message);
4378                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4379                                                   "This really shouldn't happen!\n");
4380                                /* We really shouldn't try to send the
4381                                 * IM now, but I'm not sure what to do */
4382                                g_error_free(err);
4383                        }
4384                } else if (args.flags & AIM_IMFLAGS_ISO_8859_1) {
4385                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4386                                           "Sending ISO-8859-1 IM\n");
4387                        args.charset = 0x0003;
4388                        args.charsubset = 0x0000;
4389                        args.msg = g_convert(tmpmsg, len, "ISO-8859-1", "UTF-8", NULL, &len, &err);
4390                        if (err) {
4391                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4392                                                   "conversion error: %s\n", err->message);
4393                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4394                                                   "Someone tell Ethan his 8859-1 detection is wrong\n");
4395                                args.flags ^= AIM_IMFLAGS_ISO_8859_1 | AIM_IMFLAGS_UNICODE;
4396                                len = strlen(tmpmsg);
4397                                g_error_free(err);
4398                                args.msg = g_convert(tmpmsg, len, "UCS-2BE", "UTF8", NULL, &len, &err);
4399                                if (err) {
4400                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4401                                                           "Error in unicode fallback: %s\n", err->message);
4402                                        g_error_free(err);
4403                                }
4404                        }
4405                } else {
4406                        args.charset = 0x0000;
4407                        args.charsubset = 0x0000;
4408                        args.msg = tmpmsg;
4409                }
4410                args.msglen = len;
4411
4412                ret = aim_im_sendch1_ext(od->sess, &args);
4413        }
4414
4415        g_free(tmpmsg);
4416
4417        if (ret >= 0)
4418                return 1;
4419
4420        return ret;
4421}
4422
4423static void oscar_get_info(GaimConnection *g, const char *name) {
4424        struct oscar_data *od = (struct oscar_data *)g->proto_data;
4425        if (od->icq)
4426                aim_icq_getallinfo(od->sess, name);
4427        else
4428                /* people want the away message on the top, so we get the away message
4429                 * first and then get the regular info, since it's too difficult to
4430                 * insert in the middle. i hate people. */
4431                aim_getinfo(od->sess, od->conn, name, AIM_GETINFO_AWAYMESSAGE);
4432}
4433
4434static void oscar_get_away(GaimConnection *g, const char *who) {
4435        struct oscar_data *od = (struct oscar_data *)g->proto_data;
4436        if (od->icq) {
4437                GaimBuddy *budlight = gaim_find_buddy(g->account, who);
4438                if (budlight)
4439                        if ((budlight->uc & 0xffff0000) >> 16)
4440                                aim_im_sendch2_geticqaway(od->sess, who, (budlight->uc & 0xffff0000) >> 16);
4441                        else
4442                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4443                                                   "Error: The user %s has no status message, therefore not requesting.\n", who);
4444                else
4445                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4446                                           "Error: Could not find %s in local contact list, therefore unable to request status message.\n", who);
4447        } else
4448                aim_getinfo(od->sess, od->conn, who, AIM_GETINFO_GENERALINFO);
4449}
4450
4451static void oscar_set_dir(GaimConnection *g, const char *first, const char *middle, const char *last,
4452                          const char *maiden, const char *city, const char *state, const char *country, int web) {
4453        /* XXX - some of these things are wrong, but i'm lazy */
4454        struct oscar_data *od = (struct oscar_data *)g->proto_data;
4455        aim_setdirectoryinfo(od->sess, od->conn, first, middle, last,
4456                                maiden, NULL, NULL, city, state, NULL, 0, web);
4457}
4458
4459static void oscar_set_idle(GaimConnection *gc, int time) {
4460        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4461        aim_bos_setidle(od->sess, od->conn, time);
4462}
4463
4464static void oscar_set_info(GaimConnection *gc, const char *text) {
4465        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4466        fu32_t flags = 0;
4467        char *text_html = NULL;
4468        char *msg = NULL;
4469        gsize msglen = 0;
4470
4471        if (od->rights.maxsiglen == 0)
4472                gaim_notify_warning(gc, NULL, _("Unable to set AIM profile."),
4473                                                        _("You have probably requested to set your "
4474                                                          "profile before the login procedure completed.  "
4475                                                          "Your profile remains unset; try setting it "
4476                                                          "again when you are fully connected."));
4477
4478        if (od->icq)
4479                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, NULL, NULL, 0, caps_icq);
4480        else {
4481                if (!text) {
4482                        aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, NULL, NULL, 0, caps_aim);
4483                        return;
4484                }
4485               
4486                text_html = strdup_withhtml(text);
4487                flags = oscar_encoding_check(text_html);
4488                if (flags & AIM_IMFLAGS_UNICODE) {
4489                        msg = g_convert(text_html, strlen(text_html), "UCS-2BE", "UTF-8", NULL, &msglen, NULL);
4490                        aim_bos_setprofile(od->sess, od->conn, "unicode-2-0", msg, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0, caps_aim);
4491                        g_free(msg);
4492                } else if (flags & AIM_IMFLAGS_ISO_8859_1) {
4493                        msg = g_convert(text_html, strlen(text_html), "ISO-8859-1", "UTF-8", NULL, &msglen, NULL);
4494                        aim_bos_setprofile(od->sess, od->conn, "iso-8859-1", msg, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0, caps_aim);
4495                        g_free(msg);
4496                } else {
4497                        msglen = strlen(text_html);
4498                        aim_bos_setprofile(od->sess, od->conn, "us-ascii", text_html, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0, caps_aim);
4499                }
4500
4501                if (msglen > od->rights.maxsiglen) {
4502                        gchar *errstr;
4503                        errstr = g_strdup_printf(ngettext("The maximum profile length of %d byte "
4504                                                                         "has been exceeded.  Gaim has truncated it for you.",
4505                                                                         "The maximum profile length of %d bytes "
4506                                                                         "has been exceeded.  Gaim has truncated it for you.",
4507                                                                         od->rights.maxsiglen), od->rights.maxsiglen);
4508                        gaim_notify_warning(gc, NULL, _("Profile too long."), errstr);
4509                        g_free(errstr);
4510                }
4511
4512                g_free(text_html);
4513
4514        }
4515
4516        return;
4517}
4518
4519static void oscar_set_away_aim(GaimConnection *gc, struct oscar_data *od, const char *text)
4520{
4521        fu32_t flags = 0;
4522        gchar *text_html = NULL;
4523        char *msg = NULL;
4524        gsize msglen = 0;
4525
4526        if (od->rights.maxawaymsglen == 0)
4527                gaim_notify_warning(gc, NULL, _("Unable to set AIM away message."),
4528                                                        _("You have probably requested to set your "
4529                                                          "away message before the login procedure "
4530                                                          "completed.  You remain in a \"present\" "
4531                                                          "state; try setting it again when you are "
4532                                                          "fully connected."));
4533
4534        if (gc->away) {
4535                g_free(gc->away);
4536                gc->away = NULL;
4537        }
4538
4539        if (!text) {
4540                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, NULL, "", 0, caps_aim);
4541                return;
4542        }
4543
4544        text_html = strdup_withhtml(text);
4545        flags = oscar_encoding_check(text_html);
4546        if (flags & AIM_IMFLAGS_UNICODE) {
4547                msg = g_convert(text_html, strlen(text_html), "UCS-2BE", "UTF-8", NULL, &msglen, NULL);
4548                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, "unicode-2-0", msg, 
4549                        (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen), caps_aim);
4550                g_free(msg);
4551                gc->away = g_strndup(text, od->rights.maxawaymsglen/2);
4552        } else if (flags & AIM_IMFLAGS_ISO_8859_1) {
4553                msg = g_convert(text_html, strlen(text_html), "ISO-8859-1", "UTF-8", NULL, &msglen, NULL);
4554                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, "iso-8859-1", msg, 
4555                        (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen), caps_aim);
4556                g_free(msg);
4557                gc->away = g_strndup(text_html, od->rights.maxawaymsglen);
4558        } else {
4559                msglen = strlen(text_html);
4560                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, "us-ascii", text_html, 
4561                        (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen), caps_aim);
4562                gc->away = g_strndup(text_html, od->rights.maxawaymsglen);
4563        }
4564
4565        if (msglen > od->rights.maxawaymsglen) {
4566                gchar *errstr;
4567
4568                errstr = g_strdup_printf(ngettext("The maximum away message length of %d byte "
4569                                                                 "has been exceeded.  Gaim has truncated it for you.",
4570                                                                 "The maximum away message length of %d bytes "
4571                                                                 "has been exceeded.  Gaim has truncated it for you.",
4572                                                                 od->rights.maxawaymsglen), od->rights.maxawaymsglen);
4573                gaim_notify_warning(gc, NULL, _("Away message too long."), errstr);
4574                g_free(errstr);
4575        }
4576       
4577        g_free(text_html);
4578        return;
4579}
4580
4581static void oscar_set_away_icq(GaimConnection *gc, struct oscar_data *od, const char *state, const char *message)
4582{
4583        GaimAccount *account = gaim_connection_get_account(gc);
4584        if (gc->away) {
4585                g_free(gc->away);
4586                gc->away = NULL;
4587        }
4588
4589        if (strcmp(state, _("Invisible"))) {
4590                if ((od->sess->ssi.received_data) && (aim_ssi_getpermdeny(od->sess->ssi.local) != account->perm_deny))
4591                        aim_ssi_setpermdeny(od->sess, account->perm_deny, 0xffffffff);
4592                account->perm_deny = 4;
4593        } else {
4594                if ((od->sess->ssi.received_data) && (aim_ssi_getpermdeny(od->sess->ssi.local) != 0x03))
4595                        aim_ssi_setpermdeny(od->sess, 0x03, 0xffffffff);
4596                account->perm_deny = 3;
4597        }
4598
4599        if (!strcmp(state, _("Online")))
4600                aim_setextstatus(od->sess, AIM_ICQ_STATE_NORMAL);
4601        else if (!strcmp(state, _("Away"))) {
4602                aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY);
4603                gc->away = g_strdup("");
4604        } else if (!strcmp(state, _("Do Not Disturb"))) {
4605                aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
4606                gc->away = g_strdup("");
4607        } else if (!strcmp(state, _("Not Available"))) {
4608                aim_setextstatus(od->sess, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
4609                gc->away = g_strdup("");
4610        } else if (!strcmp(state, _("Occupied"))) {
4611                aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
4612                gc->away = g_strdup("");
4613        } else if (!strcmp(state, _("Free For Chat"))) {
4614                aim_setextstatus(od->sess, AIM_ICQ_STATE_CHAT);
4615                gc->away = g_strdup("");
4616        } else if (!strcmp(state, _("Invisible"))) {
4617                aim_setextstatus(od->sess, AIM_ICQ_STATE_INVISIBLE);
4618                gc->away = g_strdup("");
4619        } else if (!strcmp(state, GAIM_AWAY_CUSTOM)) {
4620                if (message) {
4621                        aim_setextstatus(od->sess, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
4622                        gc->away = g_strdup("");
4623                } else {
4624                        aim_setextstatus(od->sess, AIM_ICQ_STATE_NORMAL);
4625                }
4626        }
4627
4628        return;
4629}
4630
4631static void oscar_set_away(GaimConnection *gc, const char *state, const char *message)
4632{
4633        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4634
4635        if (od->icq)
4636                oscar_set_away_icq(gc, od, state, message);
4637        else
4638                oscar_set_away_aim(gc, od, message);
4639
4640        return;
4641}
4642
4643static void oscar_warn(GaimConnection *gc, const char *name, int anon) {
4644        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4645        aim_im_warn(od->sess, od->conn, name, anon ? AIM_WARN_ANON : 0);
4646}
4647
4648static void oscar_dir_search(GaimConnection *gc, const char *first, const char *middle, const char *last,
4649                             const char *maiden, const char *city, const char *state, const char *country, const char *email) {
4650        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4651        if (strlen(email))
4652                aim_search_address(od->sess, od->conn, email);
4653}
4654
4655static void oscar_add_buddy(GaimConnection *gc, const char *name, GaimGroup *g) {
4656        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4657#ifdef NOSSI
4658        aim_add_buddy(od->sess, od->conn, name);
4659#else
4660        if ((od->sess->ssi.received_data) && !(aim_ssi_itemlist_exists(od->sess->ssi.local, name))) {
4661                GaimBuddy *buddy = gaim_find_buddy(gc->account, name);
4662                GaimGroup *group = gaim_find_buddys_group(buddy);
4663                if (buddy && group) {
4664                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4665                                           "ssi: adding buddy %s to group %s\n", name, group->name);
4666                        aim_ssi_addbuddy(od->sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0);
4667                }
4668        }
4669#endif
4670        if (od->icq)
4671                aim_icq_getalias(od->sess, name);
4672}
4673
4674static void oscar_add_buddies(GaimConnection *gc, GList *buddies) {
4675        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4676#ifdef NOSSI
4677        char buf[MSG_LEN];
4678        int n=0;
4679        while (buddies) {
4680                if (n > MSG_LEN - 18) {
4681                        aim_bos_setbuddylist(od->sess, od->conn, buf);
4682                        n = 0;
4683                }
4684                n += g_snprintf(buf + n, sizeof(buf) - n, "%s&", (char *)buddies->data);
4685                buddies = buddies->next;
4686        }
4687        aim_bos_setbuddylist(od->sess, od->conn, buf);
4688#else
4689        if (od->sess->ssi.received_data) {
4690                while (buddies) {
4691                        GaimBuddy *buddy = gaim_find_buddy(gc->account, (const char *)buddies->data);
4692                        GaimGroup *group = gaim_find_buddys_group(buddy);
4693                        if (buddy && group) {
4694                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4695                                                   "ssi: adding buddy %s to group %s\n", (const char *)buddies->data, group->name);
4696                                aim_ssi_addbuddy(od->sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0);
4697                        }
4698                        buddies = buddies->next;
4699                }
4700        }
4701#endif
4702}
4703
4704static void oscar_remove_buddy(GaimConnection *gc, const char *name, const char *group) {
4705        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4706#ifdef NOSSI
4707        aim_remove_buddy(od->sess, od->conn, name);
4708#else
4709        if (od->sess->ssi.received_data) {
4710                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4711                                   "ssi: deleting buddy %s from group %s\n", name, group);
4712                aim_ssi_delbuddy(od->sess, name, group);
4713        }
4714#endif
4715}
4716
4717static void oscar_remove_buddies(GaimConnection *gc, GList *buddies, const char *group) {
4718        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4719#ifdef NOSSI
4720        GList *cur;
4721        for (cur=buddies; cur; cur=cur->next)
4722                aim_remove_buddy(od->sess, od->conn, cur->data);
4723#else
4724        if (od->sess->ssi.received_data) {
4725                while (buddies) {
4726                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4727                                           "ssi: deleting buddy %s from group %s\n", (char *)buddies->data, group);
4728                        aim_ssi_delbuddy(od->sess, buddies->data, group);
4729                        buddies = buddies->next;
4730                }
4731        }
4732#endif
4733}
4734
4735#ifndef NOSSI
4736static void oscar_move_buddy(GaimConnection *gc, const char *name, const char *old_group, const char *new_group) {
4737        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4738        if (od->sess->ssi.received_data && strcmp(old_group, new_group)) {
4739                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4740                                   "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group);
4741                aim_ssi_movebuddy(od->sess, old_group, new_group, name);
4742        }
4743}
4744
4745static void oscar_alias_buddy(GaimConnection *gc, const char *name, const char *alias) {
4746        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4747        if (od->sess->ssi.received_data) {
4748                char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, name);
4749                if (gname) {
4750                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4751                                           "ssi: changing the alias for buddy %s to %s\n", name, alias);
4752                        aim_ssi_aliasbuddy(od->sess, gname, name, alias);
4753                }
4754        }
4755}
4756
4757static void oscar_rename_group(GaimConnection *g, const char *old_group, const char *new_group, GList *members) {
4758        struct oscar_data *od = (struct oscar_data *)g->proto_data;
4759
4760        if (od->sess->ssi.received_data) {
4761                if (aim_ssi_itemlist_finditem(od->sess->ssi.local, new_group, NULL, AIM_SSI_TYPE_GROUP)) {
4762                        oscar_remove_buddies(g, members, old_group);
4763                        oscar_add_buddies(g, members);
4764                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4765                                           "ssi: moved all buddies from group %s to %s\n", old_group, new_group);
4766                } else {
4767                        aim_ssi_rename_group(od->sess, old_group, new_group);
4768                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4769                                           "ssi: renamed group %s to %s\n", old_group, new_group);
4770                }
4771        }
4772}
4773
4774static gboolean gaim_ssi_rerequestdata(gpointer data) {
4775        aim_session_t *sess = data;
4776        aim_ssi_reqdata(sess);
4777        return FALSE;
4778}
4779
4780static int gaim_ssi_parseerr(aim_session_t *sess, aim_frame_t *fr, ...) {
4781        GaimConnection *gc = sess->aux_data;
4782        struct oscar_data *od = gc->proto_data;
4783        va_list ap;
4784        fu16_t reason;
4785
4786        va_start(ap, fr);
4787        reason = (fu16_t)va_arg(ap, unsigned int);
4788        va_end(ap);
4789
4790        gaim_debug(GAIM_DEBUG_ERROR, "oscar", "ssi: SNAC error %hu\n", reason);
4791
4792        if (reason == 0x0005) {
4793                gaim_notify_error(gc, NULL, _("Unable To Retrieve Buddy List"),
4794                                                  _("Gaim was temporarily unable to retrieve your buddy list from the AIM servers.  Your buddy list is not lost, and will probably become available in a few hours."));
4795                od->getblisttimer = g_timeout_add(300000, gaim_ssi_rerequestdata, od->sess);
4796        }
4797
4798        /* Activate SSI */
4799        /* Sending the enable causes other people to be able to see you, and you to see them */
4800        /* Make sure your privacy setting/invisibility is set how you want it before this! */
4801        gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: activating server-stored buddy list\n");
4802        aim_ssi_enable(od->sess);
4803
4804        return 1;
4805}
4806
4807static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
4808        GaimConnection *gc = sess->aux_data;
4809        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4810        int numtypes, i;
4811        fu16_t *maxitems;
4812        va_list ap;
4813
4814        va_start(ap, fr);
4815        numtypes = va_arg(ap, int);
4816        maxitems = va_arg(ap, fu16_t *);
4817        va_end(ap);
4818
4819        gaim_debug(GAIM_DEBUG_MISC, "oscar", "ssi rights:");
4820
4821        for (i=0; i<numtypes; i++)
4822                gaim_debug(GAIM_DEBUG_MISC, NULL, " max type 0x%04x=%hd,",
4823                                   i, maxitems[i]);
4824
4825        gaim_debug(GAIM_DEBUG_MISC, NULL, "\n");
4826
4827        if (numtypes >= 0)
4828                od->rights.maxbuddies = maxitems[0];
4829        if (numtypes >= 1)
4830                od->rights.maxgroups = maxitems[1];
4831        if (numtypes >= 2)
4832                od->rights.maxpermits = maxitems[2];
4833        if (numtypes >= 3)
4834                od->rights.maxdenies = maxitems[3];
4835
4836        return 1;
4837}
4838
4839static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
4840        GaimConnection *gc = sess->aux_data;
4841        GaimAccount *account = gaim_connection_get_account(gc);
4842        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4843        struct aim_ssi_item *curitem;
4844        int tmp;
4845        gboolean export = FALSE;
4846        /* XXX - use these?
4847        va_list ap;
4848
4849        va_start(ap, fr);
4850        fmtver = (fu16_t)va_arg(ap, int);
4851        numitems = (fu16_t)va_arg(ap, int);
4852        items = va_arg(ap, struct aim_ssi_item);
4853        timestamp = va_arg(ap, fu32_t);
4854        va_end(ap); */
4855
4856        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4857                           "ssi: syncing local list and server list\n");
4858
4859        /* Clean the buddy list */
4860        aim_ssi_cleanlist(sess);
4861
4862        /* Add from server list to local list */
4863        for (curitem=sess->ssi.local; curitem; curitem=curitem->next) {
4864                switch (curitem->type) {
4865                        case 0x0000: { /* Buddy */
4866                                if (curitem->name) {
4867                                        char *gname = aim_ssi_itemlist_findparentname(sess->ssi.local, curitem->name);
4868                                        char *gname_utf8 = gaim_try_conv_to_utf8(gname);
4869                                        char *alias = aim_ssi_getalias(sess->ssi.local, gname, curitem->name);
4870                                        char *alias_utf8 = gaim_try_conv_to_utf8(alias);
4871                                        GaimBuddy *buddy = gaim_find_buddy(gc->account, curitem->name);
4872                                        /* Should gname be freed here? -- elb */
4873                                        /* Not with the current code, but that might be cleaner -- med */
4874                                        free(alias);
4875                                        if (buddy) {
4876                                                /* Get server stored alias */
4877                                                if (alias_utf8) {
4878                                                        g_free(buddy->alias);
4879                                                        buddy->alias = g_strdup(alias_utf8);
4880                                                }
4881                                        } else {
4882                                                GaimGroup *g;
4883                                                buddy = gaim_buddy_new(gc->account, curitem->name, alias_utf8);
4884
4885                                                if (!(g = gaim_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) {
4886                                                        g = gaim_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
4887                                                        gaim_blist_add_group(g, NULL);
4888                                                }
4889
4890                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4891                                                                   "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname_utf8 ? gname_utf8 : _("Orphans"));
4892                                                gaim_blist_add_buddy(buddy, NULL, g, NULL);
4893                                                export = TRUE;
4894                                        }
4895                                        free(gname_utf8);
4896                                        free(alias_utf8);
4897                                }
4898                        } break;
4899
4900                        case 0x0001: { /* Group */
4901                                /* Shouldn't add empty groups */
4902                        } break;
4903
4904                        case 0x0002: { /* Permit buddy */
4905                                if (curitem->name) {
4906                                        /* if (!find_permdeny_by_name(gc->permit, curitem->name)) { AAA */
4907                                        GSList *list;
4908                                        for (list=account->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
4909                                        if (!list) {
4910                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4911                                                                   "ssi: adding permit buddy %s to local list\n", curitem->name);
4912                                                gaim_privacy_permit_add(account, curitem->name, TRUE);
4913                                                export = TRUE;
4914                                        }
4915                                }
4916                        } break;
4917
4918                        case 0x0003: { /* Deny buddy */
4919                                if (curitem->name) {
4920                                        GSList *list;
4921                                        for (list=account->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
4922                                        if (!list) {
4923                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4924                                                                   "ssi: adding deny buddy %s to local list\n", curitem->name);
4925                                                gaim_privacy_deny_add(account, curitem->name, TRUE);
4926                                                export = TRUE;
4927                                        }
4928                                }
4929                        } break;
4930
4931                        case 0x0004: { /* Permit/deny setting */
4932                                if (curitem->data) {
4933                                        fu8_t permdeny;
4934                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.local)) && (permdeny != account->perm_deny)) {
4935                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4936                                                                   "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny);
4937                                                account->perm_deny = permdeny;
4938                                                if (od->icq && account->perm_deny == 0x03) {
4939                                                        serv_set_away(gc, "Invisible", "");
4940                                                }
4941                                                export = TRUE;
4942                                        }
4943                                }
4944                        } break;
4945
4946                        case 0x0005: { /* Presence setting */
4947                                /* We don't want to change Gaim's setting because it applies to all accounts */
4948                        } break;
4949                } /* End of switch on curitem->type */
4950        } /* End of for loop */
4951
4952        /* If changes were made, then flush buddy list to file */
4953        if (export)
4954                gaim_blist_save();
4955
4956        { /* Add from local list to server list */
4957                GaimBlistNode *gnode, *cnode, *bnode;
4958                GaimGroup *group;
4959                GaimBuddy *buddy;
4960                struct gaim_buddy_list *blist;
4961                GSList *cur;
4962
4963                /* Buddies */
4964                if ((blist = gaim_get_blist()))
4965                        for (gnode = blist->root; gnode; gnode = gnode->next) {
4966                                if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
4967                                        continue;
4968                                group = (GaimGroup *)gnode;
4969                                for (cnode = gnode->child; cnode; cnode = cnode->next) {
4970                                        if(!GAIM_BLIST_NODE_IS_CONTACT(cnode))
4971                                                continue;
4972                                        for (bnode = cnode->child; bnode; bnode = bnode->next) {
4973                                                if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
4974                                                        continue;
4975                                                buddy = (GaimBuddy *)bnode;
4976                                                if (buddy->account == gc->account) {
4977                                                        gchar *servernick = gaim_buddy_get_setting(buddy, "servernick");
4978                                                        if (servernick) {
4979                                                                serv_got_alias(gc, buddy->name, servernick);
4980                                                                g_free(servernick);
4981                                                        }
4982                                                        if (aim_ssi_itemlist_exists(sess->ssi.local, buddy->name)) {
4983                                                                /* Store local alias on server */
4984                                                                char *alias = aim_ssi_getalias(sess->ssi.local, group->name, buddy->name);
4985                                                                if (!alias && buddy->alias && strlen(buddy->alias))
4986                                                                        aim_ssi_aliasbuddy(sess, group->name, buddy->name, buddy->alias);
4987                                                                free(alias);
4988                                                        } else {
4989                                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4990                                                                                "ssi: adding buddy %s from local list to server list\n", buddy->name);
4991                                                                aim_ssi_addbuddy(sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0);
4992                                                        }
4993                                                }
4994                                        }
4995                                }
4996                        }
4997
4998                /* Permit list */
4999                if (gc->account->permit) {
5000                        for (cur=gc->account->permit; cur; cur=cur->next)
5001                                if (!aim_ssi_itemlist_finditem(sess->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) {
5002                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5003                                                        "ssi: adding permit %s from local list to server list\n", (char *)cur->data);
5004                                        aim_ssi_addpermit(sess, cur->data);
5005                                }
5006                }
5007
5008                /* Deny list */
5009                if (gc->account->deny) {
5010                        for (cur=gc->account->deny; cur; cur=cur->next)
5011                                if (!aim_ssi_itemlist_finditem(sess->ssi.local, NULL, cur->data, AIM_SSI_TYPE_DENY)) {
5012                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5013                                                        "ssi: adding deny %s from local list to server list\n", (char *)cur->data);
5014                                        aim_ssi_adddeny(sess, cur->data);
5015                                }
5016                }
5017                /* Presence settings (idle time visibility) */
5018                if ((tmp = aim_ssi_getpresence(sess->ssi.local)) != 0xFFFFFFFF)
5019                        if (!(tmp & 0x400))
5020                                aim_ssi_setpresence(sess, tmp | 0x400);
5021        } /* end adding buddies from local list to server list */
5022
5023        /* Set our ICQ status */
5024        if  (od->icq && !gc->away) {
5025                aim_setextstatus(sess, AIM_ICQ_STATE_NORMAL);
5026        }
5027
5028        /* Activate SSI */
5029        /* Sending the enable causes other people to be able to see you, and you to see them */
5030        /* Make sure your privacy setting/invisibility is set how you want it before this! */
5031        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5032                           "ssi: activating server-stored buddy list\n");
5033        aim_ssi_enable(sess);
5034
5035        return 1;
5036}
5037
5038static int gaim_ssi_parseack(aim_session_t *sess, aim_frame_t *fr, ...) {
5039        GaimConnection *gc = sess->aux_data;
5040        va_list ap;
5041        struct aim_ssi_tmp *retval;
5042
5043        va_start(ap, fr);
5044        retval = va_arg(ap, struct aim_ssi_tmp *);
5045        va_end(ap);
5046
5047        while (retval) {
5048                gaim_debug(GAIM_DEBUG_MISC, "oscar",
5049                                   "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack,  retval->action, retval->item ? (retval->item->name ? retval->item-&g