source: libfaim/oscar.c @ 460413f

barnowl_perlaimdebianowlrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 460413f was cf02dd6, checked in by James M. Kretchmar <kretch@mit.edu>, 21 years ago
*** empty log message ***
  • Property mode set to 100644
File size: 203.0 KB
RevLine 
[07ab1cb]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"
[cf02dd6]33#include "core.h"
[07ab1cb]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
[cf02dd6]58#define OSCAR_CONNECT_STEPS 6
59
[07ab1cb]60static GaimPlugin *my_protocol = NULL;
61
[cf02dd6]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;
[07ab1cb]64
65static fu8_t features_aim[] = {0x01, 0x01, 0x01, 0x02};
66static fu8_t features_icq[] = {0x01, 0x06};
67
[cf02dd6]68typedef struct _OscarData OscarData;
69struct _OscarData {
[07ab1cb]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
[cf02dd6]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 */
[07ab1cb]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;
[cf02dd6]169        gboolean ico_sent;
[07ab1cb]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 *, ...);
[cf02dd6]218static int gaim_parse_userinfo   (aim_session_t *, aim_frame_t *, ...);
[07ab1cb]219static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
220static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
[cf02dd6]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 *, ...);
[07ab1cb]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 */
[cf02dd6]283/* Because I don't like forward declarations?  I think that was why... */
[07ab1cb]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
[cf02dd6]383static struct direct_im *find_direct_im(OscarData *od, const char *who) {
[07ab1cb]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) {
[cf02dd6]427        GSList *g = ((OscarData *)gc->proto_data)->oscar_chats;
[07ab1cb]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) {
[cf02dd6]443        GSList *g = ((OscarData *)gc->proto_data)->oscar_chats;
[07ab1cb]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;
[cf02dd6]459        OscarData *od = (OscarData *)gc->proto_data;
[07ab1cb]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;
[cf02dd6]495        OscarData *od;
[07ab1cb]496
497        if (!gc) {
498                gaim_debug(GAIM_DEBUG_INFO, "oscar",
499                                   "oscar callback for closed connection (1).\n");
500                return;
501        }
502     
[cf02dd6]503        od = (OscarData *)gc->proto_data;
[07ab1cb]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;
[cf02dd6]605        gchar *s = g_strdup_vprintf(format, va);
606        gchar *buf;
[07ab1cb]607
[cf02dd6]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')
[07ab1cb]611                gaim_debug(GAIM_DEBUG_INFO, NULL, "\n");
[cf02dd6]612        g_free(buf);
[07ab1cb]613        g_free(s);
614}
615
616static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond)
617{
618        GaimConnection *gc = data;
[cf02dd6]619        OscarData *od;
[07ab1cb]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);
[cf02dd6]640        aim_request_login(sess, conn, gaim_account_get_username(gaim_connection_get_account(gc)));
641
[07ab1cb]642        gaim_debug(GAIM_DEBUG_INFO, "oscar",
[cf02dd6]643                           "Screen name sent, waiting for response\n");
644        gaim_connection_update_progress(gc, _("Screen name sent"), 1, OSCAR_CONNECT_STEPS);
[07ab1cb]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);
[cf02dd6]651        OscarData *od = gc->proto_data = g_new0(OscarData, 1);
[07ab1cb]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);
[cf02dd6]664        aim_session_init(sess, TRUE, 0);
[07ab1cb]665        aim_setdebuggingcb(sess, oscar_debug);
[cf02dd6]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         */
[07ab1cb]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        }
[cf02dd6]693
694        gaim_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS);
[07ab1cb]695}
696
697static void oscar_close(GaimConnection *gc) {
[cf02dd6]698        OscarData *od = (OscarData *)gc->proto_data;
[07ab1cb]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;
[cf02dd6]764        OscarData *od;
[07ab1cb]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);
[cf02dd6]785
[07ab1cb]786        gaim_connection_update_progress(gc,
[cf02dd6]787                        _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS);
[07ab1cb]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;
[cf02dd6]826        OscarData *od = gc->proto_data;
[07ab1cb]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;
[cf02dd6]892        OscarData *od = gc->proto_data;
[07ab1cb]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;
[cf02dd6]911        OscarData *od = gc->proto_data;
[07ab1cb]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;
[cf02dd6]928        OscarData *od = gc->proto_data;
[07ab1cb]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) {
[cf02dd6]999        OscarData *od = (OscarData *)gc->proto_data;
[07ab1cb]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;
[cf02dd6]1030        OscarData *od = gc->proto_data;
[07ab1cb]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);
[cf02dd6]1128        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_userinfo, 0);
[07ab1cb]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
[cf02dd6]1155        od->conn = bosconn;
[07ab1cb]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
[cf02dd6]1174        gaim_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
1175
[07ab1cb]1176        return 1;
1177}
1178
[cf02dd6]1179/* XXX - Should use gaim_url_fetch for the below stuff */
[07ab1cb]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;
[cf02dd6]1193        OscarData *od = pos->gc->proto_data;
[07ab1cb]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,
[cf02dd6]1212                                                        _("Gaim was unable to get a valid AIM login hash."),
[07ab1cb]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,
[cf02dd6]1242                                                        _("Gaim was unable to get a valid AIM login hash."),
[07ab1cb]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,
[cf02dd6]1330                                                        _("Gaim was unable to get a valid login hash."),
[07ab1cb]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;
[cf02dd6]1339        OscarData *od = gc->proto_data;
[07ab1cb]1340        GaimAccount *account = gaim_connection_get_account(gc);
1341        GaimAccount *ac = gaim_connection_get_account(gc);
[cf02dd6]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;
[07ab1cb]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
[cf02dd6]1362        gaim_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS);
1363
[07ab1cb]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);
[cf02dd6]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);
[07ab1cb]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
[cf02dd6]1404        aim_email_sendcookies(sess);
1405        aim_email_activate(sess);
[07ab1cb]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;
[cf02dd6]1413        OscarData *od = gc->proto_data;
[07ab1cb]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;
[cf02dd6]1432        OscarData *od;
[07ab1cb]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;
[cf02dd6]1461        OscarData *od;
[07ab1cb]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;
[cf02dd6]1491        OscarData *od;
[07ab1cb]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;
[cf02dd6]1523        OscarData *od;
[07ab1cb]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;
[cf02dd6]1552        OscarData *od;
[07ab1cb]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;
[cf02dd6]1745        OscarData *od = gc->proto_data;
[07ab1cb]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
[cf02dd6]1786        if (caps & AIM_CAPS_ICQ_DIRECT)
1787                caps ^= AIM_CAPS_ICQ_DIRECT;
[07ab1cb]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
[cf02dd6]1802        bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, info->sn));
[07ab1cb]1803        if (!bi) {
1804                bi = g_new0(struct buddyinfo, 1);
[cf02dd6]1805                g_hash_table_insert(od->buddyinfo, g_strdup(gaim_normalize(gc->account, info->sn)), bi);
[07ab1cb]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);
[cf02dd6]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);
[07ab1cb]1817                        g_free(enc);
1818                } else {
1819                        /* No explicit encoding means utf8.  Yay. */
[cf02dd6]1820                        bi->availmsg = g_strdup(info->avail);
[07ab1cb]1821                }
1822        else
1823                bi->availmsg = NULL;
1824
1825        /* Server stored icon stuff */
1826        if (info->iconcsumlen) {
[cf02dd6]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);
[07ab1cb]1832                b = gaim_find_buddy(gc->account, info->sn);
[cf02dd6]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
[07ab1cb]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) {
[cf02dd6]1849                                od->requesticon = g_slist_append(od->requesticon, g_strdup(gaim_normalize(gc->account, info->sn)));
[07ab1cb]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;
[cf02dd6]1865        OscarData *od = gc->proto_data;
[07ab1cb]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
[cf02dd6]1875        g_hash_table_remove(od->buddyinfo, gaim_normalize(gc->account, info->sn));
[07ab1cb]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;
[cf02dd6]1890        OscarData *od = gc->proto_data;
[07ab1cb]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;
[cf02dd6]1929        OscarData *od = (OscarData *)gc->proto_data;
[07ab1cb]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;
[cf02dd6]2002        OscarData *od = gc->proto_data;
[07ab1cb]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;
[cf02dd6]2047        OscarData *od = gc->proto_data;
[07ab1cb]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;
[cf02dd6]2079        OscarData *od = gc->proto_data;
[07ab1cb]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;
[cf02dd6]2104        OscarData *od;
[07ab1cb]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
[cf02dd6]2114        od = (OscarData *)gc->proto_data;
[07ab1cb]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;
[cf02dd6]2163        OscarData *od = gc->proto_data;
2164        gchar *tmp;
2165        GaimConvImFlags flags = 0;
[07ab1cb]2166        gsize convlen;
2167        GError *err = NULL;
2168        struct buddyinfo *bi;
2169        const char *iconfile;
2170
[cf02dd6]2171        bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, userinfo->sn));
[07ab1cb]2172        if (!bi) {
2173                bi = g_new0(struct buddyinfo, 1);
[cf02dd6]2174                g_hash_table_insert(od->buddyinfo, g_strdup(gaim_normalize(gc->account, userinfo->sn)), bi);
[07ab1cb]2175        }
2176
2177        if (args->icbmflags & AIM_IMFLAGS_AWAY)
[cf02dd6]2178                flags |= GAIM_CONV_IM_AUTO_RESP;
[07ab1cb]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))) && 
[cf02dd6]2197            (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) {
[07ab1cb]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);
[cf02dd6]2241                        tmp = g_strdup(_("(There was an error receiving this message)"));
[07ab1cb]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);
[cf02dd6]2263                        tmp = g_strdup(_("(There was an error receiving this message)"));
[07ab1cb]2264                        g_error_free(err);
2265                }
2266        }
2267
[cf02dd6]2268        /* gaim_str_strip_cr(tmp); */
[07ab1cb]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;
[cf02dd6]2277        OscarData *od = gc->proto_data;
[07ab1cb]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)) {
[cf02dd6]2438                OscarData *od = gc->proto_data;
[07ab1cb]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)) {
[cf02dd6]2464                /* OscarData *od = gc->proto_data; */
[07ab1cb]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)) {
[cf02dd6]2502                OscarData *od = gc->proto_data;
[07ab1cb]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);
[cf02dd6]2509                gaim_account_notify_added(gc->account, NULL, data->name, (buddy ? gaim_get_buddy_alias_only(buddy) : NULL), NULL);
[07ab1cb]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)) {
[cf02dd6]2523                OscarData *od = gc->proto_data;
[07ab1cb]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
[cf02dd6]2540/* When someone sends you buddies */
2541static void gaim_icq_buddyadd(struct name_data *data) {
[07ab1cb]2542        GaimConnection *gc = data->gc;
2543
2544        if (g_list_find(gaim_connections_get_all(), gc)) {
[cf02dd6]2545                gaim_blist_request_add_buddy(gaim_connection_get_account(gc), data->name, NULL, data->nick);
[07ab1cb]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++) {
[cf02dd6]2568                gaim_str_strip_cr(msg1[i]);
[07ab1cb]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) {
[cf02dd6]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                                }
[07ab1cb]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) {
[cf02dd6]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."));
[07ab1cb]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 */
[cf02dd6]2638                        gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args->uin);
[07ab1cb]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 */
[cf02dd6]2670                        /* Someone added you to their buddy list? */
[07ab1cb]2671                } break;
2672
[cf02dd6]2673                case 0x13: { /* Someone has sent you some ICQ buddies */
[07ab1cb]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);
[cf02dd6]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]);
[07ab1cb]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,
[cf02dd6]2689                                                                                _("Do you want to add this buddy "
2690                                                                                  "to your buddy list?"),
[07ab1cb]2691                                                                                0, data, 2,
[cf02dd6]2692                                                                                _("Add"), G_CALLBACK(gaim_icq_buddyadd),
[07ab1cb]2693                                                                                _("Decline"), G_CALLBACK(oscar_free_name_data));
2694                                        g_free(message);
2695                                }
2696                                g_strfreev(text);
2697                        }
2698                } break;
2699
[cf02dd6]2700                case 0x1a: { /* Someone has sent you a greeting card or requested buddies? */
[07ab1cb]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;
[cf02dd6]2855        OscarData *od = gc->proto_data;
[07ab1cb]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
[cf02dd6]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);
[07ab1cb]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;
[cf02dd6]2959        OscarData *od = gc->proto_data;
[07ab1cb]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
[cf02dd6]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 */
[07ab1cb]3029static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
[cf02dd6]3030        gchar *buf;
[07ab1cb]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
[cf02dd6]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        }
[07ab1cb]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: