source: libfaim/oscar.c @ 601733d

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