source: libfaim/oscar.c @ 15283bb

barnowl_perlaimdebianowlrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 15283bb was 07ab1cb, checked in by James M. Kretchmar <kretch@mit.edu>, 20 years ago
Reverted some error messages to makemsg's
  • Property mode set to 100644
File size: 199.3 KB
Line 
1/*
2 * gaim
3 *
4 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 * Some code copyright (C) 1999-2001, Eric Warmenhoven
6 * Some code copyright (C) 2001-2003, Sean Egan
7 * Some code copyright (C) 2001-2003, Mark Doliner <thekingant@users.sourceforge.net>
8 *
9 * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx>
10 * Some libfaim code copyright (C) 2001-2003 Mark Doliner <thekingant@users.sourceforge.net>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 *
26 */
27#include "internal.h"
28
29#include "account.h"
30#include "accountopt.h"
31#include "buddyicon.h"
32#include "conversation.h"
33#include "debug.h"
34#include "ft.h"
35#include "imgstore.h"
36#include "multi.h"
37#include "notify.h"
38#include "privacy.h"
39#include "prpl.h"
40#include "proxy.h"
41#include "request.h"
42#include "util.h"
43#include "html.h"
44
45#include "aim.h"
46#include "md5.h"
47
48/* XXX CORE/UI */
49#include "gtkinternal.h"
50#include "gaim.h"
51
52
53#define UC_AOL          0x02
54#define UC_ADMIN        0x04
55#define UC_UNCONFIRMED  0x08
56#define UC_NORMAL       0x10
57#define UC_AB           0x20
58#define UC_WIRELESS     0x40
59#define UC_HIPTOP       0x80
60
61#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
62
63static GaimPlugin *my_protocol = NULL;
64
65static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_INTEROPERATE;
66static int caps_icq = AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_ICQUTF8 | AIM_CAPS_INTEROPERATE;
67
68static fu8_t features_aim[] = {0x01, 0x01, 0x01, 0x02};
69static fu8_t features_icq[] = {0x01, 0x06};
70
71struct oscar_data {
72        aim_session_t *sess;
73        aim_conn_t *conn;
74
75        guint cnpa;
76        guint paspa;
77        guint emlpa;
78        guint icopa;
79
80        gboolean iconconnecting;
81        gboolean set_icon;
82
83        GSList *create_rooms;
84
85        gboolean conf;
86        gboolean reqemail;
87        gboolean setemail;
88        char *email;
89        gboolean setnick;
90        char *newsn;
91        gboolean chpass;
92        char *oldp;
93        char *newp;
94       
95        GSList *oscar_chats;
96        GSList *direct_ims;
97        GSList *file_transfers;
98        GHashTable *buddyinfo;
99        GSList *requesticon;
100
101        gboolean killme;
102        gboolean icq;
103        GSList *evilhack;
104        guint icontimer;
105        guint getblisttimer;
106
107        struct {
108                guint maxwatchers; /* max users who can watch you */
109                guint maxbuddies; /* max users you can watch */
110                guint maxgroups; /* max groups in server list */
111                guint maxpermits; /* max users on permit list */
112                guint maxdenies; /* max users on deny list */
113                guint maxsiglen; /* max size (bytes) of profile */
114                guint maxawaymsglen; /* max size (bytes) of posted away message */
115        } rights;
116};
117
118struct create_room {
119        char *name;
120        int exchange;
121};
122
123struct chat_connection {
124        char *name;
125        char *show; /* AOL did something funny to us */
126        fu16_t exchange;
127        fu16_t instance;
128        int fd; /* this is redundant since we have the conn below */
129        aim_conn_t *conn;
130        int inpa;
131        int id;
132        GaimConnection *gc; /* i hate this. */
133        GaimConversation *cnv; /* bah. */
134        int maxlen;
135        int maxvis;
136};
137
138struct direct_im {
139        GaimConnection *gc;
140        char name[80];
141        int watcher;
142        aim_conn_t *conn;
143        gboolean connected;
144};
145
146struct ask_direct {
147        GaimConnection *gc;
148        char *sn;
149        char ip[64];
150        fu8_t cookie[8];
151};
152
153/* Various PRPL-specific buddy info that we want to keep track of */
154struct buddyinfo {
155        time_t signon;
156        int caps;
157        gboolean typingnot;
158        gchar *availmsg;
159        fu32_t ipaddr;
160
161        unsigned long ico_me_len;
162        unsigned long ico_me_csum;
163        time_t ico_me_time;
164        gboolean ico_informed;
165
166        unsigned long ico_len;
167        unsigned long ico_csum;
168        time_t ico_time;
169        gboolean ico_need;
170
171        fu16_t iconcsumlen;
172        fu8_t *iconcsum;
173};
174
175struct name_data {
176        GaimConnection *gc;
177        gchar *name;
178        gchar *nick;
179};
180
181static char *msgerrreason[] = {
182        N_("Invalid error"),
183        N_("Invalid SNAC"),
184        N_("Rate to host"),
185        N_("Rate to client"),
186        N_("Not logged in"),
187        N_("Service unavailable"),
188        N_("Service not defined"),
189        N_("Obsolete SNAC"),
190        N_("Not supported by host"),
191        N_("Not supported by client"),
192        N_("Refused by client"),
193        N_("Reply too big"),
194        N_("Responses lost"),
195        N_("Request denied"),
196        N_("Busted SNAC payload"),
197        N_("Insufficient rights"),
198        N_("In local permit/deny"),
199        N_("Too evil (sender)"),
200        N_("Too evil (receiver)"),
201        N_("User temporarily unavailable"),
202        N_("No match"),
203        N_("List overflow"),
204        N_("Request ambiguous"),
205        N_("Queue full"),
206        N_("Not while on AOL")
207};
208static int msgerrreasonlen = 25;
209
210/* All the libfaim->gaim callback functions */
211static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
212static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
213static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
214static int gaim_info_change      (aim_session_t *, aim_frame_t *, ...);
215static int gaim_account_confirm  (aim_session_t *, aim_frame_t *, ...);
216static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
217static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
218static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
219static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
220static int gaim_parse_clientauto (aim_session_t *, aim_frame_t *, ...);
221static int gaim_parse_user_info  (aim_session_t *, aim_frame_t *, ...);
222static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
223static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
224static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
225static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
226static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
227static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
228static int gaim_email_parseupdate(aim_session_t *, aim_frame_t *, ...);
229static int gaim_icon_error       (aim_session_t *, aim_frame_t *, ...);
230static int gaim_icon_parseicon   (aim_session_t *, aim_frame_t *, ...);
231static int oscar_icon_req        (aim_session_t *, aim_frame_t *, ...);
232static int gaim_parse_msgack     (aim_session_t *, aim_frame_t *, ...);
233static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
234static int gaim_parse_evilnotify (aim_session_t *, aim_frame_t *, ...);
235static int gaim_parse_searcherror(aim_session_t *, aim_frame_t *, ...);
236static int gaim_parse_searchreply(aim_session_t *, aim_frame_t *, ...);
237static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
238static int gaim_connerr          (aim_session_t *, aim_frame_t *, ...);
239static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
240static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
241static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
242static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
243static int conninitdone_email    (aim_session_t *, aim_frame_t *, ...);
244static int conninitdone_icon     (aim_session_t *, aim_frame_t *, ...);
245static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
246static int gaim_parse_mtn        (aim_session_t *, aim_frame_t *, ...);
247static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
248static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
249static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
250static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
251static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
252static int gaim_memrequest       (aim_session_t *, aim_frame_t *, ...);
253static int gaim_selfinfo         (aim_session_t *, aim_frame_t *, ...);
254static int gaim_offlinemsg       (aim_session_t *, aim_frame_t *, ...);
255static int gaim_offlinemsgdone   (aim_session_t *, aim_frame_t *, ...);
256static int gaim_icqalias         (aim_session_t *, aim_frame_t *, ...);
257static int gaim_icqinfo          (aim_session_t *, aim_frame_t *, ...);
258static int gaim_popup            (aim_session_t *, aim_frame_t *, ...);
259#ifndef NOSSI
260static int gaim_ssi_parseerr     (aim_session_t *, aim_frame_t *, ...);
261static int gaim_ssi_parserights  (aim_session_t *, aim_frame_t *, ...);
262static int gaim_ssi_parselist    (aim_session_t *, aim_frame_t *, ...);
263static int gaim_ssi_parseack     (aim_session_t *, aim_frame_t *, ...);
264static int gaim_ssi_authgiven    (aim_session_t *, aim_frame_t *, ...);
265static int gaim_ssi_authrequest  (aim_session_t *, aim_frame_t *, ...);
266static int gaim_ssi_authreply    (aim_session_t *, aim_frame_t *, ...);
267static int gaim_ssi_gotadded     (aim_session_t *, aim_frame_t *, ...);
268#endif
269
270/* for DirectIM/image transfer */
271static int gaim_odc_initiate     (aim_session_t *, aim_frame_t *, ...);
272static int gaim_odc_incoming     (aim_session_t *, aim_frame_t *, ...);
273static int gaim_odc_typing       (aim_session_t *, aim_frame_t *, ...);
274static int gaim_odc_update_ui    (aim_session_t *, aim_frame_t *, ...);
275
276/* for file transfer */
277static int oscar_sendfile_estblsh(aim_session_t *, aim_frame_t *, ...);
278static int oscar_sendfile_prompt (aim_session_t *, aim_frame_t *, ...);
279static int oscar_sendfile_ack    (aim_session_t *, aim_frame_t *, ...);
280static int oscar_sendfile_done   (aim_session_t *, aim_frame_t *, ...);
281
282/* for icons */
283static gboolean gaim_icon_timerfunc(gpointer data);
284
285/* prpl actions - remove this at some point */
286static void oscar_set_info(GaimConnection *gc, const char *text);
287
288static void oscar_free_name_data(struct name_data *data) {
289        g_free(data->name);
290        g_free(data->nick);
291        g_free(data);
292}
293
294static void oscar_free_buddyinfo(void *data) {
295        struct buddyinfo *bi = data;
296        g_free(bi->availmsg);
297        g_free(bi->iconcsum);
298        g_free(bi);
299}
300
301static fu32_t oscar_encoding_check(const char *utf8)
302{
303        int i = 0;
304        fu32_t encodingflag = 0;
305
306        /* Determine how we can send this message.  Per the warnings elsewhere
307         * in this file, these little checks determine the simplest encoding
308         * we can use for a given message send using it. */
309        while (utf8[i]) {
310                if ((unsigned char)utf8[i] > 0x7f) {
311                        /* not ASCII! */
312                        encodingflag = AIM_IMFLAGS_ISO_8859_1;
313                        break;
314                }
315                i++;
316        }
317        while (utf8[i]) {
318                /* ISO-8859-1 is 0x00-0xbf in the first byte
319                 * followed by 0xc0-0xc3 in the second */
320                if ((unsigned char)utf8[i] < 0x80) {
321                        i++;
322                        continue;
323                } else if (((unsigned char)utf8[i] & 0xfc) == 0xc0 &&
324                           ((unsigned char)utf8[i + 1] & 0xc0) == 0x80) {
325                        i += 2;
326                        continue;
327                }
328                encodingflag = AIM_IMFLAGS_UNICODE;
329                break;
330        }
331
332        return encodingflag;
333}
334
335static fu32_t oscar_encoding_parse(const char *enc)
336{
337        char *charset;
338
339        /* If anything goes wrong, fall back on ASCII and print a message */
340        if (enc == NULL) {
341                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
342                                   "Encoding was null, that's odd\n");
343                return 0;
344        }
345        charset = strstr(enc, "charset=");
346        if (charset == NULL) {
347                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
348                                   "No charset specified for info, assuming ASCII\n");
349                return 0;
350        }
351        charset += 8;
352        if (!strcmp(charset, "\"us-ascii\"") || !strcmp(charset, "\"utf-8\"")) {
353                /* UTF-8 is our native charset, ASCII is a proper subset */
354                return 0;
355        } else if (!strcmp(charset, "\"iso-8859-1\"")) {
356                return AIM_IMFLAGS_ISO_8859_1;
357        } else if (!strcmp(charset, "\"unicode-2-0\"")) {
358                return AIM_IMFLAGS_UNICODE;
359        } else {
360                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
361                                   "Unrecognized character set '%s', using ASCII\n", charset);
362                return 0;
363        }
364}
365
366gchar *oscar_encoding_to_utf8(const char *encoding, char *text, int textlen)
367{
368        gchar *utf8 = NULL;
369        int flags = oscar_encoding_parse(encoding);
370
371        switch (flags) {
372        case 0:
373                utf8 = g_strndup(text, textlen);
374                break;
375        case AIM_IMFLAGS_ISO_8859_1:
376                utf8 = g_convert(text, textlen, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
377                break;
378        case AIM_IMFLAGS_UNICODE:
379                utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", NULL, NULL, NULL);
380                break;
381        }
382
383        return utf8;
384}
385
386static struct direct_im *find_direct_im(struct oscar_data *od, const char *who) {
387        GSList *d = od->direct_ims;
388        struct direct_im *m = NULL;
389
390        while (d) {
391                m = (struct direct_im *)d->data;
392                if (!aim_sncmp(who, m->name))
393                        return m;
394                d = d->next;
395        }
396
397        return NULL;
398}
399
400static char *extract_name(const char *name) {
401        char *tmp, *x;
402        int i, j;
403
404        if (!name)
405                return NULL;
406       
407        x = strchr(name, '-');
408
409        if (!x) return NULL;
410        x = strchr(++x, '-');
411        if (!x) return NULL;
412        tmp = g_strdup(++x);
413
414        for (i = 0, j = 0; x[i]; i++) {
415                char hex[3];
416                if (x[i] != '%') {
417                        tmp[j++] = x[i];
418                        continue;
419                }
420                strncpy(hex, x + ++i, 2); hex[2] = 0;
421                i++;
422                tmp[j++] = strtol(hex, NULL, 16);
423        }
424
425        tmp[j] = 0;
426        return tmp;
427}
428
429static struct chat_connection *find_oscar_chat(GaimConnection *gc, int id) {
430        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
431        struct chat_connection *c = NULL;
432
433        while (g) {
434                c = (struct chat_connection *)g->data;
435                if (c->id == id)
436                        break;
437                g = g->next;
438                c = NULL;
439        }
440
441        return c;
442}
443
444static struct chat_connection *find_oscar_chat_by_conn(GaimConnection *gc,
445                                                        aim_conn_t *conn) {
446        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
447        struct chat_connection *c = NULL;
448
449        while (g) {
450                c = (struct chat_connection *)g->data;
451                if (c->conn == conn)
452                        break;
453                g = g->next;
454                c = NULL;
455        }
456
457        return c;
458}
459
460static void gaim_odc_disconnect(aim_session_t *sess, aim_conn_t *conn) {
461        GaimConnection *gc = sess->aux_data;
462        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
463        GaimConversation *cnv;
464        struct direct_im *dim;
465        char *sn;
466        char buf[256];
467
468        sn = g_strdup(aim_odc_getsn(conn));
469
470        gaim_debug(GAIM_DEBUG_INFO, "oscar",
471                           "%s disconnected Direct IM.\n", sn);
472
473        dim = find_direct_im(od, sn);
474        od->direct_ims = g_slist_remove(od->direct_ims, dim);
475        gaim_input_remove(dim->watcher);
476
477        if (dim->connected)
478                g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), sn);
479        else 
480                g_snprintf(buf, sizeof buf, _("Direct IM with %s failed"), sn);
481
482        cnv = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc));
483        if (cnv)
484                gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
485
486        gaim_conversation_update_progress(cnv, 0);
487
488        g_free(dim); /* I guess? I don't see it anywhere else... -- mid */
489        g_free(sn);
490
491        return;
492}
493
494static void oscar_callback(gpointer data, gint source, GaimInputCondition condition) {
495        aim_conn_t *conn = (aim_conn_t *)data;
496        aim_session_t *sess = aim_conn_getsess(conn);
497        GaimConnection *gc = sess ? sess->aux_data : NULL;
498        struct oscar_data *od;
499
500        if (!gc) {
501                /* gc is null. we return, else we seg SIGSEG on next line. */
502                gaim_debug(GAIM_DEBUG_INFO, "oscar",
503                                   "oscar callback for closed connection (1).\n");
504                return;
505        }
506     
507        od = (struct oscar_data *)gc->proto_data;
508
509        if (!g_list_find(gaim_connections_get_all(), gc)) {
510                /* oh boy. this is probably bad. i guess the only thing we
511                 * can really do is return? */
512                gaim_debug(GAIM_DEBUG_INFO, "oscar",
513                                   "oscar callback for closed connection (2).\n");
514                gaim_debug(GAIM_DEBUG_MISC, "oscar", "gc = %p\n", gc);
515                return;
516        }
517
518        if (condition & GAIM_INPUT_READ) {
519                if (conn->type == AIM_CONN_TYPE_LISTENER) {
520                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
521                                           "got information on rendezvous listener\n");
522                        if (aim_handlerendconnect(od->sess, conn) < 0) {
523                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
524                                                   "connection error (rendezvous listener)\n");
525                                aim_conn_kill(od->sess, &conn);
526                        }
527                } else {
528                        if (aim_get_command(od->sess, conn) >= 0) {
529                                aim_rxdispatch(od->sess);
530                                if (od->killme) {
531                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar", "Waiting to be destroyed\n");
532                                        return;
533                                }
534                        } else {
535                                if ((conn->type == AIM_CONN_TYPE_BOS) ||
536                                           !(aim_getconn_type(od->sess, AIM_CONN_TYPE_BOS))) {
537                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
538                                                           "major connection error\n");
539                                        gaim_connection_error(gc, _("Disconnected."));
540                                } else if (conn->type == AIM_CONN_TYPE_CHAT) {
541                                        struct chat_connection *c = find_oscar_chat_by_conn(gc, conn);
542                                        char *buf;
543                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
544                                                           "disconnected from chat room %s\n", c->name);
545                                        c->conn = NULL;
546                                        if (c->inpa > 0)
547                                                gaim_input_remove(c->inpa);
548                                        c->inpa = 0;
549                                        c->fd = -1;
550                                        aim_conn_kill(od->sess, &conn);
551                                        buf = g_strdup_printf(_("You have been disconnected from chat room %s."), c->name);
552                                        gaim_notify_error(gc, NULL, buf, NULL);
553                                        g_free(buf);
554                                } else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
555                                        if (od->cnpa > 0)
556                                                gaim_input_remove(od->cnpa);
557                                        od->cnpa = 0;
558                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
559                                                           "removing chatnav input watcher\n");
560                                        while (od->create_rooms) {
561                                                struct create_room *cr = od->create_rooms->data;
562                                                g_free(cr->name);
563                                                od->create_rooms =
564                                                        g_slist_remove(od->create_rooms, cr);
565                                                g_free(cr);
566                                                gaim_notify_error(gc, NULL,
567                                                                                  _("Chat is currently unavailable"),
568                                                                                  NULL);
569                                        }
570                                        aim_conn_kill(od->sess, &conn);
571                                } else if (conn->type == AIM_CONN_TYPE_AUTH) {
572                                        if (od->paspa > 0)
573                                                gaim_input_remove(od->paspa);
574                                        od->paspa = 0;
575                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
576                                                           "removing authconn input watcher\n");
577                                        aim_conn_kill(od->sess, &conn);
578                                } else if (conn->type == AIM_CONN_TYPE_EMAIL) {
579                                        if (od->emlpa > 0)
580                                                gaim_input_remove(od->emlpa);
581                                        od->emlpa = 0;
582                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
583                                                           "removing email input watcher\n");
584                                        aim_conn_kill(od->sess, &conn);
585                                } else if (conn->type == AIM_CONN_TYPE_ICON) {
586                                        if (od->icopa > 0)
587                                                gaim_input_remove(od->icopa);
588                                        od->icopa = 0;
589                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
590                                                           "removing icon input watcher\n");
591                                        aim_conn_kill(od->sess, &conn);
592                                } else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
593                                        if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
594                                                gaim_odc_disconnect(od->sess, conn);
595                                        aim_conn_kill(od->sess, &conn);
596                                } else {
597                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
598                                                           "holy crap! generic connection error! %hu\n",
599                                                           conn->type);
600                                        aim_conn_kill(od->sess, &conn);
601                                }
602                        }
603                }
604        }
605}
606
607static void oscar_debug(aim_session_t *sess, int level, const char *format, va_list va) {
608        char *s = g_strdup_vprintf(format, va);
609        char buf[256];
610        char *t;
611        GaimConnection *gc = sess->aux_data;
612
613        g_snprintf(buf, sizeof(buf), "%s %d: ", gaim_account_get_username(gaim_connection_get_account(gc)), level);
614        t = g_strconcat(buf, s, NULL);
615        gaim_debug(GAIM_DEBUG_INFO, "oscar", t);
616        if (t[strlen(t)-1] != '\n')
617                gaim_debug(GAIM_DEBUG_INFO, NULL, "\n");
618        g_free(t);
619        g_free(s);
620}
621
622static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond)
623{
624        GaimConnection *gc = data;
625        struct oscar_data *od;
626        aim_session_t *sess;
627        aim_conn_t *conn;
628
629        if (!g_list_find(gaim_connections_get_all(), gc)) {
630                close(source);
631                return;
632        }
633
634        od = gc->proto_data;
635        sess = od->sess;
636        conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
637       
638        conn->fd = source;
639
640        if (source < 0) {
641                gaim_connection_error(gc, _("Couldn't connect to host"));
642                return;
643        }
644
645        aim_conn_completeconnect(sess, conn);
646        gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ, oscar_callback, conn);
647        gaim_debug(GAIM_DEBUG_INFO, "oscar",
648                           "Password sent, waiting for response\n");
649}
650
651static void oscar_login(GaimAccount *account) {
652        aim_session_t *sess;
653        aim_conn_t *conn;
654        char buf[256];
655        GaimConnection *gc = gaim_account_get_connection(account);
656        struct oscar_data *od = gc->proto_data = g_new0(struct oscar_data, 1);
657
658        gaim_debug(GAIM_DEBUG_MISC, "oscar", "oscar_login: gc = %p\n", gc);
659
660        if (isdigit(*(gaim_account_get_username(account)))) {
661                od->icq = TRUE;
662        } else {
663                gc->flags |= GAIM_CONNECTION_HTML;
664                gc->flags |= GAIM_CONNECTION_AUTO_RESP;
665        }
666        od->buddyinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, oscar_free_buddyinfo);
667
668        sess = g_new0(aim_session_t, 1);
669
670        aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
671        aim_setdebuggingcb(sess, oscar_debug);
672
673        /* we need an immediate queue because we don't use a while-loop to
674         * see if things need to be sent. */
675        aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
676        od->sess = sess;
677        sess->aux_data = gc;
678
679        conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
680        if (conn == NULL) {
681                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
682                                   "internal connection error\n");
683                gaim_connection_error(gc, _("Unable to login to AIM"));
684                return;
685        }
686
687        g_snprintf(buf, sizeof(buf), _("Signon: %s"), gaim_account_get_username(account));
688        gaim_connection_update_progress(gc, buf, 2, 5);
689
690        aim_conn_addhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
691        aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
692        aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
693
694        conn->status |= AIM_CONN_STATUS_INPROGRESS;
695        if (gaim_proxy_connect(account, gaim_account_get_string(account, "server", FAIM_LOGIN_SERVER),
696                          gaim_account_get_int(account, "port", FAIM_LOGIN_PORT),
697                          oscar_login_connect, gc) < 0) {
698                gaim_connection_error(gc, _("Couldn't connect to host"));
699                return;
700        }
701        aim_request_login(sess, conn, gaim_account_get_username(account));
702}
703
704static void oscar_close(GaimConnection *gc) {
705        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
706
707        while (od->oscar_chats) {
708                struct chat_connection *n = od->oscar_chats->data;
709                if (n->inpa > 0)
710                        gaim_input_remove(n->inpa);
711                g_free(n->name);
712                g_free(n->show);
713                od->oscar_chats = g_slist_remove(od->oscar_chats, n);
714                g_free(n);
715        }
716        while (od->direct_ims) {
717                struct direct_im *n = od->direct_ims->data;
718                if (n->watcher > 0)
719                        gaim_input_remove(n->watcher);
720                od->direct_ims = g_slist_remove(od->direct_ims, n);
721                g_free(n);
722        }
723/* BBB */
724        while (od->file_transfers) {
725                GaimXfer *xfer;
726                xfer = (GaimXfer *)od->file_transfers->data;
727                gaim_xfer_destroy(xfer);
728        }
729        while (od->requesticon) {
730                char *sn = od->requesticon->data;
731                od->requesticon = g_slist_remove(od->requesticon, sn);
732                free(sn);
733        }
734        g_hash_table_destroy(od->buddyinfo);
735        while (od->evilhack) {
736                g_free(od->evilhack->data);
737                od->evilhack = g_slist_remove(od->evilhack, od->evilhack->data);
738        }
739        while (od->create_rooms) {
740                struct create_room *cr = od->create_rooms->data;
741                g_free(cr->name);
742                od->create_rooms = g_slist_remove(od->create_rooms, cr);
743                g_free(cr);
744        }
745        if (od->email)
746                g_free(od->email);
747        if (od->newp)
748                g_free(od->newp);
749        if (od->oldp)
750                g_free(od->oldp);
751        if (gc->inpa > 0)
752                gaim_input_remove(gc->inpa);
753        if (od->cnpa > 0)
754                gaim_input_remove(od->cnpa);
755        if (od->paspa > 0)
756                gaim_input_remove(od->paspa);
757        if (od->emlpa > 0)
758                gaim_input_remove(od->emlpa);
759        if (od->icopa > 0)
760                gaim_input_remove(od->icopa);
761        if (od->icontimer > 0)
762                g_source_remove(od->icontimer);
763        if (od->getblisttimer)
764                g_source_remove(od->getblisttimer);
765        aim_session_kill(od->sess);
766        g_free(od->sess);
767        od->sess = NULL;
768        g_free(gc->proto_data);
769        gc->proto_data = NULL;
770        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Signed off.\n");
771}
772
773static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) {
774        GaimConnection *gc = data;
775        struct oscar_data *od;
776        aim_session_t *sess;
777        aim_conn_t *bosconn;
778
779        if (!g_list_find(gaim_connections_get_all(), gc)) {
780                close(source);
781                return;
782        }
783
784        od = gc->proto_data;
785        sess = od->sess;
786        bosconn = od->conn;     
787        bosconn->fd = source;
788
789        if (source < 0) {
790                gaim_connection_error(gc, _("Could Not Connect"));
791                return;
792        }
793
794        aim_conn_completeconnect(sess, bosconn);
795        gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ, oscar_callback, bosconn);
796        gaim_connection_update_progress(gc,
797                        _("Connection established, cookie sent"), 4, 5);
798}
799
800/* BBB */
801/*
802 * This little area in oscar.c is the nexus of file transfer code,
803 * so I wrote a little explanation of what happens.  I am such a
804 * ninja.
805 *
806 * The series of events for a file send is:
807 *  -Create xfer and call gaim_xfer_request (this happens in oscar_ask_sendfile)
808 *  -User chooses a file and oscar_xfer_init is called.  It establishs a
809 *   listening socket, then asks the remote user to connect to us (and
810 *   gives them the file name, port, IP, etc.)
811 *  -They connect to us and we send them an AIM_CB_OFT_PROMPT (this happens
812 *   in oscar_sendfile_estblsh)
813 *  -They send us an AIM_CB_OFT_ACK and then we start sending data
814 *  -When we finish, they send us an AIM_CB_OFT_DONE and they close the
815 *   connection.
816 *  -We get drunk because file transfer kicks ass.
817 *
818 * The series of events for a file receive is:
819 *  -Create xfer and call gaim_xfer request (this happens in incomingim_chan2)
820 *  -Gaim user selects file to name and location to save file to and
821 *   oscar_xfer_init is called
822 *  -It connects to the remote user using the IP they gave us earlier
823 *  -After connecting, they send us an AIM_CB_OFT_PROMPT.  In reply, we send
824 *   them an AIM_CB_OFT_ACK.
825 *  -They begin to send us lots of raw data.
826 *  -When they finish sending data we send an AIM_CB_OFT_DONE and then close
827 *   the connectionn.
828 */
829static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition);
830
831/* XXX - This function is pretty ugly */
832static void oscar_xfer_init(GaimXfer *xfer)
833{
834        struct aim_oft_info *oft_info = xfer->data;
835        GaimConnection *gc = oft_info->sess->aux_data;
836        struct oscar_data *od = gc->proto_data;
837
838        if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
839                int i;
840
841                xfer->filename = g_path_get_basename(xfer->local_filename);
842                strncpy(oft_info->fh.name, xfer->filename, 64);
843                oft_info->fh.totsize = gaim_xfer_get_size(xfer);
844                oft_info->fh.size = gaim_xfer_get_size(xfer);
845                oft_info->fh.checksum = aim_oft_checksum_file(xfer->local_filename);
846
847                /*
848                 * First try the port specified earlier (5190).  If that fails,
849                 * increment by 1 and try again.
850                 */
851                aim_sendfile_listen(od->sess, oft_info);
852                for (i=0; (i<5 && !oft_info->conn); i++) {
853                        xfer->local_port = oft_info->port = oft_info->port + 1;
854                        aim_sendfile_listen(od->sess, oft_info);
855                }
856                gaim_debug(GAIM_DEBUG_MISC, "oscar",
857                                   "port is %d, ip is %s\n",
858                                   xfer->local_port, oft_info->clientip);
859                if (oft_info->conn) {
860                        xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
861                        aim_im_sendch2_sendfile_ask(od->sess, oft_info);
862                        aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED, oscar_sendfile_estblsh, 0);
863                } else {
864                        gaim_notify_error(gc, NULL, _("File Transfer Aborted"),
865                                                          _("Unable to establish listener socket."));
866                        /* XXX - The below line causes a crash because the transfer is canceled before the "Ok" callback on the file selection thing exists, I think */
867                        /* gaim_xfer_cancel_remote(xfer); */
868                }
869        } else if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
870                oft_info->conn = aim_newconn(od->sess, AIM_CONN_TYPE_RENDEZVOUS, NULL);
871                if (oft_info->conn) {
872                        oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
873                        aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_PROMPT, oscar_sendfile_prompt, 0);
874                        oft_info->conn->fd = xfer->fd = gaim_proxy_connect(gaim_connection_get_account(gc), xfer->remote_ip, xfer->remote_port, 
875                                                                      oscar_sendfile_connected, xfer);
876                        if (xfer->fd == -1) {
877                                gaim_notify_error(gc, NULL, _("File Transfer Aborted"),
878                                                                  _("Unable to establish file descriptor."));
879                                /* gaim_xfer_cancel_remote(xfer); */
880                        }
881                } else {
882                        gaim_notify_error(gc, NULL, _("File Transfer Aborted"),
883                                                          _("Unable to create new connection."));
884                        /* gaim_xfer_cancel_remote(xfer); */
885                        /* Try a different port? Ask them to connect to us? */
886                }
887
888        }
889}
890
891static void oscar_xfer_start(GaimXfer *xfer)
892{
893
894        gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_xfer_start\n");
895        /* I'm pretty sure we don't need to do jack here.  Nor Jill. */
896}
897
898static void oscar_xfer_end(GaimXfer *xfer)
899{
900        struct aim_oft_info *oft_info = xfer->data;
901        GaimConnection *gc = oft_info->sess->aux_data;
902        struct oscar_data *od = gc->proto_data;
903
904        gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_xfer_end\n");
905
906        if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
907                oft_info->fh.nrecvd = gaim_xfer_get_bytes_sent(xfer);
908                aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_DONE, oft_info);
909        }
910
911        aim_conn_kill(oft_info->sess, &oft_info->conn);
912        aim_oft_destroyinfo(oft_info);
913        xfer->data = NULL;
914        od->file_transfers = g_slist_remove(od->file_transfers, xfer);
915}
916
917static void oscar_xfer_cancel_send(GaimXfer *xfer)
918{
919        struct aim_oft_info *oft_info = xfer->data;
920        GaimConnection *gc = oft_info->sess->aux_data;
921        struct oscar_data *od = gc->proto_data;
922
923        gaim_debug(GAIM_DEBUG_INFO, "oscar",
924                           "AAA - in oscar_xfer_cancel_send\n");
925
926        aim_im_sendch2_sendfile_cancel(oft_info->sess, oft_info);
927
928        aim_conn_kill(oft_info->sess, &oft_info->conn);
929        aim_oft_destroyinfo(oft_info);
930        xfer->data = NULL;
931        od->file_transfers = g_slist_remove(od->file_transfers, xfer);
932}
933
934static void oscar_xfer_cancel_recv(GaimXfer *xfer)
935{
936        struct aim_oft_info *oft_info = xfer->data;
937        GaimConnection *gc = oft_info->sess->aux_data;
938        struct oscar_data *od = gc->proto_data;
939
940        gaim_debug(GAIM_DEBUG_INFO, "oscar",
941                           "AAA - in oscar_xfer_cancel_recv\n");
942
943        aim_im_sendch2_sendfile_cancel(oft_info->sess, oft_info);
944
945        aim_conn_kill(oft_info->sess, &oft_info->conn);
946        aim_oft_destroyinfo(oft_info);
947        xfer->data = NULL;
948        od->file_transfers = g_slist_remove(od->file_transfers, xfer);
949}
950
951static void oscar_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size)
952{
953        struct aim_oft_info *oft_info = xfer->data;
954
955        if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
956                /*
957                 * If we're done sending, intercept the socket from the core ft code
958                 * and wait for the other guy to send the "done" OFT packet.
959                 */
960                if (gaim_xfer_get_bytes_remaining(xfer) <= 0) {
961                        gaim_input_remove(xfer->watcher);
962                        xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
963                        xfer->fd = 0;
964                        gaim_xfer_set_completed(xfer, TRUE);
965                }
966        } else if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
967                /* Update our rolling checksum.  Like Walmart, yo. */
968                oft_info->fh.recvcsum = aim_oft_checksum_chunk(buffer, size, oft_info->fh.recvcsum);
969        }
970}
971
972static GaimXfer *oscar_find_xfer_by_cookie(GSList *fts, const char *ck)
973{
974        GaimXfer *xfer;
975        struct aim_oft_info *oft_info;
976
977        while (fts) {
978                xfer = fts->data;
979                oft_info = xfer->data;
980
981                if (oft_info && !strcmp(ck, oft_info->cookie))
982                        return xfer;
983
984                fts = g_slist_next(fts);
985        }
986
987        return NULL;
988}
989
990static GaimXfer *oscar_find_xfer_by_conn(GSList *fts, aim_conn_t *conn)
991{
992        GaimXfer *xfer;
993        struct aim_oft_info *oft_info;
994
995        while (fts) {
996                xfer = fts->data;
997                oft_info = xfer->data;
998
999                if (oft_info && (conn == oft_info->conn))
1000                        return xfer;
1001
1002                fts = g_slist_next(fts);
1003        }
1004
1005        return NULL;
1006}
1007
1008static void oscar_ask_sendfile(GaimConnection *gc, const char *destsn) {
1009        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
1010        GaimXfer *xfer;
1011        struct aim_oft_info *oft_info;
1012
1013        /* You want to send a file to someone else, you're so generous */
1014
1015        /* Build the file transfer handle */
1016        xfer = gaim_xfer_new(gaim_connection_get_account(gc), GAIM_XFER_SEND, destsn);
1017        xfer->local_port = 5190;
1018
1019        /* Create the oscar-specific data */
1020        oft_info = aim_oft_createinfo(od->sess, NULL, destsn, xfer->local_ip, xfer->local_port, 0, 0, NULL);
1021        xfer->data = oft_info;
1022
1023         /* Setup our I/O op functions */
1024        gaim_xfer_set_init_fnc(xfer, oscar_xfer_init);
1025        gaim_xfer_set_start_fnc(xfer, oscar_xfer_start);
1026        gaim_xfer_set_end_fnc(xfer, oscar_xfer_end);
1027        gaim_xfer_set_cancel_send_fnc(xfer, oscar_xfer_cancel_send);
1028        gaim_xfer_set_cancel_recv_fnc(xfer, oscar_xfer_cancel_recv);
1029        gaim_xfer_set_ack_fnc(xfer, oscar_xfer_ack);
1030
1031        /* Keep track of this transfer for later */
1032        od->file_transfers = g_slist_append(od->file_transfers, xfer);
1033
1034        /* Now perform the request */
1035        gaim_xfer_request(xfer);
1036}
1037
1038static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
1039        GaimConnection *gc = sess->aux_data;
1040        struct oscar_data *od = gc->proto_data;
1041        GaimAccount *account = gc->account;
1042        aim_conn_t *bosconn;
1043        char *host; int port;
1044        int i, rc;
1045        va_list ap;
1046        struct aim_authresp_info *info;
1047
1048        port = gaim_account_get_int(account, "port", FAIM_LOGIN_PORT);
1049
1050        va_start(ap, fr);
1051        info = va_arg(ap, struct aim_authresp_info *);
1052        va_end(ap);
1053
1054        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1055                           "inside auth_resp (Screen name: %s)\n", info->sn);
1056
1057        if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) {
1058                char buf[256];
1059                switch (info->errorcode) {
1060                case 0x05:
1061                        /* Incorrect nick/password */
1062                        gc->wants_to_die = TRUE;
1063                        gaim_connection_error(gc, _("Incorrect nickname or password."));
1064                        break;
1065                case 0x11:
1066                        /* Suspended account */
1067                        gc->wants_to_die = TRUE;
1068                        gaim_connection_error(gc, _("Your account is currently suspended."));
1069                        break;
1070                case 0x14:
1071                        /* service temporarily unavailable */
1072                        gaim_connection_error(gc, _("The AOL Instant Messenger service is temporarily unavailable."));
1073                        break;
1074                case 0x18:
1075                        /* connecting too frequently */
1076                        gc->wants_to_die = TRUE;
1077                        gaim_connection_error(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
1078                        break;
1079                case 0x1c:
1080                        /* client too old */
1081                        gc->wants_to_die = TRUE;
1082                        g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), GAIM_WEBSITE);
1083                        gaim_connection_error(gc, buf);
1084                        break;
1085                default:
1086                        gaim_connection_error(gc, _("Authentication failed"));
1087                        break;
1088                }
1089                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1090                                   "Login Error Code 0x%04hx\n", info->errorcode);
1091                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1092                                   "Error URL: %s\n", info->errorurl);
1093                od->killme = TRUE;
1094                return 1;
1095        }
1096
1097
1098        gaim_debug(GAIM_DEBUG_MISC, "oscar",
1099                           "Reg status: %hu\n", info->regstatus);
1100
1101        if (info->email) {
1102                gaim_debug(GAIM_DEBUG_MISC, "oscar", "Email: %s\n", info->email);
1103        } else {
1104                gaim_debug(GAIM_DEBUG_MISC, "oscar", "Email is NULL\n");
1105        }
1106       
1107        gaim_debug(GAIM_DEBUG_MISC, "oscar", "BOSIP: %s\n", info->bosip);
1108        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1109                           "Closing auth connection...\n");
1110        aim_conn_kill(sess, &fr->conn);
1111
1112        bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL);
1113        if (bosconn == NULL) {
1114                gaim_connection_error(gc, _("Internal Error"));
1115                od->killme = TRUE;
1116                return 0;
1117        }
1118
1119        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1120        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0);
1121        aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, gaim_bosrights, 0);
1122        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
1123        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
1124        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0);
1125        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
1126        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
1127        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
1128        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
1129        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
1130        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
1131        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_CLIENTAUTORESP, gaim_parse_clientauto, 0);
1132        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
1133        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0);
1134        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0);
1135        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, gaim_parse_searchreply, 0);
1136        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
1137        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parse_mtn, 0);
1138        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_user_info, 0);
1139        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0);
1140        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
1141        aim_conn_addhandler(sess, bosconn, 0x0004, 0x0005, gaim_icbm_param_info, 0);
1142        aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, gaim_parse_genericerr, 0);
1143        aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, gaim_parse_genericerr, 0);
1144        aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, gaim_parse_genericerr, 0);
1145        aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, gaim_memrequest, 0);
1146        aim_conn_addhandler(sess, bosconn, 0x0001, 0x000f, gaim_selfinfo, 0);
1147        aim_conn_addhandler(sess, bosconn, 0x0001, 0x0021, oscar_icon_req,0);
1148        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
1149        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
1150        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_POP, 0x0002, gaim_popup, 0);
1151        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS, gaim_icqalias, 0);
1152        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0);
1153#ifndef NOSSI
1154        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ERROR, gaim_ssi_parseerr, 0);
1155        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
1156        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
1157        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_NOLIST, gaim_ssi_parselist, 0);
1158        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);
1159        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTH, gaim_ssi_authgiven, 0);
1160        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREQ, gaim_ssi_authrequest, 0);
1161        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RECVAUTHREP, gaim_ssi_authreply, 0);
1162        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_ADDED, gaim_ssi_gotadded, 0);
1163#endif
1164
1165        ((struct oscar_data *)gc->proto_data)->conn = bosconn;
1166        for (i = 0; i < (int)strlen(info->bosip); i++) {
1167                if (info->bosip[i] == ':') {
1168                        port = atoi(&(info->bosip[i+1]));
1169                        break;
1170                }
1171        }
1172        host = g_strndup(info->bosip, i);
1173        bosconn->status |= AIM_CONN_STATUS_INPROGRESS;
1174        rc = gaim_proxy_connect(gc->account, host, port, oscar_bos_connect, gc);
1175        g_free(host);
1176        if (rc < 0) {
1177                gaim_connection_error(gc, _("Could Not Connect"));
1178                od->killme = TRUE;
1179                return 0;
1180        }
1181        aim_sendcookie(sess, bosconn, info->cookielen, info->cookie);
1182        gaim_input_remove(gc->inpa);
1183
1184        return 1;
1185}
1186
1187struct pieceofcrap {
1188        GaimConnection *gc;
1189        unsigned long offset;
1190        unsigned long len;
1191        char *modname;
1192        int fd;
1193        aim_conn_t *conn;
1194        unsigned int inpa;
1195};
1196
1197static void damn_you(gpointer data, gint source, GaimInputCondition c)
1198{
1199        struct pieceofcrap *pos = data;
1200        struct oscar_data *od = pos->gc->proto_data;
1201        char in = '\0';
1202        int x = 0;
1203        unsigned char m[17];
1204
1205        while (read(pos->fd, &in, 1) == 1) {
1206                if (in == '\n')
1207                        x++;
1208                else if (in != '\r')
1209                        x = 0;
1210                if (x == 2)
1211                        break;
1212                in = '\0';
1213        }
1214        if (in != '\n') {
1215                char buf[256];
1216                g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  You may want to use TOC until "
1217                        "this is fixed.  Check %s for updates."), GAIM_WEBSITE);
1218                gaim_notify_warning(pos->gc, NULL,
1219                                                        _("Gaim was Unable to get a valid AIM login hash."),
1220                                                        buf);
1221                gaim_input_remove(pos->inpa);
1222                close(pos->fd);
1223                g_free(pos);
1224                return;
1225        }
1226        read(pos->fd, m, 16);
1227        m[16] = '\0';
1228        gaim_debug(GAIM_DEBUG_MISC, "oscar", "Sending hash: ");
1229        for (x = 0; x < 16; x++)
1230                gaim_debug(GAIM_DEBUG_MISC, NULL, "%02hhx ", (unsigned char)m[x]);
1231
1232        gaim_debug(GAIM_DEBUG_MISC, NULL, "\n");
1233        gaim_input_remove(pos->inpa);
1234        close(pos->fd);
1235        aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
1236        g_free(pos);
1237}
1238
1239static void straight_to_hell(gpointer data, gint source, GaimInputCondition cond) {
1240        struct pieceofcrap *pos = data;
1241        gchar *buf;
1242
1243        pos->fd = source;
1244
1245        if (source < 0) {
1246                buf = g_strdup_printf(_("You may be disconnected shortly.  You may want to use TOC until "
1247                        "this is fixed.  Check %s for updates."), GAIM_WEBSITE);
1248                gaim_notify_warning(pos->gc, NULL,
1249                                                        _("Gaim was Unable to get a valid AIM login hash."),
1250                                                        buf);
1251                g_free(buf);
1252                if (pos->modname)
1253                        g_free(pos->modname);
1254                g_free(pos);
1255                return;
1256        }
1257
1258        buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
1259                        pos->offset, pos->len, pos->modname ? pos->modname : "");
1260        write(pos->fd, buf, strlen(buf));
1261        g_free(buf);
1262        if (pos->modname)
1263                g_free(pos->modname);
1264        pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos);
1265        return;
1266}
1267
1268/* size of icbmui.ocm, the largest module in AIM 3.5 */
1269#define AIM_MAX_FILE_SIZE 98304
1270
1271int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
1272        va_list ap;
1273        struct pieceofcrap *pos;
1274        fu32_t offset, len;
1275        char *modname;
1276
1277        va_start(ap, fr);
1278        offset = va_arg(ap, fu32_t);
1279        len = va_arg(ap, fu32_t);
1280        modname = va_arg(ap, char *);
1281        va_end(ap);
1282
1283        gaim_debug(GAIM_DEBUG_MISC, "oscar",
1284                           "offset: %u, len: %u, file: %s\n",
1285                           offset, len, (modname ? modname : "aim.exe"));
1286
1287        if (len == 0) {
1288                gaim_debug(GAIM_DEBUG_MISC, "oscar", "len is 0, hashing NULL\n");
1289                aim_sendmemblock(sess, fr->conn, offset, len, NULL,
1290                                AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
1291                return 1;
1292        }
1293        /* uncomment this when you're convinced it's right. remember, it's been wrong before.
1294        if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
1295                char *buf;
1296                int i = 8;
1297                if (modname)
1298                        i += strlen(modname);
1299                buf = g_malloc(i);
1300                i = 0;
1301                if (modname) {
1302                        memcpy(buf, modname, strlen(modname));
1303                        i += strlen(modname);
1304                }
1305                buf[i++] = offset & 0xff;
1306                buf[i++] = (offset >> 8) & 0xff;
1307                buf[i++] = (offset >> 16) & 0xff;
1308                buf[i++] = (offset >> 24) & 0xff;
1309                buf[i++] = len & 0xff;
1310                buf[i++] = (len >> 8) & 0xff;
1311                buf[i++] = (len >> 16) & 0xff;
1312                buf[i++] = (len >> 24) & 0xff;
1313                gaim_debug(GAIM_DEBUG_MISC, "oscar", "len + offset is invalid, "
1314                           "hashing request\n");
1315                aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
1316                g_free(buf);
1317                return 1;
1318        }
1319        */
1320
1321        pos = g_new0(struct pieceofcrap, 1);
1322        pos->gc = sess->aux_data;
1323        pos->conn = fr->conn;
1324
1325        pos->offset = offset;
1326        pos->len = len;
1327        pos->modname = modname ? g_strdup(modname) : NULL;
1328
1329        if (gaim_proxy_connect(pos->gc->account, "gaim.sourceforge.net", 80, straight_to_hell, pos) != 0) {
1330                char buf[256];
1331                if (pos->modname)
1332                        g_free(pos->modname);
1333                g_free(pos);
1334                g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  You may want to use TOC until "
1335                        "this is fixed.  Check %s for updates."), GAIM_WEBSITE);
1336                gaim_notify_warning(pos->gc, NULL,
1337                                                        _("Gaim was Unable to get a valid login hash."),
1338                                                        buf);
1339        }
1340
1341        return 1;
1342}
1343
1344static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {
1345        char *key;
1346        va_list ap;
1347        GaimConnection *gc = sess->aux_data;
1348        GaimAccount *account = gaim_connection_get_account(gc);
1349        GaimAccount *ac = gaim_connection_get_account(gc);
1350        struct oscar_data *od = gc->proto_data;
1351
1352        va_start(ap, fr);
1353        key = va_arg(ap, char *);
1354        va_end(ap);
1355
1356        if (od->icq) {
1357                struct client_info_s info = CLIENTINFO_ICQ_KNOWNGOOD;
1358                aim_send_login(sess, fr->conn, gaim_account_get_username(ac),
1359                                           gaim_account_get_password(account), &info, key);
1360        } else {
1361#if 0
1362                struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b};
1363#endif
1364                struct client_info_s info = CLIENTINFO_AIM_KNOWNGOOD;
1365                aim_send_login(sess, fr->conn, gaim_account_get_username(ac),
1366                                           gaim_account_get_password(account), &info, key);
1367        }
1368
1369        return 1;
1370}
1371
1372static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {
1373        GaimConnection *gc = sess->aux_data;
1374        struct chat_connection *chatcon;
1375        static int id = 1;
1376
1377        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, 0x0001, gaim_parse_genericerr, 0);
1378        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
1379        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
1380        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
1381        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
1382
1383        aim_clientready(sess, fr->conn);
1384
1385        chatcon = find_oscar_chat_by_conn(gc, fr->conn);
1386        chatcon->id = id;
1387        chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
1388
1389        return 1;
1390}
1391
1392static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
1393
1394        aim_conn_addhandler(sess, fr->conn, 0x000d, 0x0001, gaim_parse_genericerr, 0);
1395        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
1396
1397        aim_clientready(sess, fr->conn);
1398
1399        aim_chatnav_reqrights(sess, fr->conn);
1400
1401        return 1;
1402}
1403
1404static int conninitdone_email(aim_session_t *sess, aim_frame_t *fr, ...) {
1405
1406        aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0);
1407        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_EML, AIM_CB_EML_MAILSTATUS, gaim_email_parseupdate, 0);
1408
1409        aim_email_sendcookies(sess, fr->conn);
1410        aim_email_activate(sess, fr->conn);
1411        aim_clientready(sess, fr->conn);
1412
1413        return 1;
1414}
1415
1416static int conninitdone_icon(aim_session_t *sess, aim_frame_t *fr, ...) {
1417        GaimConnection *gc = sess->aux_data;
1418        struct oscar_data *od = gc->proto_data;
1419
1420        aim_conn_addhandler(sess, fr->conn, 0x0018, 0x0001, gaim_parse_genericerr, 0);
1421        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_ERROR, gaim_icon_error, 0);
1422        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ICO, AIM_CB_ICO_RESPONSE, gaim_icon_parseicon, 0);
1423
1424        aim_clientready(sess, fr->conn);
1425
1426        od->iconconnecting = FALSE;
1427
1428        if (od->icontimer)
1429                g_source_remove(od->icontimer);
1430        od->icontimer = g_timeout_add(100, gaim_icon_timerfunc, gc);
1431
1432        return 1;
1433}
1434
1435static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) {
1436        GaimConnection *gc = data;
1437        struct oscar_data *od;
1438        aim_session_t *sess;
1439        aim_conn_t *tstconn;
1440
1441        if (!g_list_find(gaim_connections_get_all(), gc)) {
1442                close(source);
1443                return;
1444        }
1445
1446        od = gc->proto_data;
1447        sess = od->sess;
1448        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
1449        tstconn->fd = source;
1450
1451        if (source < 0) {
1452                aim_conn_kill(sess, &tstconn);
1453                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1454                                   "unable to connect to chatnav server\n");
1455                return;
1456        }
1457
1458        aim_conn_completeconnect(sess, tstconn);
1459        od->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1460        gaim_debug(GAIM_DEBUG_INFO, "oscar", "chatnav: connected\n");
1461}
1462
1463static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond)
1464{
1465        GaimConnection *gc = data;
1466        struct oscar_data *od;
1467        aim_session_t *sess;
1468        aim_conn_t *tstconn;
1469
1470        if (!g_list_find(gaim_connections_get_all(), gc)) {
1471                close(source);
1472                return;
1473        }
1474
1475        od = gc->proto_data;
1476        sess = od->sess;
1477        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
1478        tstconn->fd = source;
1479
1480        if (source < 0) {
1481                aim_conn_kill(sess, &tstconn);
1482                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1483                                   "unable to connect to authorizer\n");
1484                return;
1485        }
1486
1487        aim_conn_completeconnect(sess, tstconn);
1488        od->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1489        gaim_debug(GAIM_DEBUG_INFO, "oscar", "admin: connected\n");
1490}
1491
1492static void oscar_chat_connect(gpointer data, gint source, GaimInputCondition cond)
1493{
1494        struct chat_connection *ccon = data;
1495        GaimConnection *gc = ccon->gc;
1496        struct oscar_data *od;
1497        aim_session_t *sess;
1498        aim_conn_t *tstconn;
1499
1500        if (!g_list_find(gaim_connections_get_all(), gc)) {
1501                close(source);
1502                g_free(ccon->show);
1503                g_free(ccon->name);
1504                g_free(ccon);
1505                return;
1506        }
1507
1508        od = gc->proto_data;
1509        sess = od->sess;
1510        tstconn = ccon->conn;
1511        tstconn->fd = source;
1512
1513        if (source < 0) {
1514                aim_conn_kill(sess, &tstconn);
1515                g_free(ccon->show);
1516                g_free(ccon->name);
1517                g_free(ccon);
1518                return;
1519        }
1520
1521        aim_conn_completeconnect(sess, ccon->conn);
1522        ccon->inpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1523        od->oscar_chats = g_slist_append(od->oscar_chats, ccon);
1524}
1525
1526static void oscar_email_connect(gpointer data, gint source, GaimInputCondition cond) {
1527        GaimConnection *gc = data;
1528        struct oscar_data *od;
1529        aim_session_t *sess;
1530        aim_conn_t *tstconn;
1531
1532        if (!g_list_find(gaim_connections_get_all(), gc)) {
1533                close(source);
1534                return;
1535        }
1536
1537        od = gc->proto_data;
1538        sess = od->sess;
1539        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_EMAIL);
1540        tstconn->fd = source;
1541
1542        if (source < 0) {
1543                aim_conn_kill(sess, &tstconn);
1544                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1545                                   "unable to connect to email server\n");
1546                return;
1547        }
1548
1549        aim_conn_completeconnect(sess, tstconn);
1550        od->emlpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1551        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1552                           "email: connected\n");
1553}
1554
1555static void oscar_icon_connect(gpointer data, gint source, GaimInputCondition cond) {
1556        GaimConnection *gc = data;
1557        struct oscar_data *od;
1558        aim_session_t *sess;
1559        aim_conn_t *tstconn;
1560
1561        if (!g_list_find(gaim_connections_get_all(), gc)) {
1562                close(source);
1563                return;
1564        }
1565
1566        od = gc->proto_data;
1567        sess = od->sess;
1568        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_ICON);
1569        tstconn->fd = source;
1570
1571        if (source < 0) {
1572                aim_conn_kill(sess, &tstconn);
1573                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1574                                   "unable to connect to icon server\n");
1575                return;
1576        }
1577
1578        aim_conn_completeconnect(sess, tstconn);
1579        od->icopa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, oscar_callback, tstconn);
1580        gaim_debug(GAIM_DEBUG_INFO, "oscar", "icon: connected\n");
1581}
1582
1583/* Hrmph. I don't know how to make this look better. --mid */
1584static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
1585        GaimConnection *gc = sess->aux_data;
1586        GaimAccount *account = gaim_connection_get_account(gc);
1587        aim_conn_t *tstconn;
1588        int i;
1589        char *host;
1590        int port;
1591        va_list ap;
1592        struct aim_redirect_data *redir;
1593
1594        port = gaim_account_get_int(account, "port", FAIM_LOGIN_PORT);
1595
1596        va_start(ap, fr);
1597        redir = va_arg(ap, struct aim_redirect_data *);
1598        va_end(ap);
1599
1600        for (i = 0; i < (int)strlen(redir->ip); i++) {
1601                if (redir->ip[i] == ':') {
1602                        port = atoi(&(redir->ip[i+1]));
1603                        break;
1604                }
1605        }
1606        host = g_strndup(redir->ip, i);
1607
1608        switch(redir->group) {
1609        case 0x7: /* Authorizer */
1610                gaim_debug(GAIM_DEBUG_INFO, "oscar",
1611                                   "Reconnecting with authorizor...\n");
1612                tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
1613                if (tstconn == NULL) {
1614                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1615                                           "unable to reconnect with authorizer\n");
1616                        g_free(host);
1617                        return 1;
1618                }
1619                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1620                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0);
1621
1622                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
1623                if (gaim_proxy_connect(account, host, port, oscar_auth_connect, gc) != 0) {
1624                        aim_conn_kill(sess, &tstconn);
1625                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1626                                           "unable to reconnect with authorizer\n");
1627                        g_free(host);
1628                        return 1;
1629                }
1630                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1631        break;
1632
1633        case 0xd: /* ChatNav */
1634                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL);
1635                if (tstconn == NULL) {
1636                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1637                                           "unable to connect to chatnav server\n");
1638                        g_free(host);
1639                        return 1;
1640                }
1641                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1642                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0);
1643
1644                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
1645                if (gaim_proxy_connect(account, host, port, oscar_chatnav_connect, gc) != 0) {
1646                        aim_conn_kill(sess, &tstconn);
1647                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1648                                           "unable to connect to chatnav server\n");
1649                        g_free(host);
1650                        return 1;
1651                }
1652                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1653        break;
1654
1655        case 0xe: { /* Chat */
1656                struct chat_connection *ccon;
1657
1658                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
1659                if (tstconn == NULL) {
1660                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1661                                           "unable to connect to chat server\n");
1662                        g_free(host);
1663                        return 1;
1664                }
1665
1666                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1667                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0);
1668
1669                ccon = g_new0(struct chat_connection, 1);
1670                ccon->conn = tstconn;
1671                ccon->gc = gc;
1672                ccon->fd = -1;
1673                ccon->name = g_strdup(redir->chat.room);
1674                ccon->exchange = redir->chat.exchange;
1675                ccon->instance = redir->chat.instance;
1676                ccon->show = extract_name(redir->chat.room);
1677
1678                ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
1679                if (gaim_proxy_connect(account, host, port, oscar_chat_connect, ccon) != 0) {
1680                        aim_conn_kill(sess, &tstconn);
1681                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1682                                           "unable to connect to chat server\n");
1683                        g_free(host);
1684                        g_free(ccon->show);
1685                        g_free(ccon->name);
1686                        g_free(ccon);
1687                        return 1;
1688                }
1689                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1690                gaim_debug(GAIM_DEBUG_INFO, "oscar",
1691                                   "Connected to chat room %s exchange %hu\n",
1692                                   ccon->name, ccon->exchange);
1693        } break;
1694
1695        case 0x0010: { /* icon */
1696                if (!(tstconn = aim_newconn(sess, AIM_CONN_TYPE_ICON, NULL))) {
1697                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1698                                           "unable to connect to icon server\n");
1699                        g_free(host);
1700                        return 1;
1701                }
1702                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1703                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_icon, 0);
1704
1705                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
1706                if (gaim_proxy_connect(account, host, port, oscar_icon_connect, gc) != 0) {
1707                        aim_conn_kill(sess, &tstconn);
1708                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1709                                           "unable to connect to icon server\n");
1710                        g_free(host);
1711                        return 1;
1712                }
1713                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1714        } break;
1715
1716        case 0x0018: { /* email */
1717                if (!(tstconn = aim_newconn(sess, AIM_CONN_TYPE_EMAIL, NULL))) {
1718                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1719                                           "unable to connect to email server\n");
1720                        g_free(host);
1721                        return 1;
1722                }
1723                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1724                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_email, 0);
1725
1726                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
1727                if (gaim_proxy_connect(account, host, port, oscar_email_connect, gc) != 0) {
1728                        aim_conn_kill(sess, &tstconn);
1729                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
1730                                           "unable to connect to email server\n");
1731                        g_free(host);
1732                        return 1;
1733                }
1734                aim_sendcookie(sess, tstconn, redir->cookielen, redir->cookie);
1735        } break;
1736
1737        default: /* huh? */
1738                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
1739                                   "got redirect for unknown service 0x%04hx\n",
1740                                   redir->group);
1741                break;
1742        }
1743
1744        g_free(host);
1745        return 1;
1746}
1747
1748static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {
1749        GaimConnection *gc = sess->aux_data;
1750        struct oscar_data *od = gc->proto_data;
1751        struct buddyinfo *bi;
1752        time_t time_idle = 0, signon = 0;
1753        int type = 0;
1754        int caps = 0;
1755        va_list ap;
1756        aim_userinfo_t *info;
1757
1758        va_start(ap, fr);
1759        info = va_arg(ap, aim_userinfo_t *);
1760        va_end(ap);
1761
1762        if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
1763                caps = info->capabilities;
1764        if (info->flags & AIM_FLAG_ACTIVEBUDDY)
1765                type |= UC_AB;
1766        if (caps & AIM_CAPS_HIPTOP)
1767                type |= UC_HIPTOP;
1768
1769        if (info->present & AIM_USERINFO_PRESENT_FLAGS) {
1770                if (info->flags & AIM_FLAG_UNCONFIRMED)
1771                        type |= UC_UNCONFIRMED;
1772                if (info->flags & AIM_FLAG_ADMINISTRATOR)
1773                        type |= UC_ADMIN;
1774                if (info->flags & AIM_FLAG_AOL)
1775                        type |= UC_AOL;
1776                if (info->flags & AIM_FLAG_FREE)
1777                        type |= UC_NORMAL;
1778                if (info->flags & AIM_FLAG_AWAY)
1779                        type |= UC_UNAVAILABLE;
1780                if (info->flags & AIM_FLAG_WIRELESS)
1781                        type |= UC_WIRELESS;
1782        }
1783        if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
1784                type = (info->icqinfo.status << 16);
1785                if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
1786                      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
1787                        type |= UC_UNAVAILABLE;
1788                }
1789        }
1790
1791        if (caps & AIM_CAPS_ICQ)
1792                caps ^= AIM_CAPS_ICQ;
1793
1794        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
1795                time(&time_idle);
1796                time_idle -= info->idletime*60;
1797        }
1798
1799        if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
1800                signon = info->onlinesince;
1801        else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
1802                signon = time(NULL) - info->sessionlen;
1803
1804        if (!aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), info->sn))
1805                gaim_connection_set_display_name(gc, info->sn);
1806
1807        bi = g_hash_table_lookup(od->buddyinfo, normalize(info->sn));
1808        if (!bi) {
1809                bi = g_new0(struct buddyinfo, 1);
1810                g_hash_table_insert(od->buddyinfo, g_strdup(normalize(info->sn)), bi);
1811        }
1812        bi->signon = info->onlinesince ? info->onlinesince : (info->sessionlen + time(NULL));
1813        if (caps)
1814                bi->caps = caps;
1815        bi->typingnot = FALSE;
1816        bi->ico_informed = FALSE;
1817        bi->ipaddr = info->icqinfo.ipaddr;
1818
1819        /* Available message stuff */
1820        free(bi->availmsg);
1821        if (info->availmsg)
1822                if (info->availmsg_encoding) {
1823                        gchar *enc = g_strdup_printf("charset=\"%s\"", info->availmsg_encoding);
1824                        bi->availmsg = oscar_encoding_to_utf8(enc, info->availmsg, info->availmsg_len);
1825                        g_free(enc);
1826                } else {
1827                        /* No explicit encoding means utf8.  Yay. */
1828                        bi->availmsg = g_strdup(info->availmsg);
1829                }
1830        else
1831                bi->availmsg = NULL;
1832
1833        /* Server stored icon stuff */
1834        if (info->iconcsumlen) {
1835                char *b16, *saved_b16;
1836                GaimBuddy *b;
1837
1838                free(bi->iconcsum);
1839                bi->iconcsum = malloc(info->iconcsumlen);
1840                memcpy(bi->iconcsum, info->iconcsum, info->iconcsumlen);
1841                bi->iconcsumlen = info->iconcsumlen;
1842                b16 = tobase16(bi->iconcsum, bi->iconcsumlen);
1843                b = gaim_find_buddy(gc->account, info->sn);
1844                saved_b16 = gaim_buddy_get_setting(b, "icon_checksum");
1845                if (!b16 || !saved_b16 || strcmp(b16, saved_b16)) {
1846                        GSList *cur = od->requesticon;
1847                        while (cur && aim_sncmp((char *)cur->data, info->sn))
1848                                cur = cur->next;
1849                        if (!cur) {
1850                                od->requesticon = g_slist_append(od->requesticon, strdup(normalize(info->sn)));
1851                                if (od->icontimer)
1852                                        g_source_remove(od->icontimer);
1853                                od->icontimer = g_timeout_add(500, gaim_icon_timerfunc, gc);
1854                        }
1855                }
1856                g_free(saved_b16);
1857                g_free(b16);
1858        }
1859
1860        serv_got_update(gc, info->sn, 1, (info->warnlevel/10.0) + 0.5, signon, time_idle, type);
1861
1862        return 1;
1863}
1864
1865static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
1866        GaimConnection *gc = sess->aux_data;
1867        struct oscar_data *od = gc->proto_data;
1868        va_list ap;
1869        aim_userinfo_t *info;
1870
1871        va_start(ap, fr);
1872        info = va_arg(ap, aim_userinfo_t *);
1873        va_end(ap);
1874
1875        serv_got_update(gc, info->sn, 0, 0, 0, 0, 0);
1876
1877        g_hash_table_remove(od->buddyinfo, normalize(info->sn));
1878
1879        return 1;
1880}
1881
1882static void cancel_direct_im(struct ask_direct *d) {
1883        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Freeing DirectIM prompts.\n");
1884
1885        g_free(d->sn);
1886        g_free(d);
1887}
1888
1889static void oscar_odc_callback(gpointer data, gint source, GaimInputCondition condition) {
1890        struct direct_im *dim = data;
1891        GaimConnection *gc = dim->gc;
1892        struct oscar_data *od = gc->proto_data;
1893        GaimConversation *cnv;
1894        char buf[256];
1895        struct sockaddr name;
1896        socklen_t name_len = 1;
1897       
1898        if (!g_list_find(gaim_connections_get_all(), gc)) {
1899                g_free(dim);
1900                return;
1901        }
1902
1903        if (source < 0) {
1904                g_free(dim);
1905                return;
1906        }
1907
1908        dim->conn->fd = source;
1909        aim_conn_completeconnect(od->sess, dim->conn);
1910        cnv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, dim->name);
1911
1912        /* This is the best way to see if we're connected or not */
1913        if (getpeername(source, &name, &name_len) == 0) {
1914                g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name);
1915                dim->connected = TRUE;
1916                gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
1917        }
1918        od->direct_ims = g_slist_append(od->direct_ims, dim);
1919       
1920        dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
1921}
1922
1923/* BBB */
1924/*
1925 * This is called after a remote AIM user has connected to us.  We
1926 * want to do some voodoo with the socket file descriptors, add a
1927 * callback or two, and then send the AIM_CB_OFT_PROMPT.
1928 */
1929static int oscar_sendfile_estblsh(aim_session_t *sess, aim_frame_t *fr, ...) {
1930        GaimConnection *gc = sess->aux_data;
1931        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
1932        GaimXfer *xfer;
1933        struct aim_oft_info *oft_info;
1934        va_list ap;
1935        aim_conn_t *conn, *listenerconn;
1936
1937        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1938                           "AAA - in oscar_sendfile_estblsh\n");
1939        va_start(ap, fr);
1940        conn = va_arg(ap, aim_conn_t *);
1941        listenerconn = va_arg(ap, aim_conn_t *);
1942        va_end(ap);
1943
1944        if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, listenerconn)))
1945                return 1;
1946
1947        if (!(oft_info = xfer->data))
1948                return 1;
1949
1950        /* Stop watching listener conn; watch transfer conn instead */
1951        gaim_input_remove(xfer->watcher);
1952        aim_conn_kill(sess, &listenerconn);
1953
1954        oft_info->conn = conn;
1955        xfer->fd = oft_info->conn->fd;
1956
1957        aim_conn_addhandler(sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK, oscar_sendfile_ack, 0);
1958        aim_conn_addhandler(sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE, oscar_sendfile_done, 0);
1959        xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
1960
1961        /* Inform the other user that we are connected and ready to transfer */
1962        aim_oft_sendheader(sess, AIM_CB_OFT_PROMPT, oft_info);
1963
1964        return 0;
1965}
1966
1967/*
1968 * This is the gaim callback passed to gaim_proxy_connect when connecting to another AIM
1969 * user in order to transfer a file.
1970 */
1971static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition) {
1972        GaimXfer *xfer;
1973        struct aim_oft_info *oft_info;
1974
1975        gaim_debug(GAIM_DEBUG_INFO, "oscar",
1976                           "AAA - in oscar_sendfile_connected\n");
1977        if (!(xfer = data))
1978                return;
1979        if (!(oft_info = xfer->data))
1980                return;
1981        if (source < 0)
1982                return;
1983
1984        xfer->fd = source;
1985        oft_info->conn->fd = source;
1986
1987        aim_conn_completeconnect(oft_info->sess, oft_info->conn);
1988        xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
1989
1990        /* Inform the other user that we are connected and ready to transfer */
1991        aim_im_sendch2_sendfile_accept(oft_info->sess, oft_info);
1992
1993        return;
1994}
1995
1996/*
1997 * This is called when a buddy sends us some file info.  This happens when they
1998 * are sending a file to you, and you have just established a connection to them.
1999 * You should send them the exact same info except use the real cookie.  We also
2000 * get like totally ready to like, receive the file, kay?
2001 */
2002static int oscar_sendfile_prompt(aim_session_t *sess, aim_frame_t *fr, ...) {
2003        GaimConnection *gc = sess->aux_data;
2004        struct oscar_data *od = gc->proto_data;
2005        GaimXfer *xfer;
2006        struct aim_oft_info *oft_info;
2007        va_list ap;
2008        aim_conn_t *conn;
2009        fu8_t *cookie;
2010        struct aim_fileheader_t *fh;
2011
2012        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2013                           "AAA - in oscar_sendfile_prompt\n");
2014        va_start(ap, fr);
2015        conn = va_arg(ap, aim_conn_t *);
2016        cookie = va_arg(ap, fu8_t *);
2017        fh = va_arg(ap, struct aim_fileheader_t *);
2018        va_end(ap);
2019
2020        if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn)))
2021                return 1;
2022
2023        if (!(oft_info = xfer->data))
2024                return 1;
2025
2026        /* We want to stop listening with a normal thingy */
2027        gaim_input_remove(xfer->watcher);
2028        xfer->watcher = 0;
2029
2030        /* They sent us some information about the file they're sending */
2031        memcpy(&oft_info->fh, fh, sizeof(*fh));
2032
2033        /* Fill in the cookie */
2034        memcpy(&oft_info->fh.bcookie, oft_info->cookie, 8);
2035
2036        /* XXX - convert the name from UTF-8 to UCS-2 if necessary, and pass the encoding to the call below */
2037        aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_ACK, oft_info);
2038        gaim_xfer_start(xfer, xfer->fd, NULL, 0);
2039
2040        return 0;
2041}
2042
2043/*
2044 * We are sending a file to someone else.  They have just acknowledged our
2045 * prompt, so we want to start sending data like there's no tomorrow.
2046 */
2047static int oscar_sendfile_ack(aim_session_t *sess, aim_frame_t *fr, ...) {
2048        GaimConnection *gc = sess->aux_data;
2049        struct oscar_data *od = gc->proto_data;
2050        GaimXfer *xfer;
2051        va_list ap;
2052        aim_conn_t *conn;
2053        fu8_t *cookie;
2054        struct aim_fileheader_t *fh;
2055
2056        gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_sendfile_ack\n");
2057        va_start(ap, fr);
2058        conn = va_arg(ap, aim_conn_t *);
2059        cookie = va_arg(ap, fu8_t *);
2060        fh = va_arg(ap, struct aim_fileheader_t *);
2061        va_end(ap);
2062
2063        if (!(xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie)))
2064                return 1;
2065
2066        /* We want to stop listening with a normal thingy */
2067        gaim_input_remove(xfer->watcher);
2068        xfer->watcher = 0;
2069
2070        gaim_xfer_start(xfer, xfer->fd, NULL, 0);
2071
2072        return 0;
2073}
2074
2075/*
2076 * We just sent a file to someone.  They said they got it and everything,
2077 * so we can close our direct connection and what not.
2078 */
2079static int oscar_sendfile_done(aim_session_t *sess, aim_frame_t *fr, ...) {
2080        GaimConnection *gc = sess->aux_data;
2081        struct oscar_data *od = gc->proto_data;
2082        GaimXfer *xfer;
2083        va_list ap;
2084        aim_conn_t *conn;
2085        fu8_t *cookie;
2086        struct aim_fileheader_t *fh;
2087
2088        gaim_debug(GAIM_DEBUG_INFO, "oscar", "AAA - in oscar_sendfile_done\n");
2089        va_start(ap, fr);
2090        conn = va_arg(ap, aim_conn_t *);
2091        cookie = va_arg(ap, fu8_t *);
2092        fh = va_arg(ap, struct aim_fileheader_t *);
2093        va_end(ap);
2094
2095        if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn)))
2096                return 1;
2097
2098        xfer->fd = conn->fd;
2099        gaim_xfer_end(xfer);
2100
2101        return 0;
2102}
2103
2104static void accept_direct_im(struct ask_direct *d) {
2105        GaimConnection *gc = d->gc;
2106        struct oscar_data *od;
2107        struct direct_im *dim;
2108        char *host; int port = 4443;
2109        int i, rc;
2110
2111        if (!g_list_find(gaim_connections_get_all(), gc)) {
2112                cancel_direct_im(d);
2113                return;
2114        }
2115
2116        od = (struct oscar_data *)gc->proto_data;
2117        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Accepted DirectIM.\n");
2118
2119        dim = find_direct_im(od, d->sn);
2120        if (dim) {
2121                cancel_direct_im(d); /* 40 */
2122                return;
2123        }
2124        dim = g_new0(struct direct_im, 1);
2125        dim->gc = d->gc;
2126        g_snprintf(dim->name, sizeof dim->name, "%s", d->sn);
2127
2128        dim->conn = aim_odc_connect(od->sess, d->sn, NULL, d->cookie);
2129        if (!dim->conn) {
2130                g_free(dim);
2131                cancel_direct_im(d);
2132                return;
2133        }
2134
2135        aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
2136                                gaim_odc_incoming, 0);
2137        aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
2138                                gaim_odc_typing, 0);
2139        aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER,
2140                                gaim_odc_update_ui, 0);
2141        for (i = 0; i < (int)strlen(d->ip); i++) {
2142                if (d->ip[i] == ':') {
2143                        port = atoi(&(d->ip[i+1]));
2144                        break;
2145                }
2146        }
2147        host = g_strndup(d->ip, i);
2148        dim->conn->status |= AIM_CONN_STATUS_INPROGRESS;
2149        rc = gaim_proxy_connect(gc->account, host, port, oscar_odc_callback, dim);
2150        g_free(host);
2151        if (rc < 0) {
2152                aim_conn_kill(od->sess, &dim->conn);
2153                g_free(dim);
2154                cancel_direct_im(d);
2155                return;
2156        }
2157
2158        cancel_direct_im(d);
2159
2160        return;
2161}
2162
2163static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
2164        GaimConnection *gc = sess->aux_data;
2165        struct oscar_data *od = gc->proto_data;
2166        char *tmp;
2167        GaimImFlags flags = 0;
2168        gsize convlen;
2169        GError *err = NULL;
2170        struct buddyinfo *bi;
2171        const char *iconfile;
2172
2173        bi = g_hash_table_lookup(od->buddyinfo, normalize(userinfo->sn));
2174        if (!bi) {
2175                bi = g_new0(struct buddyinfo, 1);
2176                g_hash_table_insert(od->buddyinfo, g_strdup(normalize(userinfo->sn)), bi);
2177        }
2178
2179        if (args->icbmflags & AIM_IMFLAGS_AWAY)
2180                flags |= GAIM_IM_AUTO_RESP;
2181
2182        if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT)
2183                bi->typingnot = TRUE;
2184        else
2185                bi->typingnot = FALSE;
2186
2187        if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) {
2188                gaim_debug(GAIM_DEBUG_MISC, "oscar",
2189                                   "%s has an icon\n", userinfo->sn);
2190                if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) {
2191                        bi->ico_need = TRUE;
2192                        bi->ico_len = args->iconlen;
2193                        bi->ico_csum = args->iconsum;
2194                        bi->ico_time = args->iconstamp;
2195                }
2196        }
2197
2198        if ((iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc))) && 
2199            (args->icbmflags & AIM_IMFLAGS_BUDDYREQ)) {
2200                FILE *file;
2201                struct stat st;
2202
2203                if (!stat(iconfile, &st)) {
2204                        char *buf = g_malloc(st.st_size);
2205                        file = fopen(iconfile, "rb");
2206                        if (file) {
2207                                int len = fread(buf, 1, st.st_size, file);
2208                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
2209                                                   "Sending buddy icon to %s (%d bytes, "
2210                                                   "%lu reported)\n",
2211                                                   userinfo->sn, len, st.st_size);
2212                                aim_im_sendch2_icon(sess, userinfo->sn, buf, st.st_size,
2213                                        st.st_mtime, aimutil_iconsum(buf, st.st_size));
2214                                fclose(file);
2215                        } else
2216                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2217                                                   "Can't open buddy icon file!\n");
2218                        g_free(buf);
2219                } else
2220                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2221                                           "Can't stat buddy icon file!\n");
2222        }
2223
2224        gaim_debug(GAIM_DEBUG_MISC, "oscar",
2225                           "Character set is %hu %hu\n", args->charset, args->charsubset);
2226        if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
2227                /* This message is marked as UNICODE, so we have to
2228                 * convert it to utf-8 before handing it to the gaim core.
2229                 * This conversion should *never* fail, if it does it
2230                 * means that either the incoming ICBM is corrupted or
2231                 * there is something we don't understand about it.
2232                 * For the record, AIM Unicode is big-endian UCS-2 */
2233
2234                gaim_debug(GAIM_DEBUG_INFO, "oscar", "Received UNICODE IM\n");
2235
2236                if (!args->msg || !args->msglen)
2237                        return 1;
2238
2239                tmp = g_convert(args->msg, args->msglen, "UTF-8", "UCS-2BE", NULL, &convlen, &err);
2240                if (err) {
2241                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2242                                           "Unicode IM conversion: %s\n", err->message);
2243                        tmp = strdup(_("(There was an error receiving this message)"));
2244                        g_error_free(err);
2245                }
2246        } else {
2247                /* This will get executed for both AIM_IMFLAGS_ISO_8859_1 and
2248                 * unflagged messages, which are ASCII.  That's OK because
2249                 * ASCII is a strict subset of ISO-8859-1; this should
2250                 * help with compatibility with old, broken versions of
2251                 * gaim (everything before 0.60) and other broken clients
2252                 * that will happily send ISO-8859-1 without marking it as
2253                 * such */
2254                if (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)
2255                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2256                                           "Received ISO-8859-1 IM\n");
2257
2258                if (!args->msg || !args->msglen)
2259                        return 1;
2260
2261                tmp = g_convert(args->msg, args->msglen, "UTF-8", "ISO-8859-1", NULL, &convlen, &err);
2262                if (err) {
2263                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2264                                           "ISO-8859-1 IM conversion: %s\n", err->message);
2265                        tmp = strdup(_("(There was an error receiving this message)"));
2266                        g_error_free(err);
2267                }
2268        }
2269
2270        /* strip_linefeed(tmp); */
2271        serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL));
2272        g_free(tmp);
2273
2274        return 1;
2275}
2276
2277static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
2278        GaimConnection *gc = sess->aux_data;
2279        struct oscar_data *od = gc->proto_data;
2280        const char *username = gaim_account_get_username(gaim_connection_get_account(gc));
2281
2282        if (!args)
2283                return 0;
2284
2285        gaim_debug(GAIM_DEBUG_MISC, "oscar",
2286                           "rendezvous with %s, status is %hu\n",
2287                           userinfo->sn, args->status);
2288
2289        if (args->reqclass & AIM_CAPS_CHAT) {
2290                char *name;
2291                GHashTable *components;
2292
2293                if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange || !args->msg)
2294                        return 1;
2295                components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
2296                                g_free);
2297                name = extract_name(args->info.chat.roominfo.name);
2298                g_hash_table_replace(components, g_strdup("room"), g_strdup(name ? name : args->info.chat.roominfo.name));
2299                g_hash_table_replace(components, g_strdup("exchange"), g_strdup_printf("%d", args->info.chat.roominfo.exchange));
2300                serv_got_chat_invite(gc,
2301                                     name ? name : args->info.chat.roominfo.name,
2302                                     userinfo->sn,
2303                                     args->msg,
2304                                     components);
2305                if (name)
2306                        g_free(name);
2307        } else if (args->reqclass & AIM_CAPS_SENDFILE) {
2308/* BBB */
2309                if (args->status == AIM_RENDEZVOUS_PROPOSE) {
2310                        /* Someone wants to send a file (or files) to us */
2311                        GaimXfer *xfer;
2312                        struct aim_oft_info *oft_info;
2313
2314                        if (!args->cookie || !args->port || !args->verifiedip || 
2315                            !args->info.sendfile.filename || !args->info.sendfile.totsize || 
2316                            !args->info.sendfile.totfiles || !args->reqclass) {
2317                                gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2318                                                   "%s tried to send you a file with incomplete "
2319                                                   "information.\n", userinfo->sn);
2320                                if (args->proxyip)
2321                                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2322                                                           "IP for a proxy server was given.  Gaim "
2323                                                           "does not support this yet.\n");
2324                                return 1;
2325                        }
2326
2327                        if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR) {
2328                                /* last char of the ft req is a star, they are sending us a
2329                                 * directory -- remove the star and trailing slash so we dont save
2330                                 * directories that look like 'dirname\*'  -- arl */
2331                                char *tmp = strrchr(args->info.sendfile.filename, '\\');
2332                                if (tmp && (tmp[1] == '*')) {
2333                                        tmp[0] = '\0';
2334                                }
2335                        }
2336
2337                        /* Build the file transfer handle */
2338                        xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, userinfo->sn);
2339                        xfer->remote_ip = g_strdup(args->verifiedip);
2340                        xfer->remote_port = args->port;
2341                        gaim_xfer_set_filename(xfer, args->info.sendfile.filename);
2342                        gaim_xfer_set_size(xfer, args->info.sendfile.totsize);
2343
2344                        /* Create the oscar-specific data */
2345                        oft_info = aim_oft_createinfo(od->sess, args->cookie, userinfo->sn, args->clientip, xfer->remote_port, 0, 0, NULL);
2346                        if (args->proxyip)
2347                                oft_info->proxyip = g_strdup(args->proxyip);
2348                        if (args->verifiedip)
2349                                oft_info->verifiedip = g_strdup(args->verifiedip);
2350                        xfer->data = oft_info;
2351
2352                         /* Setup our I/O op functions */
2353                        gaim_xfer_set_init_fnc(xfer, oscar_xfer_init);
2354                        gaim_xfer_set_start_fnc(xfer, oscar_xfer_start);
2355                        gaim_xfer_set_end_fnc(xfer, oscar_xfer_end);
2356                        gaim_xfer_set_cancel_send_fnc(xfer, oscar_xfer_cancel_send);
2357                        gaim_xfer_set_cancel_recv_fnc(xfer, oscar_xfer_cancel_recv);
2358                        gaim_xfer_set_ack_fnc(xfer, oscar_xfer_ack);
2359
2360                        /*
2361                         * XXX - Should do something with args->msg, args->encoding, and args->language
2362                         *       probably make it apply to all ch2 messages.
2363                         */
2364
2365                        /* Keep track of this transfer for later */
2366                        od->file_transfers = g_slist_append(od->file_transfers, xfer);
2367
2368                        /* Now perform the request */
2369                        gaim_xfer_request(xfer);
2370                } else if (args->status == AIM_RENDEZVOUS_CANCEL) {
2371                        /* The other user wants to cancel a file transfer */
2372                        GaimXfer *xfer;
2373                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2374                                           "AAA - File transfer canceled by remote user\n");
2375                        if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, args->cookie)))
2376                                gaim_xfer_cancel_remote(xfer);
2377                } else if (args->status == AIM_RENDEZVOUS_ACCEPT) {
2378                        /*
2379                         * This gets sent by the receiver of a file
2380                         * as they connect directly to us.  If we don't
2381                         * get this, then maybe a third party connected
2382                         * to us, and we shouldn't send them anything.
2383                         */
2384                } else {
2385                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2386                                           "unknown rendezvous status!\n");
2387                }
2388        } else if (args->reqclass & AIM_CAPS_GETFILE) {
2389        } else if (args->reqclass & AIM_CAPS_VOICE) {
2390        } else if (args->reqclass & AIM_CAPS_BUDDYICON) {
2391                gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc),
2392                                                                          userinfo->sn, args->info.icon.icon,
2393                                                                          args->info.icon.length);
2394        } else if (args->reqclass & AIM_CAPS_DIRECTIM) {
2395                struct ask_direct *d = g_new0(struct ask_direct, 1);
2396                char buf[256];
2397
2398                if (!args->verifiedip) {
2399                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2400                                           "directim kill blocked (%s)\n", userinfo->sn);
2401                        return 1;
2402                }
2403
2404                gaim_debug(GAIM_DEBUG_INFO, "oscar",
2405                                   "%s received direct im request from %s (%s)\n",
2406                                   username, userinfo->sn, args->verifiedip);
2407
2408                d->gc = gc;
2409                d->sn = g_strdup(userinfo->sn);
2410                strncpy(d->ip, args->verifiedip, sizeof(d->ip));
2411                memcpy(d->cookie, args->cookie, 8);
2412                g_snprintf(buf, sizeof buf, _("%s has just asked to directly connect to %s"), userinfo->sn, username);
2413
2414                gaim_request_action(gc, NULL, buf,
2415                                                        _("This requires a direct connection between "
2416                                                          "the two computers and is necessary for IM "
2417                                                          "Images.  Because your IP address will be "
2418                                                          "revealed, this may be considered a privacy "
2419                                                          "risk."), 0, d, 2,
2420                                                        _("Connect"), G_CALLBACK(accept_direct_im),
2421                                                        _("Cancel"), G_CALLBACK(cancel_direct_im));
2422        } else {
2423                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2424                                   "Unknown reqclass %hu\n", args->reqclass);
2425        }
2426
2427        return 1;
2428}
2429
2430/*
2431 * Authorization Functions
2432 * Most of these are callbacks from dialogs.  They're used by both
2433 * methods of authorization (SSI and old-school channel 4 ICBM)
2434 */
2435/* When you ask other people for authorization */
2436static void gaim_auth_request(struct name_data *data, char *msg) {
2437        GaimConnection *gc = data->gc;
2438
2439        if (g_list_find(gaim_connections_get_all(), gc)) {
2440                struct oscar_data *od = gc->proto_data;
2441                GaimBuddy *buddy = gaim_find_buddy(gc->account, data->name);
2442                GaimGroup *group = gaim_find_buddys_group(buddy);
2443                if (buddy && group) {
2444                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2445                                           "ssi: adding buddy %s to group %s\n",
2446                                           buddy->name, group->name);
2447                        aim_ssi_sendauthrequest(od->sess, data->name, msg ? msg : _("Please authorize me so I can add you to my buddy list."));
2448                        if (!aim_ssi_itemlist_finditem(od->sess->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY))
2449                                aim_ssi_addbuddy(od->sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 1);
2450                }
2451        }
2452}
2453
2454static void gaim_auth_request_msgprompt(struct name_data *data) {
2455        gaim_request_input(data->gc, NULL, _("Authorization Request Message:"),
2456                                           NULL, _("Please authorize me!"), TRUE, FALSE,
2457                                           _("OK"), G_CALLBACK(gaim_auth_request),
2458                                           _("Cancel"), G_CALLBACK(oscar_free_name_data),
2459                                           data);
2460}
2461
2462static void gaim_auth_dontrequest(struct name_data *data) {
2463        GaimConnection *gc = data->gc;
2464
2465        if (g_list_find(gaim_connections_get_all(), gc)) {
2466                /* struct oscar_data *od = gc->proto_data; */
2467                /* XXX - Take the buddy out of our buddy list */
2468        }
2469
2470        oscar_free_name_data(data);
2471}
2472
2473static void gaim_auth_sendrequest(GaimConnection *gc, const char *name) {
2474        struct name_data *data = g_new(struct name_data, 1);
2475        GaimBuddy *buddy;
2476        gchar *dialog_msg, *nombre;
2477
2478        buddy = gaim_find_buddy(gc->account, name);
2479        if (buddy && (gaim_get_buddy_alias_only(buddy)))
2480                nombre = g_strdup_printf("%s (%s)", name, gaim_get_buddy_alias_only(buddy));
2481        else
2482                nombre = NULL;
2483
2484        dialog_msg = g_strdup_printf(_("The user %s requires authorization before being added to a buddy list.  Do you want to send an authorization request?"), (nombre ? nombre : name));
2485        data->gc = gc;
2486        data->name = g_strdup(name);
2487        data->nick = NULL;
2488
2489        gaim_request_action(gc, NULL, _("Request Authorization"), dialog_msg,
2490                                                0, data, 2,
2491                                                _("Request Authorization"),
2492                                                G_CALLBACK(gaim_auth_request_msgprompt),
2493                                                _("Cancel"), G_CALLBACK(gaim_auth_dontrequest));
2494
2495        g_free(dialog_msg);
2496        g_free(nombre);
2497}
2498
2499/* When other people ask you for authorization */
2500static void gaim_auth_grant(struct name_data *data) {
2501        GaimConnection *gc = data->gc;
2502
2503        if (g_list_find(gaim_connections_get_all(), gc)) {
2504                struct oscar_data *od = gc->proto_data;
2505#ifdef NOSSI
2506                GaimBuddy *buddy;
2507                gchar message;
2508                message = 0;
2509                buddy = gaim_find_buddy(gc->account, data->name);
2510                aim_im_sendch4(od->sess, data->name, AIM_ICQMSG_AUTHGRANTED, &message);
2511                show_got_added(gc, NULL, data->name, (buddy ? gaim_get_buddy_alias_only(buddy) : NULL), NULL);
2512#else
2513                aim_ssi_sendauthreply(od->sess, data->name, 0x01, NULL);
2514#endif
2515        }
2516
2517        oscar_free_name_data(data);
2518}
2519
2520/* When other people ask you for authorization */
2521static void gaim_auth_dontgrant(struct name_data *data, char *msg) {
2522        GaimConnection *gc = data->gc;
2523
2524        if (g_list_find(gaim_connections_get_all(), gc)) {
2525                struct oscar_data *od = gc->proto_data;
2526#ifdef NOSSI
2527                aim_im_sendch4(od->sess, data->name, AIM_ICQMSG_AUTHDENIED, msg ? msg : _("No reason given."));
2528#else
2529                aim_ssi_sendauthreply(od->sess, data->name, 0x00, msg ? msg : _("No reason given."));
2530#endif
2531        }
2532}
2533
2534static void gaim_auth_dontgrant_msgprompt(struct name_data *data) {
2535        gaim_request_input(data->gc, NULL, _("Authorization Denied Message:"),
2536                                           NULL, _("No reason given."), TRUE, FALSE,
2537                                           _("OK"), G_CALLBACK(gaim_auth_dontgrant),
2538                                           _("Cancel"), G_CALLBACK(oscar_free_name_data),
2539                                           data);
2540}
2541
2542/* When someone sends you contacts  */
2543static void gaim_icq_contactadd(struct name_data *data) {
2544        GaimConnection *gc = data->gc;
2545
2546        if (g_list_find(gaim_connections_get_all(), gc)) {
2547                show_add_buddy(gc, data->name, NULL, data->nick);
2548        }
2549
2550        oscar_free_name_data(data);
2551}
2552
2553static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args, time_t t) {
2554        GaimConnection *gc = sess->aux_data;
2555        gchar **msg1, **msg2;
2556        GError *err = NULL;
2557        int i, numtoks;
2558
2559        if (!args->type || !args->msg || !args->uin)
2560                return 1;
2561
2562        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2563                           "Received a channel 4 message of type 0x%02hhx.\n", args->type);
2564
2565        /* Split up the message at the delimeter character, then convert each string to UTF-8 */
2566        msg1 = g_strsplit(args->msg, "\376", 0);
2567        for (numtoks=0; msg1[numtoks]; numtoks++);
2568        msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *));
2569        for (i=0; msg1[i]; i++) {
2570                strip_linefeed(msg1[i]);
2571                msg2[i] = g_convert(msg1[i], strlen(msg1[i]), "UTF-8", "ISO-8859-1", NULL, NULL, &err);
2572                if (err) {
2573                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2574                                           "Error converting a string from ISO-8859-1 to "
2575                                           "UTF-8 in oscar ICBM channel 4 parsing\n");
2576                        g_error_free(err);
2577                }
2578        }
2579        msg2[i] = NULL;
2580
2581        switch (args->type) {
2582                case 0x01: { /* MacICQ message or basic offline message */
2583                        if (i >= 1) {
2584                                gchar *uin = g_strdup_printf("%u", args->uin);
2585                                if (t) { /* This is an offline message */
2586                                        /* I think this timestamp is in UTC, or something */
2587                                        serv_got_im(gc, uin, msg2[0], 0, t);
2588                                } else { /* This is a message from MacICQ/Miranda */
2589                                        serv_got_im(gc, uin, msg2[0], 0, time(NULL));
2590                                }
2591                                g_free(uin);
2592                        }
2593                } break;
2594
2595                case 0x04: { /* Someone sent you a URL */
2596                        if (i >= 2) {
2597                                gchar *uin = g_strdup_printf("%u", args->uin);
2598                                gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>", msg2[1], msg2[0]);
2599                                serv_got_im(gc, uin, message, 0, time(NULL));
2600                                g_free(uin);
2601                                g_free(message);
2602                        }
2603                } break;
2604
2605                case 0x06: { /* Someone requested authorization */
2606                        if (i >= 6) {
2607                                struct name_data *data = g_new(struct name_data, 1);
2608                                gchar *dialog_msg = g_strdup_printf(_("The user %u wants to add you to their buddy list for the following reason:\n%s"), args->uin, msg2[5] ? msg2[5] : _("No reason given."));
2609                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
2610                                                   "Received an authorization request from UIN %u\n",
2611                                                   args->uin);
2612                                data->gc = gc;
2613                                data->name = g_strdup_printf("%u", args->uin);
2614                                data->nick = NULL;
2615
2616                                gaim_request_action(gc, NULL, _("Authorization Request"),
2617                                                                        dialog_msg, 0, data, 2,
2618                                                                        _("Authorize"),
2619                                                                        G_CALLBACK(gaim_auth_grant),
2620                                                                        _("Deny"),
2621                                                                        G_CALLBACK(gaim_auth_dontgrant_msgprompt));
2622                                g_free(dialog_msg);
2623                        }
2624                } break;
2625
2626                case 0x07: { /* Someone has denied you authorization */
2627                        if (i >= 1) {
2628                                gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your contact list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given."));
2629                                gaim_notify_info(gc, NULL, _("ICQ authorization denied."),
2630                                                                 dialog_msg);
2631                                g_free(dialog_msg);
2632                        }
2633                } break;
2634
2635                case 0x08: { /* Someone has granted you authorization */
2636                        gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your contact list."), args->uin);
2637                        gaim_notify_info(gc, NULL, "ICQ authorization accepted.",
2638                                                         dialog_msg);
2639                        g_free(dialog_msg);
2640                } break;
2641
2642                case 0x09: { /* Message from the Godly ICQ server itself, I think */
2643                        if (i >= 5) {
2644                                gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
2645                                gaim_notify_info(gc, NULL, "ICQ Server Message", dialog_msg);
2646                                g_free(dialog_msg);
2647                        }
2648                } break;
2649
2650                case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
2651                        if (i >= 6) {
2652                                gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
2653                                gaim_notify_info(gc, NULL, "ICQ Page", dialog_msg);
2654                                g_free(dialog_msg);
2655                        }
2656                } break;
2657
2658                case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
2659                        if (i >= 6) {
2660                                gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]);
2661                                gaim_notify_info(gc, NULL, "ICQ Email", dialog_msg);
2662                                g_free(dialog_msg);
2663                        }
2664                } break;
2665
2666                case 0x12: {
2667                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
2668                        /* Someone added you to their contact list? */
2669                } break;
2670
2671                case 0x13: { /* Someone has sent you some ICQ contacts */
2672                        int i, num;
2673                        gchar **text;
2674                        text = g_strsplit(args->msg, "\376", 0);
2675                        if (text) {
2676                                num = 0;
2677                                for (i=0; i<strlen(text[0]); i++)
2678                                        num = num*10 + text[0][i]-48;
2679                                for (i=0; i<num; i++) {
2680                                        struct name_data *data = g_new(struct name_data, 1);
2681                                        gchar *message = g_strdup_printf(_("ICQ user %u has sent you a contact: %s (%s)"), args->uin, text[i*2+2], text[i*2+1]);
2682                                        data->gc = gc;
2683                                        data->name = g_strdup(text[i*2+1]);
2684                                        data->nick = g_strdup(text[i*2+2]);
2685
2686                                        gaim_request_action(gc, NULL, message,
2687                                                                                _("Do you want to add this contact "
2688                                                                                  "to your Buddy List?"),
2689                                                                                0, data, 2,
2690                                                                                _("Add"), G_CALLBACK(gaim_icq_contactadd),
2691                                                                                _("Decline"), G_CALLBACK(oscar_free_name_data));
2692                                        g_free(message);
2693                                }
2694                                g_strfreev(text);
2695                        }
2696                } break;
2697
2698                case 0x1a: { /* Someone has sent you a greeting card or requested contacts? */
2699                        /* This is boring and silly. */
2700                } break;
2701
2702                default: {
2703                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2704                                           "Received a channel 4 message of unknown type "
2705                                           "(type 0x%02hhx).\n", args->type);
2706                } break;
2707        }
2708
2709        g_strfreev(msg1);
2710        g_strfreev(msg2);
2711
2712        return 1;
2713}
2714
2715static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
2716        fu16_t channel;
2717        int ret = 0;
2718        aim_userinfo_t *userinfo;
2719        va_list ap;
2720
2721        va_start(ap, fr);
2722        channel = (fu16_t)va_arg(ap, unsigned int);
2723        userinfo = va_arg(ap, aim_userinfo_t *);
2724
2725        switch (channel) {
2726                case 1: { /* standard message */
2727                        struct aim_incomingim_ch1_args *args;
2728                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
2729                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
2730                } break;
2731
2732                case 2: { /* rendevous */
2733                        struct aim_incomingim_ch2_args *args;
2734                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
2735                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
2736                } break;
2737
2738                case 4: { /* ICQ */
2739                        struct aim_incomingim_ch4_args *args;
2740                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
2741                        ret = incomingim_chan4(sess, fr->conn, userinfo, args, 0);
2742                } break;
2743
2744                default: {
2745                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2746                                           "ICBM received on unsupported channel (channel "
2747                                           "0x%04hx).", channel);
2748                } break;
2749        }
2750
2751        va_end(ap);
2752
2753        return ret;
2754}
2755
2756static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
2757        char *buf;
2758        va_list ap;
2759        fu16_t chan, nummissed, reason;
2760        aim_userinfo_t *userinfo;
2761
2762        va_start(ap, fr);
2763        chan = (fu16_t)va_arg(ap, unsigned int);
2764        userinfo = va_arg(ap, aim_userinfo_t *);
2765        nummissed = (fu16_t)va_arg(ap, unsigned int);
2766        reason = (fu16_t)va_arg(ap, unsigned int);
2767        va_end(ap);
2768
2769        switch(reason) {
2770                case 0: /* Invalid (0) */
2771                        buf = g_strdup_printf(
2772                                   ngettext(
2773                                   "You missed %hu message from %s because it was invalid.",
2774                                   "You missed %hu messages from %s because they were invalid.",
2775                                   nummissed),
2776                                   nummissed,
2777                                   userinfo->sn);
2778                        break;
2779                case 1: /* Message too large */
2780                        buf = g_strdup_printf(
2781                                   ngettext(
2782                                   "You missed %hu message from %s because it was too large.",
2783                                   "You missed %hu messages from %s because they were too large.",
2784                                   nummissed),
2785                                   nummissed,
2786                                   userinfo->sn);
2787                        break;
2788                case 2: /* Rate exceeded */
2789                        buf = g_strdup_printf(
2790                                   ngettext(
2791                                   "You missed %hu message from %s because the rate limit has been exceeded.",
2792                                   "You missed %hu messages from %s because the rate limit has been exceeded.",
2793                                   nummissed),
2794                                   nummissed,
2795                                   userinfo->sn);
2796                        break;
2797                case 3: /* Evil Sender */
2798                        buf = g_strdup_printf(
2799                                   ngettext(
2800                                   "You missed %hu message from %s because he/she was too evil.",
2801                                   "You missed %hu messages from %s because he/she was too evil.",
2802                                   nummissed),
2803                                   nummissed,
2804                                   userinfo->sn);
2805                        break;
2806                case 4: /* Evil Receiver */
2807                        buf = g_strdup_printf(
2808                                   ngettext(
2809                                   "You missed %hu message from %s because you are too evil.",
2810                                   "You missed %hu messages from %s because you are too evil.",
2811                                   nummissed),
2812                                   nummissed,
2813                                   userinfo->sn);
2814                        break;
2815                default:
2816                        buf = g_strdup_printf(
2817                                   ngettext(
2818                                   "You missed %hu message from %s for an unknown reason.",
2819                                   "You missed %hu messages from %s for an unknown reason.",
2820                                   nummissed),
2821                                   nummissed,
2822                                   userinfo->sn);
2823                        break;
2824        }
2825        gaim_notify_error(sess->aux_data, NULL, buf, NULL);
2826        g_free(buf);
2827
2828        return 1;
2829}
2830
2831static char *gaim_icq_status(int state) {
2832        /* Make a cute little string that shows the status of the dude or dudet */
2833        if (state & AIM_ICQ_STATE_CHAT)
2834                return g_strdup_printf(_("Free For Chat"));
2835        else if (state & AIM_ICQ_STATE_DND)
2836                return g_strdup_printf(_("Do Not Disturb"));
2837        else if (state & AIM_ICQ_STATE_OUT)
2838                return g_strdup_printf(_("Not Available"));
2839        else if (state & AIM_ICQ_STATE_BUSY)
2840                return g_strdup_printf(_("Occupied"));
2841        else if (state & AIM_ICQ_STATE_AWAY)
2842                return g_strdup_printf(_("Away"));
2843        else if (state & AIM_ICQ_STATE_WEBAWARE)
2844                return g_strdup_printf(_("Web Aware"));
2845        else if (state & AIM_ICQ_STATE_INVISIBLE)
2846                return g_strdup_printf(_("Invisible"));
2847        else
2848                return g_strdup_printf(_("Online"));
2849}
2850
2851static int gaim_parse_clientauto_ch2(aim_session_t *sess, const char *who, fu16_t reason, const char *cookie) {
2852        GaimConnection *gc = sess->aux_data;
2853        struct oscar_data *od = gc->proto_data;
2854
2855/* BBB */
2856        switch (reason) {
2857                case 3: { /* Decline sendfile. */
2858                        GaimXfer *xfer;
2859                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
2860                                           "AAA - Other user declined file transfer\n");
2861                        if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, cookie)))
2862                                gaim_xfer_cancel_remote(xfer);
2863                } break;
2864
2865                default: {
2866                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2867                                           "Received an unknown rendezvous client auto-response "
2868                                           "from %s.  Type 0x%04hx\n", who, reason);
2869                }
2870
2871        }
2872
2873        return 0;
2874}
2875
2876static int gaim_parse_clientauto_ch4(aim_session_t *sess, char *who, fu16_t reason, fu32_t state, char *msg) {
2877        GaimConnection *gc = sess->aux_data;
2878
2879        switch(reason) {
2880                case 0x0003: { /* Reply from an ICQ status message request */
2881                        char *status_msg = gaim_icq_status(state);
2882                        char *dialog_msg, **splitmsg;
2883                        struct oscar_data *od = gc->proto_data;
2884                        GSList *l = od->evilhack;
2885                        gboolean evilhack = FALSE;
2886
2887                        /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
2888                        splitmsg = g_strsplit(msg, "\r\n", 0);
2889
2890                        /* If who is in od->evilhack, then we're just getting the away message, otherwise this
2891                         * will just get appended to the info box (which is already showing). */
2892                        while (l) {
2893                                char *x = l->data;
2894                                if (!strcmp(x, normalize(who))) {
2895                                        evilhack = TRUE;
2896                                        g_free(x);
2897                                        od->evilhack = g_slist_remove(od->evilhack, x);
2898                                        break;
2899                                }
2900                                l = l->next;
2901                        }
2902
2903                        if (evilhack)
2904                                dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<HR>%s"), who, status_msg, g_strjoinv("<BR>", splitmsg));
2905                        else
2906                                dialog_msg = g_strdup_printf(_("<B>Status:</B> %s<HR>%s"), status_msg, g_strjoinv("<BR>", splitmsg));
2907                        g_show_info_text(gc, who, 2, dialog_msg, NULL);
2908
2909                        g_free(status_msg);
2910                        g_free(dialog_msg);
2911                        g_strfreev(splitmsg);
2912                } break;
2913
2914                default: {
2915                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
2916                                           "Received an unknown client auto-response from %s.  "
2917                                           "Type 0x%04hx\n", who, reason);
2918                } break;
2919        } /* end of switch */
2920
2921        return 0;
2922}
2923
2924static int gaim_parse_clientauto(aim_session_t *sess, aim_frame_t *fr, ...) {
2925        va_list ap;
2926        fu16_t chan, reason;
2927        char *who;
2928
2929        va_start(ap, fr);
2930        chan = (fu16_t)va_arg(ap, unsigned int);
2931        who = va_arg(ap, char *);
2932        reason = (fu16_t)va_arg(ap, unsigned int);
2933
2934        if (chan == 0x0002) { /* File transfer declined */
2935                char *cookie = va_arg(ap, char *);
2936                return gaim_parse_clientauto_ch2(sess, who, reason, cookie);
2937        } else if (chan == 0x0004) { /* ICQ message */
2938                fu32_t state = 0;
2939                char *msg = NULL;
2940                if (reason == 0x0003) {
2941                        state = va_arg(ap, fu32_t);
2942                        msg = va_arg(ap, char *);
2943                }
2944                return gaim_parse_clientauto_ch4(sess, who, reason, state, msg);
2945        }
2946
2947        va_end(ap);
2948
2949        return 1;
2950}
2951
2952static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
2953        va_list ap;
2954        fu16_t reason;
2955        char *m;
2956
2957        va_start(ap, fr);
2958        reason = (fu16_t) va_arg(ap, unsigned int);
2959        va_end(ap);
2960
2961        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2962                           "snac threw error (reason 0x%04hx: %s)\n", reason,
2963                           (reason < msgerrreasonlen) ? msgerrreason[reason] : "unknown");
2964
2965        m = g_strdup_printf(_("SNAC threw error: %s\n"),
2966                        reason < msgerrreasonlen ? _(msgerrreason[reason]) : _("Unknown error"));
2967        gaim_notify_error(sess->aux_data, NULL, m, NULL);
2968        g_free(m);
2969
2970        return 1;
2971}
2972
2973static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
2974#if 0
2975        GaimConnection *gc = sess->aux_data;
2976        struct oscar_data *od = gc->proto_data;
2977        GaimXfer *xfer;
2978#endif
2979        va_list ap;
2980        fu16_t reason;
2981        char *data, *buf;
2982
2983        va_start(ap, fr);
2984        reason = (fu16_t)va_arg(ap, unsigned int);
2985        data = va_arg(ap, char *);
2986        va_end(ap);
2987
2988        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
2989                           "Message error with data %s and reason %hu\n", data, reason);
2990
2991/* BBB */
2992#if 0
2993        /* If this was a file transfer request, data is a cookie */
2994        if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, data))) {
2995                gaim_xfer_cancel_remote(xfer);
2996                return 1;
2997        }
2998#endif
2999
3000        /* Data is assumed to be the destination sn */
3001        buf = g_strdup_printf(_("Your message to %s did not get sent:"), data);
3002        gaim_notify_error(sess->aux_data, NULL, buf,
3003                                          (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("No reason given."));
3004        g_free(buf);
3005
3006        return 1;
3007}
3008
3009static int gaim_parse_mtn(aim_session_t *sess, aim_frame_t *fr, ...) {
3010        GaimConnection *gc = sess->aux_data;
3011        va_list ap;
3012        fu16_t type1, type2;
3013        char *sn;
3014
3015        va_start(ap, fr);
3016        type1 = (fu16_t) va_arg(ap, unsigned int);
3017        sn = va_arg(ap, char *);
3018        type2 = (fu16_t) va_arg(ap, unsigned int);
3019        va_end(ap);
3020
3021        switch (type2) {
3022                case 0x0000: { /* Text has been cleared */
3023                        serv_got_typing_stopped(gc, sn);
3024                } break;
3025
3026                case 0x0001: { /* Paused typing */
3027                        serv_got_typing(gc, sn, 0, GAIM_TYPED);
3028                } break;
3029
3030                case 0x0002: { /* Typing */
3031                        serv_got_typing(gc, sn, 0, GAIM_TYPING);
3032                } break;
3033
3034                default: {
3035                        gaim_debug(GAIM_DEBUG_ERROR, "oscar", "Received unknown typing notification message from %s.  Type1 is 0x%04x and type2 is 0x%04hx.\n", sn, type1, type2);
3036                } break;
3037        }
3038
3039        return 1;
3040}
3041
3042static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
3043        char *buf;
3044        va_list ap;
3045        fu16_t reason;
3046        char *destn;
3047
3048        va_start(ap, fr);
3049        reason = (fu16_t) va_arg(ap, unsigned int);
3050        destn = va_arg(ap, char *);
3051        va_end(ap);
3052
3053        buf = g_strdup_printf(_("User information for %s unavailable:"), destn);
3054        gaim_notify_error(sess->aux_data, NULL, buf,
3055                                          (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("No reason given."));
3056        g_free(buf);
3057
3058        return 1;
3059}
3060
3061#if 0
3062static char *images(int flags) {
3063        static char buf[1024];
3064        g_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
3065                        (flags & AIM_FLAG_ACTIVEBUDDY) ? "<IMG SRC=\"ab_icon.gif\">" : "",
3066                        (flags & AIM_FLAG_UNCONFIRMED) ? "<IMG SRC=\"dt_icon.gif\">" : "",
3067                        (flags & AIM_FLAG_AOL) ? "<IMG SRC=\"aol_icon.gif\">" : "",
3068                        (flags & AIM_FLAG_ICQ) ? "<IMG SRC=\"icq_icon.gif\">" : "",
3069                        (flags & AIM_FLAG_ADMINISTRATOR) ? "<IMG SRC=\"admin_icon.gif\">" : "",
3070                        (flags & AIM_FLAG_FREE) ? "<IMG SRC=\"free_icon.gif\">" : "",
3071                        (flags & AIM_FLAG_WIRELESS) ? "<IMG SRC=\"wireless_icon.gif\">" : "");
3072        return buf;
3073}
3074#endif
3075
3076static char *caps_string(guint caps)
3077{
3078        static char buf[512], *tmp;
3079        int count = 0, i = 0;
3080        guint bit = 1;
3081
3082        if (!caps) {
3083                return NULL;
3084        } else while (bit <= AIM_CAPS_LAST) {
3085                if (bit & caps) {
3086                        switch (bit) {
3087                        case AIM_CAPS_BUDDYICON:
3088                                tmp = _("Buddy Icon");
3089                                break;
3090                        case AIM_CAPS_VOICE:
3091                                tmp = _("Voice");
3092                                break;
3093                        case AIM_CAPS_DIRECTIM:
3094                                tmp = _("Direct IM");
3095                                break;
3096                        case AIM_CAPS_CHAT:
3097                                tmp = _("Chat");
3098                                break;
3099                        case AIM_CAPS_GETFILE:
3100                                tmp = _("Get File");
3101                                break;
3102                        case AIM_CAPS_SENDFILE:
3103                                tmp = _("Send File");
3104                                break;
3105                        case AIM_CAPS_GAMES:
3106                        case AIM_CAPS_GAMES2:
3107                                tmp = _("Games");
3108                                break;
3109                        case AIM_CAPS_SAVESTOCKS:
3110                                tmp = _("Add-Ins");
3111                                break;
3112                        case AIM_CAPS_SENDBUDDYLIST:
3113                                tmp = _("Send Buddy List");
3114                                break;
3115                        case AIM_CAPS_ICQ:
3116                                tmp = _("EveryBuddy Bug");
3117                                break;
3118                        case AIM_CAPS_APINFO:
3119                                tmp = _("AP User");
3120                                break;
3121                        case AIM_CAPS_ICQRTF:
3122                                tmp = _("ICQ RTF");
3123                                break;
3124                        case AIM_CAPS_EMPTY:
3125                                tmp = _("Nihilist");
3126                                break;
3127                        case AIM_CAPS_ICQSERVERRELAY:
3128                                tmp = _("ICQ Server Relay");
3129                                break;
3130                        case AIM_CAPS_ICQUTF8OLD:
3131                                tmp = _("Old ICQ UTF8");
3132                                break;
3133                        case AIM_CAPS_TRILLIANCRYPT:
3134                                tmp = _("Trillian Encryption");
3135                                break;
3136                        case AIM_CAPS_ICQUTF8:
3137                                tmp = _("ICQ UTF8");
3138                                break;
3139                        case AIM_CAPS_HIPTOP:
3140                                tmp = _("Hiptop");
3141                                break;
3142                        case AIM_CAPS_SECUREIM:
3143                                tmp = _("Secure IM");
3144                                break;
3145                        default:
3146                                tmp = NULL;
3147                                break;
3148                        }
3149                        if (tmp)
3150                                i += g_snprintf(buf + i, sizeof(buf) - i, "%s%s", (count ? ", " : ""),
3151                                                tmp);
3152                        count++;
3153                }
3154                bit <<= 1;
3155        }
3156        return buf; 
3157}
3158
3159static int gaim_parse_user_info(aim_session_t *sess, aim_frame_t *fr, ...) {
3160        GaimConnection *gc = sess->aux_data;
3161        struct oscar_data *od = gc->proto_data;
3162        gchar *header;
3163        GSList *l = od->evilhack;
3164        gboolean evilhack = FALSE;
3165        gchar *membersince = NULL, *onlinesince = NULL, *idle = NULL;
3166        va_list ap;
3167        aim_userinfo_t *info;
3168        fu16_t infotype;
3169        char *text_enc = NULL, *text = NULL, *utf8 = NULL;
3170        int text_len;
3171        const char *username = gaim_account_get_username(gaim_connection_get_account(gc));
3172
3173        va_start(ap, fr);
3174        info = va_arg(ap, aim_userinfo_t *);
3175        infotype = (fu16_t) va_arg(ap, unsigned int);
3176        text_enc = va_arg(ap, char *);
3177        text = va_arg(ap, char *);
3178        text_len = va_arg(ap, int);
3179        va_end(ap);
3180
3181        if (text_len > 0) {
3182                if (!(utf8 = oscar_encoding_to_utf8(text_enc, text, text_len))) {
3183                        utf8 = g_strdup(_("<i>Unable to display information because it was sent in an unknown encoding.</i>"));
3184                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
3185                                           "Encountered an unknown encoding while parsing userinfo\n");
3186                }
3187        }
3188
3189        if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) {
3190                onlinesince = g_strdup_printf(_("Online Since : <b>%s</b><br>\n"),
3191                                        asctime(localtime((time_t *)&info->onlinesince)));
3192        }
3193
3194        if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
3195                membersince = g_strdup_printf(_("Member Since : <b>%s</b><br>\n"),
3196                                        asctime(localtime((time_t *)&info->membersince)));
3197        }
3198
3199        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
3200                gchar *itime = sec_to_text(info->idletime*60);
3201                idle = g_strdup_printf(_("Idle : <b>%s</b>"), itime);
3202                g_free(itime);
3203        } else
3204                idle = g_strdup(_("Idle: <b>Active</b>"));
3205
3206        header = g_strdup_printf(_("Username : <b>%s</b>  %s <br>\n"
3207                        "Warning Level : <b>%d %%</b><br>\n"
3208                        "%s"
3209                        "%s"
3210                        "%s\n"
3211                        "<hr>\n"),
3212                        info->sn,
3213                        /* images(info->flags), */
3214                        "",
3215                        (int)((info->warnlevel/10.0) + 0.5),
3216                        onlinesince ? onlinesince : "",
3217                        membersince ? membersince : "",
3218                        idle ? idle : "");
3219
3220        g_free(onlinesince);
3221        g_free(membersince);
3222        g_free(idle);
3223
3224        while (l) {
3225                char *x = l->data;
3226                if (!strcmp(x, normalize(info->sn))) {
3227                        evilhack = TRUE;
3228                        g_free(x);
3229                        od->evilhack = g_slist_remove(od->evilhack, x);
3230                        break;
3231                }
3232                l = l->next;
3233        }
3234
3235        if (infotype == AIM_GETINFO_AWAYMESSAGE) {
3236                if (evilhack) {
3237                        g_show_info_text(gc, info->sn, 2,
3238                                         header,
3239                                         (utf8 && *utf8) ? away_subs(utf8, username) :
3240                                         _("<i>User has no away message</i>"), NULL);
3241                } else {
3242                        g_show_info_text(gc, info->sn, 0,
3243                                         header,
3244                                         (utf8 && *utf8) ? away_subs(utf8, username) : NULL,
3245                                         (utf8 && *utf8) ? "<hr>" : NULL,
3246                                         NULL);
3247                }
3248        } else if (infotype == AIM_GETINFO_CAPABILITIES) {
3249                g_show_info_text(gc, info->sn, 2,
3250                                header,
3251                                "<i>", _("Client Capabilities: "),
3252                                caps_string(info->capabilities),
3253                                "</i>",
3254                                NULL);
3255        } else {
3256                g_show_info_text(gc, info->sn, 1,
3257                                 (utf8 && *utf8) ? away_subs(utf8, username) : _("<i>No Information Provided</i>"),
3258                                 NULL);
3259        }
3260
3261        g_free(header);
3262        g_free(utf8);
3263
3264        return 1;
3265}
3266
3267static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
3268        char *msg;
3269        fu16_t id;
3270        va_list ap;
3271
3272        va_start(ap, fr);
3273        id  = (fu16_t) va_arg(ap, unsigned int);
3274        msg = va_arg(ap, char *);
3275        va_end(ap);
3276
3277        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3278                           "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id);
3279        if (id < 4)
3280                gaim_notify_warning(sess->aux_data, NULL,
3281                                                        _("Your AIM connection may be lost."), NULL);
3282
3283        return 1;
3284}
3285
3286static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
3287        va_list ap;
3288        fu16_t type;
3289        GaimConnection *gc = sess->aux_data;
3290        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
3291
3292        va_start(ap, fr);
3293        type = (fu16_t) va_arg(ap, unsigned int);
3294
3295        switch(type) {
3296                case 0x0002: {
3297                        fu8_t maxrooms;
3298                        struct aim_chat_exchangeinfo *exchanges;
3299                        int exchangecount, i;
3300
3301                        maxrooms = (fu8_t) va_arg(ap, unsigned int);
3302                        exchangecount = va_arg(ap, int);
3303                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
3304
3305                        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3306                                           "chat info: Chat Rights:\n");
3307                        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3308                                           "chat info: \tMax Concurrent Rooms: %hhd\n", maxrooms);
3309                        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3310                                           "chat info: \tExchange List: (%d total)\n", exchangecount);
3311                        for (i = 0; i < exchangecount; i++)
3312                                gaim_debug(GAIM_DEBUG_MISC, "oscar",
3313                                                   "chat info: \t\t%hu    %s\n",
3314                                                   exchanges[i].number, exchanges[i].name ? exchanges[i].name : "");
3315                        while (od->create_rooms) {
3316                                struct create_room *cr = od->create_rooms->data;
3317                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
3318                                                   "creating room %s\n", cr->name);
3319                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
3320                                g_free(cr->name);
3321                                od->create_rooms = g_slist_remove(od->create_rooms, cr);
3322                                g_free(cr);
3323                        }
3324                        }
3325                        break;
3326                case 0x0008: {
3327                        char *fqcn, *name, *ck;
3328                        fu16_t instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
3329                        fu8_t createperms;
3330                        fu32_t createtime;
3331
3332                        fqcn = va_arg(ap, char *);
3333                        instance = (fu16_t)va_arg(ap, unsigned int);
3334                        exchange = (fu16_t)va_arg(ap, unsigned int);
3335                        flags = (fu16_t)va_arg(ap, unsigned int);
3336                        createtime = va_arg(ap, fu32_t);
3337                        maxmsglen = (fu16_t)va_arg(ap, unsigned int);
3338                        maxoccupancy = (fu16_t)va_arg(ap, unsigned int);
3339                        createperms = (fu8_t)va_arg(ap, unsigned int);
3340                        unknown = (fu16_t)va_arg(ap, unsigned int);
3341                        name = va_arg(ap, char *);
3342                        ck = va_arg(ap, char *);
3343
3344                        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3345                                           "created room: %s %hu %hu %hu %u %hu %hu %hhu %hu %s %s\n",
3346                                        fqcn,
3347                                        exchange, instance, flags,
3348                                        createtime,
3349                                        maxmsglen, maxoccupancy, createperms, unknown,
3350                                        name, ck);
3351                        aim_chat_join(od->sess, od->conn, exchange, ck, instance);
3352                        }
3353                        break;
3354                default:
3355                        gaim_debug(GAIM_DEBUG_WARNING, "oscar",
3356                                           "chatnav info: unknown type (%04hx)\n", type);
3357                        break;
3358        }
3359
3360        va_end(ap);
3361
3362        return 1;
3363}
3364
3365static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
3366        va_list ap;
3367        int count, i;
3368        aim_userinfo_t *info;
3369        GaimConnection *g = sess->aux_data;
3370
3371        struct chat_connection *c = NULL;
3372
3373        va_start(ap, fr);
3374        count = va_arg(ap, int);
3375        info  = va_arg(ap, aim_userinfo_t *);
3376        va_end(ap);
3377
3378        c = find_oscar_chat_by_conn(g, fr->conn);
3379        if (!c)
3380                return 1;
3381
3382        for (i = 0; i < count; i++)
3383                gaim_chat_add_user(GAIM_CHAT(c->cnv), info[i].sn, NULL);
3384
3385        return 1;
3386}
3387
3388static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
3389        va_list ap;
3390        int count, i;
3391        aim_userinfo_t *info;
3392        GaimConnection *g = sess->aux_data;
3393
3394        struct chat_connection *c = NULL;
3395
3396        va_start(ap, fr);
3397        count = va_arg(ap, int);
3398        info  = va_arg(ap, aim_userinfo_t *);
3399        va_end(ap);
3400
3401        c = find_oscar_chat_by_conn(g, fr->conn);
3402        if (!c)
3403                return 1;
3404
3405        for (i = 0; i < count; i++)
3406                gaim_chat_remove_user(GAIM_CHAT(c->cnv), info[i].sn, NULL);
3407
3408        return 1;
3409}
3410
3411static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
3412        va_list ap;
3413        aim_userinfo_t *userinfo;
3414        struct aim_chat_roominfo *roominfo;
3415        char *roomname;
3416        int usercount;
3417        char *roomdesc;
3418        fu16_t unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
3419        fu32_t creationtime;
3420        GaimConnection *gc = sess->aux_data;
3421        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
3422
3423        va_start(ap, fr);
3424        roominfo = va_arg(ap, struct aim_chat_roominfo *);
3425        roomname = va_arg(ap, char *);
3426        usercount= va_arg(ap, int);
3427        userinfo = va_arg(ap, aim_userinfo_t *);
3428        roomdesc = va_arg(ap, char *);
3429        unknown_c9 = (fu16_t)va_arg(ap, unsigned int);
3430        creationtime = va_arg(ap, fu32_t);
3431        maxmsglen = (fu16_t)va_arg(ap, unsigned int);
3432        unknown_d2 = (fu16_t)va_arg(ap, unsigned int);
3433        unknown_d5 = (fu16_t)va_arg(ap, unsigned int);
3434        maxvisiblemsglen = (fu16_t)va_arg(ap, unsigned int);
3435        va_end(ap);
3436
3437        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3438                           "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n",
3439                           maxmsglen, maxvisiblemsglen);
3440
3441        ccon->maxlen = maxmsglen;
3442        ccon->maxvis = maxvisiblemsglen;
3443
3444        return 1;
3445}
3446
3447static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
3448        GaimConnection *gc = sess->aux_data;
3449        va_list ap;
3450        aim_userinfo_t *info;
3451        char *msg;
3452        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
3453
3454        va_start(ap, fr);
3455        info = va_arg(ap, aim_userinfo_t *);
3456        msg = va_arg(ap, char *);
3457        va_end(ap);
3458
3459        serv_got_chat_in(gc, ccon->id, info->sn, 0, msg, time((time_t)NULL));
3460
3461        return 1;
3462}
3463
3464static int gaim_email_parseupdate(aim_session_t *sess, aim_frame_t *fr, ...) {
3465        va_list ap;
3466        GaimConnection *gc = sess->aux_data;
3467        struct aim_emailinfo *emailinfo;
3468        int havenewmail;
3469
3470        va_start(ap, fr);
3471        emailinfo = va_arg(ap, struct aim_emailinfo *);
3472        havenewmail = va_arg(ap, int);
3473        va_end(ap);
3474
3475        if (emailinfo && gaim_account_get_check_mail(gc->account)) {
3476                gchar *to = g_strdup_printf("%s@%s", gaim_account_get_username(gaim_connection_get_account(gc)), emailinfo->domain);
3477                if (emailinfo->unread && havenewmail)
3478                        gaim_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL, (const char **)&to, (const char **)&emailinfo->url, NULL, NULL);
3479                g_free(to);
3480        }
3481
3482        return 1;
3483}
3484
3485static int gaim_icon_error(aim_session_t *sess, aim_frame_t *fr, ...) {
3486        GaimConnection *gc = sess->aux_data;
3487        struct oscar_data *od = gc->proto_data;
3488        char *sn;
3489
3490        sn = od->requesticon->data;
3491        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3492                           "removing %s from hash table\n", sn);
3493        od->requesticon = g_slist_remove(od->requesticon, sn);
3494        free(sn);
3495
3496        if (od->icontimer)
3497                g_source_remove(od->icontimer);
3498        od->icontimer = g_timeout_add(500, gaim_icon_timerfunc, gc);
3499
3500        return 1;
3501}
3502
3503static int gaim_icon_parseicon(aim_session_t *sess, aim_frame_t *fr, ...) {
3504        GaimConnection *gc = sess->aux_data;
3505        struct oscar_data *od = gc->proto_data;
3506        GSList *cur;
3507        va_list ap;
3508        char *sn;
3509        fu8_t *iconcsum, *icon;
3510        fu16_t iconcsumlen, iconlen;
3511
3512        va_start(ap, fr);
3513        sn = va_arg(ap, char *);
3514        iconcsum = va_arg(ap, fu8_t *);
3515        iconcsumlen = va_arg(ap, int);
3516        icon = va_arg(ap, fu8_t *);
3517        iconlen = va_arg(ap, int);
3518        va_end(ap);
3519
3520        if (iconlen > 0) {
3521                char *b16;
3522                GaimBuddy *b = gaim_find_buddy(gc->account, sn);
3523                gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc),
3524                                                                          sn, icon, iconlen);
3525                b16 = tobase16(iconcsum, iconcsumlen);
3526                if (b16) {
3527                        gaim_buddy_set_setting(b, "icon_checksum", b16);
3528                        gaim_blist_save();
3529                        free(b16);
3530                }
3531        }
3532
3533        cur = od->requesticon;
3534        while (cur) {
3535                char *cursn = cur->data;
3536                if (!aim_sncmp(cursn, sn)) {
3537                        od->requesticon = g_slist_remove(od->requesticon, cursn);
3538                        free(cursn);
3539                        cur = od->requesticon;
3540                } else
3541                        cur = cur->next;
3542        }
3543
3544        if (od->icontimer)
3545                g_source_remove(od->icontimer);
3546        od->icontimer = g_timeout_add(250, gaim_icon_timerfunc, gc);
3547
3548        return 1;
3549}
3550
3551static gboolean gaim_icon_timerfunc(gpointer data) {
3552        GaimConnection *gc = data;
3553        struct oscar_data *od = gc->proto_data;
3554        struct buddyinfo *bi;
3555        aim_conn_t *conn;
3556
3557        conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_ICON);
3558        if (!conn) {
3559                if (!od->iconconnecting) {
3560                        aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_ICON);
3561                        od->iconconnecting = TRUE;
3562                }
3563                return FALSE;
3564        }
3565
3566        if (od->set_icon) {
3567                struct stat st;
3568                const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc));
3569                if (iconfile == NULL) {
3570                        /* Set an empty icon, or something */
3571                } else if (!stat(iconfile, &st)) {
3572                        char *buf = g_malloc(st.st_size);
3573                        FILE *file = fopen(iconfile, "rb");
3574                        if (file) {
3575                                fread(buf, 1, st.st_size, file);
3576                                fclose(file);
3577                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
3578                                           "Uploading icon to icon server\n");
3579                                aim_bart_upload(od->sess, buf, st.st_size);
3580                        } else
3581                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
3582                                           "Can't open buddy icon file!\n");
3583                        g_free(buf);
3584                } else {
3585                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
3586                                   "Can't stat buddy icon file!\n");
3587                }
3588                od->set_icon = FALSE;
3589        }
3590
3591        if (!od->requesticon) {
3592                gaim_debug(GAIM_DEBUG_MISC, "oscar",
3593                                   "no more icons to request\n");
3594                return FALSE;
3595        }
3596
3597        bi = g_hash_table_lookup(od->buddyinfo, (char *)od->requesticon->data);
3598        if (bi && (bi->iconcsumlen > 0)) {
3599                aim_bart_request(od->sess, od->requesticon->data, bi->iconcsum, bi->iconcsumlen);
3600                return FALSE;
3601        } else {
3602                char *sn = od->requesticon->data;
3603                od->requesticon = g_slist_remove(od->requesticon, sn);
3604                free(sn);
3605        }
3606
3607        return TRUE;
3608}
3609
3610/*
3611 * Recieved in response to an IM sent with the AIM_IMFLAGS_ACK option.
3612 */
3613static int gaim_parse_msgack(aim_session_t *sess, aim_frame_t *fr, ...) {
3614        va_list ap;
3615        fu16_t type;
3616        char *sn;
3617
3618        va_start(ap, fr);
3619        type = (fu16_t) va_arg(ap, unsigned int);
3620        sn = va_arg(ap, char *);
3621        va_end(ap);
3622
3623        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Sent message to %s.\n", sn);
3624
3625        return 1;
3626}
3627
3628static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
3629        static const char *codes[5] = {
3630                "invalid",
3631                "change",
3632                "warning",
3633                "limit",
3634                "limit cleared",
3635        };
3636        va_list ap;
3637        fu16_t code, rateclass;
3638        fu32_t windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
3639
3640        va_start(ap, fr); 
3641        code = (fu16_t)va_arg(ap, unsigned int);
3642        rateclass= (fu16_t)va_arg(ap, unsigned int);
3643        windowsize = va_arg(ap, fu32_t);
3644        clear = va_arg(ap, fu32_t);
3645        alert = va_arg(ap, fu32_t);
3646        limit = va_arg(ap, fu32_t);
3647        disconnect = va_arg(ap, fu32_t);
3648        currentavg = va_arg(ap, fu32_t);
3649        maxavg = va_arg(ap, fu32_t);
3650        va_end(ap);
3651
3652        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3653                           "rate %s (param ID 0x%04hx): curavg = %u, maxavg = %u, alert at %u, "
3654                     "clear warning at %u, limit at %u, disconnect at %u (window size = %u)\n",
3655                     (code < 5) ? codes[code] : codes[0],
3656                     rateclass,
3657                     currentavg, maxavg,
3658                     alert, clear,
3659                     limit, disconnect,
3660                     windowsize);
3661
3662        /* XXX fix these values */
3663        if (code == AIM_RATE_CODE_CHANGE) {
3664                if (currentavg >= clear)
3665                        aim_conn_setlatency(fr->conn, 0);
3666        } else if (code == AIM_RATE_CODE_WARNING) {
3667                aim_conn_setlatency(fr->conn, windowsize/4);
3668        } else if (code == AIM_RATE_CODE_LIMIT) {
3669                gaim_notify_error(sess->aux_data, NULL, _("Rate limiting error."),
3670                                                  _("The last action you attempted could not be "
3671                                                        "performed because you are over the rate limit. "
3672                                                        "Please wait 10 seconds and try again."));
3673                aim_conn_setlatency(fr->conn, windowsize/2);
3674        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
3675                aim_conn_setlatency(fr->conn, 0);
3676        }
3677
3678        return 1;
3679}
3680
3681static int gaim_parse_evilnotify(aim_session_t *sess, aim_frame_t *fr, ...) {
3682        va_list ap;
3683        fu16_t newevil;
3684        aim_userinfo_t *userinfo;
3685        GaimConnection *gc = sess->aux_data;
3686
3687        va_start(ap, fr);
3688        newevil = (fu16_t) va_arg(ap, unsigned int);
3689        userinfo = va_arg(ap, aim_userinfo_t *);
3690        va_end(ap);
3691
3692        serv_got_eviled(gc, (userinfo && userinfo->sn[0]) ? userinfo->sn : NULL, (newevil/10.0) + 0.5);
3693
3694        return 1;
3695}
3696
3697static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
3698        va_list ap;
3699        aim_userinfo_t *info;
3700        GaimConnection *gc = sess->aux_data;
3701
3702        va_start(ap, fr);
3703        info = va_arg(ap, aim_userinfo_t *);
3704        va_end(ap);
3705
3706        gc->evil = (info->warnlevel/10.0) + 0.5;
3707
3708        if (info->onlinesince)
3709                gc->login_time_official = info->onlinesince;
3710
3711        return 1;
3712}
3713
3714static int gaim_connerr(aim_session_t *sess, aim_frame_t *fr, ...) {
3715        GaimConnection *gc = sess->aux_data;
3716        struct oscar_data *od = gc->proto_data;
3717        va_list ap;
3718        fu16_t code;
3719        char *msg;
3720
3721        va_start(ap, fr);
3722        code = (fu16_t)va_arg(ap, int);
3723        msg = va_arg(ap, char *);
3724        va_end(ap);
3725
3726        gaim_debug(GAIM_DEBUG_INFO, "oscar",
3727                           "Disconnected.  Code is 0x%04x and msg is %s\n", code, msg);
3728        if ((fr) && (fr->conn) && (fr->conn->type == AIM_CONN_TYPE_BOS)) {
3729                if (code == 0x0001) {
3730                        gc->wants_to_die = TRUE;
3731                        gaim_connection_error(gc, _("You have been disconnected because you have signed on with this screen name at another location."));
3732                } else {
3733                        gaim_connection_error(gc, _("You have been signed off for an unknown reason."));
3734                }
3735                od->killme = TRUE;
3736        }
3737
3738        return 1;
3739}
3740
3741static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
3742
3743        aim_reqpersonalinfo(sess, fr->conn);
3744
3745#ifndef NOSSI
3746        gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: requesting ssi list\n");
3747        aim_ssi_reqrights(sess);
3748        aim_ssi_reqdata(sess);
3749#endif
3750
3751        aim_bos_reqlocaterights(sess, fr->conn);
3752        aim_bos_reqbuddyrights(sess, fr->conn);
3753        aim_im_reqparams(sess);
3754        aim_bos_reqrights(sess, fr->conn); /* XXX - Don't call this with ssi? */
3755
3756#ifdef NOSSI
3757        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
3758        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE | AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
3759#endif
3760
3761        return 1;
3762}
3763
3764static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
3765        GaimConnection *gc = sess->aux_data;
3766        struct oscar_data *od = gc->proto_data;
3767
3768        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0003, gaim_info_change, 0);
3769        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0005, gaim_info_change, 0);
3770        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_ADM, 0x0007, gaim_account_confirm, 0);
3771
3772        aim_clientready(sess, fr->conn);
3773        gaim_debug(GAIM_DEBUG_INFO, "oscar", "connected to admin\n");
3774
3775        if (od->chpass) {
3776                gaim_debug(GAIM_DEBUG_INFO, "oscar", "changing password\n");
3777                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
3778                g_free(od->oldp);
3779                od->oldp = NULL;
3780                g_free(od->newp);
3781                od->newp = NULL;
3782                od->chpass = FALSE;
3783        }
3784        if (od->setnick) {
3785                gaim_debug(GAIM_DEBUG_INFO, "oscar", "formatting screenname\n");
3786                aim_admin_setnick(sess, fr->conn, od->newsn);
3787                g_free(od->newsn);
3788                od->newsn = NULL;
3789                od->setnick = FALSE;
3790        }
3791        if (od->conf) {
3792                gaim_debug(GAIM_DEBUG_INFO, "oscar", "confirming account\n");
3793                aim_admin_reqconfirm(sess, fr->conn);
3794                od->conf = FALSE;
3795        }
3796        if (od->reqemail) {
3797                gaim_debug(GAIM_DEBUG_INFO, "oscar", "requesting email\n");
3798                aim_admin_getinfo(sess, fr->conn, 0x0011);
3799                od->reqemail = FALSE;
3800        }
3801        if (od->setemail) {
3802                gaim_debug(GAIM_DEBUG_INFO, "oscar", "setting email\n");
3803                aim_admin_setemail(sess, fr->conn, od->email);
3804                g_free(od->email);
3805                od->email = NULL;
3806                od->setemail = FALSE;
3807        }
3808
3809        return 1;
3810}
3811
3812static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
3813        struct aim_icbmparameters *params;
3814        va_list ap;
3815
3816        va_start(ap, fr);
3817        params = va_arg(ap, struct aim_icbmparameters *);
3818        va_end(ap);
3819
3820        /* XXX - evidently this crashes on solaris. i have no clue why
3821        gaim_debug(GAIM_DEBUG_MISC, "oscar", "ICBM Parameters: maxchannel = %hu, default flags = 0x%08lx, max msg len = %hu, "
3822                        "max sender evil = %f, max receiver evil = %f, min msg interval = %u\n",
3823                        params->maxchan, params->flags, params->maxmsglen,
3824                        ((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0,
3825                        params->minmsginterval);
3826        */
3827
3828        /* Maybe senderwarn and recverwarn should be user preferences... */
3829        params->flags = 0x0000000b;
3830        params->maxmsglen = 8000;
3831        params->minmsginterval = 0;
3832
3833        aim_im_setparams(sess, params);
3834
3835        return 1;
3836}
3837
3838static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
3839{
3840        GaimConnection *gc = sess->aux_data;
3841        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
3842        va_list ap;
3843        fu16_t maxsiglen;
3844
3845        va_start(ap, fr);
3846        maxsiglen = (fu16_t) va_arg(ap, int);
3847        va_end(ap);
3848
3849        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3850                           "locate rights: max sig len = %d\n", maxsiglen);
3851
3852        od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen;
3853
3854        if (od->icq)
3855                aim_bos_setprofile(sess, fr->conn, NULL, NULL, 0, NULL, NULL, 0, caps_icq);
3856        else
3857                oscar_set_info(gc, gc->account->user_info);
3858
3859        return 1;
3860}
3861
3862static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
3863        va_list ap;
3864        fu16_t maxbuddies, maxwatchers;
3865        GaimConnection *gc = sess->aux_data;
3866        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
3867
3868        va_start(ap, fr);
3869        maxbuddies = (fu16_t) va_arg(ap, unsigned int);
3870        maxwatchers = (fu16_t) va_arg(ap, unsigned int);
3871        va_end(ap);
3872
3873        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3874                           "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers);
3875
3876        od->rights.maxbuddies = (guint)maxbuddies;
3877        od->rights.maxwatchers = (guint)maxwatchers;
3878
3879        return 1;
3880}
3881
3882static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
3883        fu16_t maxpermits, maxdenies;
3884        va_list ap;
3885        GaimConnection *gc = sess->aux_data;
3886        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
3887
3888        va_start(ap, fr);
3889        maxpermits = (fu16_t) va_arg(ap, unsigned int);
3890        maxdenies = (fu16_t) va_arg(ap, unsigned int);
3891        va_end(ap);
3892
3893        gaim_debug(GAIM_DEBUG_MISC, "oscar",
3894                           "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies);
3895
3896        od->rights.maxpermits = (guint)maxpermits;
3897        od->rights.maxdenies = (guint)maxdenies;
3898
3899        gaim_connection_set_state(gc, GAIM_CONNECTED);
3900        serv_finish_login(gc);
3901
3902        gaim_debug(GAIM_DEBUG_INFO, "oscar", "buddy list loaded\n");
3903
3904        aim_clientready(sess, fr->conn);
3905        aim_srv_setavailmsg(sess, NULL);
3906        aim_bos_setidle(sess, fr->conn, 0);
3907
3908        if (od->icq) {
3909                aim_icq_reqofflinemsgs(sess);
3910                aim_icq_hideip(sess);
3911        }
3912
3913        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
3914        if (sess->authinfo->email)
3915                aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_EMAIL);
3916
3917        return 1;
3918}
3919
3920static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
3921        va_list ap;
3922        struct aim_icq_offlinemsg *msg;
3923        struct aim_incomingim_ch4_args args;
3924        time_t t;
3925
3926        va_start(ap, fr);
3927        msg = va_arg(ap, struct aim_icq_offlinemsg *);
3928        va_end(ap);
3929
3930        gaim_debug(GAIM_DEBUG_INFO, "oscar",
3931                           "Received offline message.  Converting to channel 4 ICBM...\n");
3932        args.uin = msg->sender;
3933        args.type = msg->type;
3934        args.flags = msg->flags;
3935        args.msglen = msg->msglen;
3936        args.msg = msg->msg;
3937        t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
3938        incomingim_chan4(sess, fr->conn, NULL, &args, t);
3939
3940        return 1;
3941}
3942
3943static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
3944{
3945        aim_icq_ackofflinemsgs(sess);
3946        return 1;
3947}
3948
3949static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
3950{
3951        GaimConnection *gc = sess->aux_data;
3952        gchar *buf, *tmp, *utf8;
3953        gchar who[16];
3954        GaimBuddy *buddy;
3955        gchar *primary;
3956        va_list ap;
3957        struct aim_icq_info *info;
3958
3959        va_start(ap, fr);
3960        info = va_arg(ap, struct aim_icq_info *);
3961        va_end(ap);
3962
3963        if (!info->uin)
3964                return 0;
3965
3966        g_snprintf(who, sizeof(who), "%u", info->uin);
3967        buf = g_strdup_printf("<b>%s:</b> %s", _("UIN"), who);
3968        if (info->nick && info->nick[0] && (utf8 = gaim_try_conv_to_utf8(info->nick))) {
3969                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Nick"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
3970        }
3971        if (info->first && info->first[0] && (utf8 = gaim_try_conv_to_utf8(info->first))) {
3972                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("First Name"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
3973        }
3974        if (info->last && info->last[0] && (utf8 = gaim_try_conv_to_utf8(info->last))) {
3975                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Last Name"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
3976        }
3977        if (info->email && info->email[0] && (utf8 = gaim_try_conv_to_utf8(info->email))) {
3978                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Email Address"), ":</b> <a href=\"mailto:", utf8, "\">", utf8, "</a>", NULL);  g_free(tmp); g_free(utf8);
3979        }
3980        if (info->numaddresses && info->email2) {
3981                int i;
3982                for (i = 0; i < info->numaddresses; i++) {
3983                        if (info->email2[i] && info->email2[i][0] && (utf8 = gaim_try_conv_to_utf8(info->email2[i]))) {
3984                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Email Address"), ":</b> <a href=\"mailto:", utf8, "\">", utf8, "</a>", NULL);  g_free(tmp); g_free(utf8);
3985                        }
3986                }
3987        }
3988        if (info->mobile && info->mobile[0] && (utf8 = gaim_try_conv_to_utf8(info->mobile))) {
3989                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Mobile Phone"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
3990        }
3991        if (info->gender) {
3992                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Gender"), ":</b> ", info->gender==1 ? _("Female") : _("Male"), NULL);  g_free(tmp);
3993        }
3994        if (info->birthyear || info->birthmonth || info->birthday) {
3995                char date[30];
3996                struct tm tm;
3997                tm.tm_mday = (int)info->birthday;
3998                tm.tm_mon = (int)info->birthmonth-1;
3999                tm.tm_year = (int)info->birthyear-1900;
4000                strftime(date, sizeof(date), "%x", &tm);
4001                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Birthday"), ":</b> ", date, NULL);  g_free(tmp);
4002        }
4003        if (info->age) {
4004                char age[5];
4005                snprintf(age, sizeof(age), "%hhd", info->age);
4006                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Age"), ":</b> ", age, NULL);  g_free(tmp);
4007        }
4008        if (info->personalwebpage && info->personalwebpage[0] && (utf8 = gaim_try_conv_to_utf8(info->personalwebpage))) {
4009                tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Personal Web Page"), ":</b> <a href=\"", utf8, "\">", utf8, "</a>", NULL);  g_free(tmp); g_free(utf8);
4010        }
4011        if (info->info && info->info[0] && (utf8 = gaim_try_conv_to_utf8(info->info))) {
4012                tmp = buf;  buf = g_strconcat(tmp, "<hr><b>", _("Additional Information"), ":</b><br>", utf8, NULL);  g_free(tmp); g_free(utf8);
4013        }
4014        tmp = buf;  buf = g_strconcat(tmp, "<hr>\n", NULL);  g_free(tmp);
4015        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
4016                tmp = buf;  buf = g_strconcat(tmp, "<b>", _("Home Address"), ":</b>", NULL);  g_free(tmp);
4017                if (info->homeaddr && info->homeaddr[0] && (utf8 = gaim_try_conv_to_utf8(info->homeaddr))) {
4018                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Address"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4019                }
4020                if (info->homecity && info->homecity[0] && (utf8 = gaim_try_conv_to_utf8(info->homecity))) {
4021                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("City"), ":</b> ", utf8,  NULL);  g_free(tmp); g_free(utf8);
4022                }
4023                if (info->homestate && info->homestate[0] && (utf8 = gaim_try_conv_to_utf8(info->homestate))) {
4024                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("State"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4025                }
4026                if (info->homezip && info->homezip[0] && (utf8 = gaim_try_conv_to_utf8(info->homezip))) {
4027                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Zip Code"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4028                }
4029                tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp);
4030        }
4031        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
4032                tmp = buf;  buf = g_strconcat(tmp, "<b>", _("Work Address"), ":</b>", NULL);  g_free(tmp);
4033                if (info->workaddr && info->workaddr[0] && (utf8 = gaim_try_conv_to_utf8(info->workaddr))) {
4034                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Address"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4035                }
4036                if (info->workcity && info->workcity[0] && (utf8 = gaim_try_conv_to_utf8(info->workcity))) {
4037                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("City"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4038                }
4039                if (info->workstate && info->workstate[0] && (utf8 = gaim_try_conv_to_utf8(info->workstate))) {
4040                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("State"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4041                }
4042                if (info->workzip && info->workzip[0] && (utf8 = gaim_try_conv_to_utf8(info->workzip))) {
4043                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Zip Code"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4044                }
4045                tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp);
4046        }
4047        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
4048                tmp = buf;  buf = g_strconcat(tmp, "<b>", _("Work Information"), ":</b>", NULL);  g_free(tmp);
4049                if (info->workcompany && info->workcompany[0] && (utf8 = gaim_try_conv_to_utf8(info->workcompany))) {
4050                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Company"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4051                }
4052                if (info->workdivision && info->workdivision[0] && (utf8 = gaim_try_conv_to_utf8(info->workdivision))) {
4053                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Division"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4054                }
4055                if (info->workposition && info->workposition[0] && (utf8 = gaim_try_conv_to_utf8(info->workposition))) {
4056                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Position"), ":</b> ", utf8, NULL);  g_free(tmp); g_free(utf8);
4057                }
4058                if (info->workwebpage && info->workwebpage[0] && (utf8 = gaim_try_conv_to_utf8(info->workwebpage))) {
4059                        tmp = buf;  buf = g_strconcat(tmp, "\n<br><b>", _("Web Page"), ":</b> <a href=\"", utf8, "\">", utf8, "</a>", NULL);  g_free(tmp); g_free(utf8);
4060                }
4061                tmp = buf; buf = g_strconcat(tmp, "\n<hr>\n", NULL); g_free(tmp);
4062        }
4063
4064        buddy = gaim_find_buddy(gaim_connection_get_account(gc), who);
4065        primary = g_strdup_printf(_("ICQ Info for %s"), gaim_get_buddy_alias(buddy));
4066        gaim_notify_formatted(gc, NULL, primary, NULL, buf, NULL, NULL);
4067        g_free(primary);
4068        g_free(buf);
4069
4070        return 1;
4071}
4072
4073static int gaim_icqalias(aim_session_t *sess, aim_frame_t *fr, ...)
4074{
4075        GaimConnection *gc = sess->aux_data;
4076        gchar who[16], *utf8;
4077        GaimBuddy *b;
4078        va_list ap;
4079        struct aim_icq_info *info;
4080
4081        va_start(ap, fr);
4082        info = va_arg(ap, struct aim_icq_info *);
4083        va_end(ap);
4084
4085        if (info->uin && info->nick && info->nick[0] && (utf8 = gaim_try_conv_to_utf8(info->nick))) {
4086                g_snprintf(who, sizeof(who), "%u", info->uin);
4087                serv_got_alias(gc, who, utf8);
4088                if ((b = gaim_find_buddy(gc->account, who))) {
4089                        gaim_buddy_set_setting(b, "servernick", utf8);
4090                        gaim_blist_save();
4091                }
4092                g_free(utf8);
4093        }
4094
4095        return 1;
4096}
4097
4098static int gaim_popup(aim_session_t *sess, aim_frame_t *fr, ...)
4099{
4100        char *msg, *url;
4101        fu16_t wid, hei, delay;
4102        va_list ap;
4103
4104        va_start(ap, fr);
4105        msg = va_arg(ap, char *);
4106        url = va_arg(ap, char *);
4107        wid = (fu16_t) va_arg(ap, int);
4108        hei = (fu16_t) va_arg(ap, int);
4109        delay = (fu16_t) va_arg(ap, int);
4110        va_end(ap);
4111
4112        serv_got_popup(msg, url, wid, hei);
4113
4114        return 1;
4115}
4116
4117static int gaim_parse_searchreply(aim_session_t *sess, aim_frame_t *fr, ...) {
4118        GaimConnection *gc = sess->aux_data;
4119        gchar *secondary;
4120        GString *text;
4121        int i, num;
4122        va_list ap;
4123        char *email, *SNs;
4124
4125        va_start(ap, fr);
4126        email = va_arg(ap, char *);
4127        num = va_arg(ap, int);
4128        SNs = va_arg(ap, char *);
4129        va_end(ap);
4130
4131        secondary = g_strdup_printf(_("The following screennames are associated with %s"), email);
4132        text = g_string_new("");
4133        for (i = 0; i < num; i++)
4134                g_string_append_printf(text, "%s<br>", &SNs[i * (MAXSNLEN + 1)]);
4135        gaim_notify_formatted(gc, NULL, _("Search Results"), secondary, text->str, NULL, NULL);
4136
4137        g_free(secondary);
4138        g_string_free(text, TRUE);
4139
4140        return 1;
4141}
4142
4143static int gaim_parse_searcherror(aim_session_t *sess, aim_frame_t *fr, ...) {
4144        va_list ap;
4145        char *email;
4146        char *buf;
4147
4148        va_start(ap, fr);
4149        email = va_arg(ap, char *);
4150        va_end(ap);
4151
4152        buf = g_strdup_printf(_("No results found for email address %s"), email);
4153        gaim_notify_error(sess->aux_data, NULL, buf, NULL);
4154        g_free(buf);
4155
4156        return 1;
4157}
4158
4159static int gaim_account_confirm(aim_session_t *sess, aim_frame_t *fr, ...) {
4160        GaimConnection *gc = sess->aux_data;
4161        fu16_t status;
4162        va_list ap;
4163        char msg[256];
4164
4165        va_start(ap, fr);
4166        status = (fu16_t) va_arg(ap, unsigned int); /* status code of confirmation request */
4167        va_end(ap);
4168
4169        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4170                           "account confirmation returned status 0x%04x (%s)\n", status,
4171                        status ? "unknown" : "email sent");
4172        if (!status) {
4173                g_snprintf(msg, sizeof(msg), _("You should receive an email asking to confirm %s."),
4174                                gaim_account_get_username(gaim_connection_get_account(gc)));
4175                gaim_notify_info(gc, NULL, _("Account Confirmation Requested"), msg);
4176        }
4177
4178        return 1;
4179}
4180
4181static int gaim_info_change(aim_session_t *sess, aim_frame_t *fr, ...) {
4182        GaimConnection *gc = sess->aux_data;
4183        va_list ap;
4184        fu16_t perms, err;
4185        char *url, *sn, *email;
4186        int change;
4187
4188        va_start(ap, fr);
4189        change = va_arg(ap, int);
4190        perms = (fu16_t) va_arg(ap, unsigned int);
4191        err = (fu16_t) va_arg(ap, unsigned int);
4192        url = va_arg(ap, char *);
4193        sn = va_arg(ap, char *);
4194        email = va_arg(ap, char *);
4195        va_end(ap);
4196
4197        gaim_debug(GAIM_DEBUG_MISC, "oscar",
4198                           "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, sn=%s, email=%s\n",
4199                change ? "change" : "request", perms, err, url, sn, email);
4200
4201        if (err && url) {
4202                char *dialog_msg;
4203                char *dialog_top = g_strdup_printf(_("Error Changing Account Info"));
4204                switch (err) {
4205                        case 0x0001: {
4206                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name differs from the original."), err);
4207                        } break;
4208                        case 0x0006: {
4209                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name ends in a space."), err);
4210                        } break;
4211                        case 0x000b: {
4212                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name is too long."), err);
4213                        } break;
4214                        case 0x001d: {
4215                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because there is already a request pending for this screen name."), err);
4216                        } break;
4217                        case 0x0021: {
4218                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address has too many screen names associated with it."), err);
4219                        } break;
4220                        case 0x0023: {
4221                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err);
4222                        } break;
4223                        default: {
4224                                dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err);
4225                        } break;
4226                }
4227                gaim_notify_error(gc, NULL, dialog_top, dialog_msg);
4228                g_free(dialog_top);
4229                g_free(dialog_msg);
4230                return 1;
4231        }
4232
4233        if (sn) {
4234                char *dialog_msg = g_strdup_printf(_("Your screen name is currently formatted as follows:\n%s"), sn);
4235                gaim_notify_info(gc, NULL, _("Account Info"), dialog_msg);
4236                g_free(dialog_msg);
4237        }
4238
4239        if (email) {
4240                char *dialog_msg = g_strdup_printf(_("The email address for %s is %s"), 
4241                                                   gaim_account_get_username(gaim_connection_get_account(gc)), email);
4242                gaim_notify_info(gc, NULL, _("Account Info"), dialog_msg);
4243                g_free(dialog_msg);
4244        }
4245
4246        return 1;
4247}
4248
4249static void oscar_keepalive(GaimConnection *gc) {
4250        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4251        aim_flap_nop(od->sess, od->conn);
4252}
4253
4254static int oscar_send_typing(GaimConnection *gc, const char *name, int typing) {
4255        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4256        struct direct_im *dim = find_direct_im(od, name);
4257        if (dim)
4258                if (typing == GAIM_TYPING)
4259                        aim_odc_send_typing(od->sess, dim->conn, 0x0002);
4260                else if (typing == GAIM_TYPED)
4261                        aim_odc_send_typing(od->sess, dim->conn, 0x0001);
4262                else
4263                        aim_odc_send_typing(od->sess, dim->conn, 0x0000);
4264        else {
4265                /* Don't send if this turkey is in our deny list */
4266                GSList *list;
4267                for (list=gc->account->deny; (list && aim_sncmp(name, list->data)); list=list->next);
4268                if (!list) {
4269                        struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, normalize(name));
4270                        if (bi && bi->typingnot) {
4271                                if (typing == GAIM_TYPING)
4272                                        aim_im_sendmtn(od->sess, 0x0001, name, 0x0002);
4273                                else if (typing == GAIM_TYPED)
4274                                        aim_im_sendmtn(od->sess, 0x0001, name, 0x0001);
4275                                else
4276                                        aim_im_sendmtn(od->sess, 0x0001, name, 0x0000);
4277                        }
4278                }
4279        }
4280        return 0;
4281}
4282static void oscar_ask_direct_im(GaimConnection *gc, const char *name);
4283static int gaim_odc_send_im(aim_session_t *, aim_conn_t *, const char *, GaimImFlags);
4284
4285static int oscar_send_im(GaimConnection *gc, const char *name, const char *message, GaimImFlags imflags) {
4286        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4287        struct direct_im *dim = find_direct_im(od, name);
4288        int ret = 0;
4289        GError *err = NULL;
4290        const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc));
4291        char *tmpmsg = NULL;
4292
4293        if (dim && dim->connected) {
4294                /* If we're directly connected, send a direct IM */
4295                ret = gaim_odc_send_im(od->sess, dim->conn, message, imflags);
4296        } else if (imflags & GAIM_IM_IMAGES) {
4297                /* Trying to send an IM image outside of a direct connection. */
4298                oscar_ask_direct_im(gc, name);
4299                ret = -ENOTCONN;
4300        } else {
4301                struct buddyinfo *bi;
4302                struct aim_sendimext_args args;
4303                struct stat st;
4304                gsize len;
4305
4306                bi = g_hash_table_lookup(od->buddyinfo, normalize(name));
4307                if (!bi) {
4308                        bi = g_new0(struct buddyinfo, 1);
4309                        g_hash_table_insert(od->buddyinfo, g_strdup(normalize(name)), bi);
4310                }
4311
4312                args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES;
4313                if (od->icq) {
4314                        args.features = features_icq;
4315                        args.featureslen = sizeof(features_icq);
4316                        args.flags |= AIM_IMFLAGS_OFFLINE;
4317                } else {
4318                        args.features = features_aim;
4319                        args.featureslen = sizeof(features_aim);
4320
4321                        if (imflags & GAIM_IM_AUTO_RESP)
4322                                args.flags |= AIM_IMFLAGS_AWAY;
4323                }
4324
4325                if (bi->ico_need) {
4326                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4327                                           "Sending buddy icon request with message\n");
4328                        args.flags |= AIM_IMFLAGS_BUDDYREQ;
4329                        bi->ico_need = FALSE;
4330                }
4331
4332                if (iconfile && !stat(iconfile, &st)) {
4333                        FILE *file = fopen(iconfile, "r");
4334                        if (file) {
4335                                char *buf = g_malloc(st.st_size);
4336                                fread(buf, 1, st.st_size, file);
4337                                fclose(file);
4338
4339                                args.iconlen   = st.st_size;
4340                                args.iconsum   = aimutil_iconsum(buf, st.st_size);
4341                                args.iconstamp = st.st_mtime;
4342
4343                                if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time))
4344                                        bi->ico_informed = FALSE;
4345
4346                                if (!bi->ico_informed) {
4347                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4348                                                           "Claiming to have a buddy icon\n");
4349                                        args.flags |= AIM_IMFLAGS_HASICON;
4350                                        bi->ico_me_len = args.iconlen;
4351                                        bi->ico_me_csum = args.iconsum;
4352                                        bi->ico_me_time = args.iconstamp;
4353                                        bi->ico_informed = TRUE;
4354                                }
4355
4356                                g_free(buf);
4357                        }
4358                }
4359
4360                args.destsn = name;
4361
4362                /* For ICQ send newlines as CR/LF, for AIM send newlines as <BR> */
4363                if (isdigit(name[0]))
4364                        tmpmsg = add_cr(message);
4365                else
4366                        tmpmsg = strdup_withhtml(message);
4367                len = strlen(tmpmsg);
4368
4369                args.flags |= oscar_encoding_check(tmpmsg);
4370                if (args.flags & AIM_IMFLAGS_UNICODE) {
4371                        gaim_debug(GAIM_DEBUG_INFO, "oscar", "Sending Unicode IM\n");
4372                        args.charset = 0x0002;
4373                        args.charsubset = 0x0000;
4374                        args.msg = g_convert(tmpmsg, len, "UCS-2BE", "UTF-8", NULL, &len, &err);
4375                        if (err) {
4376                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4377                                                   "Error converting a unicode message: %s\n", err->message);
4378                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4379                                                   "This really shouldn't happen!\n");
4380                                /* We really shouldn't try to send the
4381                                 * IM now, but I'm not sure what to do */
4382                                g_error_free(err);
4383                        }
4384                } else if (args.flags & AIM_IMFLAGS_ISO_8859_1) {
4385                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4386                                           "Sending ISO-8859-1 IM\n");
4387                        args.charset = 0x0003;
4388                        args.charsubset = 0x0000;
4389                        args.msg = g_convert(tmpmsg, len, "ISO-8859-1", "UTF-8", NULL, &len, &err);
4390                        if (err) {
4391                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4392                                                   "conversion error: %s\n", err->message);
4393                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4394                                                   "Someone tell Ethan his 8859-1 detection is wrong\n");
4395                                args.flags ^= AIM_IMFLAGS_ISO_8859_1 | AIM_IMFLAGS_UNICODE;
4396                                len = strlen(tmpmsg);
4397                                g_error_free(err);
4398                                args.msg = g_convert(tmpmsg, len, "UCS-2BE", "UTF8", NULL, &len, &err);
4399                                if (err) {
4400                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4401                                                           "Error in unicode fallback: %s\n", err->message);
4402                                        g_error_free(err);
4403                                }
4404                        }
4405                } else {
4406                        args.charset = 0x0000;
4407                        args.charsubset = 0x0000;
4408                        args.msg = tmpmsg;
4409                }
4410                args.msglen = len;
4411
4412                ret = aim_im_sendch1_ext(od->sess, &args);
4413        }
4414
4415        g_free(tmpmsg);
4416
4417        if (ret >= 0)
4418                return 1;
4419
4420        return ret;
4421}
4422
4423static void oscar_get_info(GaimConnection *g, const char *name) {
4424        struct oscar_data *od = (struct oscar_data *)g->proto_data;
4425        if (od->icq)
4426                aim_icq_getallinfo(od->sess, name);
4427        else
4428                /* people want the away message on the top, so we get the away message
4429                 * first and then get the regular info, since it's too difficult to
4430                 * insert in the middle. i hate people. */
4431                aim_getinfo(od->sess, od->conn, name, AIM_GETINFO_AWAYMESSAGE);
4432}
4433
4434static void oscar_get_away(GaimConnection *g, const char *who) {
4435        struct oscar_data *od = (struct oscar_data *)g->proto_data;
4436        if (od->icq) {
4437                GaimBuddy *budlight = gaim_find_buddy(g->account, who);
4438                if (budlight)
4439                        if ((budlight->uc & 0xffff0000) >> 16)
4440                                aim_im_sendch2_geticqaway(od->sess, who, (budlight->uc & 0xffff0000) >> 16);
4441                        else
4442                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4443                                                   "Error: The user %s has no status message, therefore not requesting.\n", who);
4444                else
4445                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
4446                                           "Error: Could not find %s in local contact list, therefore unable to request status message.\n", who);
4447        } else
4448                aim_getinfo(od->sess, od->conn, who, AIM_GETINFO_GENERALINFO);
4449}
4450
4451static void oscar_set_dir(GaimConnection *g, const char *first, const char *middle, const char *last,
4452                          const char *maiden, const char *city, const char *state, const char *country, int web) {
4453        /* XXX - some of these things are wrong, but i'm lazy */
4454        struct oscar_data *od = (struct oscar_data *)g->proto_data;
4455        aim_setdirectoryinfo(od->sess, od->conn, first, middle, last,
4456                                maiden, NULL, NULL, city, state, NULL, 0, web);
4457}
4458
4459static void oscar_set_idle(GaimConnection *gc, int time) {
4460        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4461        aim_bos_setidle(od->sess, od->conn, time);
4462}
4463
4464static void oscar_set_info(GaimConnection *gc, const char *text) {
4465        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4466        fu32_t flags = 0;
4467        char *text_html = NULL;
4468        char *msg = NULL;
4469        gsize msglen = 0;
4470
4471        if (od->rights.maxsiglen == 0)
4472                gaim_notify_warning(gc, NULL, _("Unable to set AIM profile."),
4473                                                        _("You have probably requested to set your "
4474                                                          "profile before the login procedure completed.  "
4475                                                          "Your profile remains unset; try setting it "
4476                                                          "again when you are fully connected."));
4477
4478        if (od->icq)
4479                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, NULL, NULL, 0, caps_icq);
4480        else {
4481                if (!text) {
4482                        aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, NULL, NULL, 0, caps_aim);
4483                        return;
4484                }
4485               
4486                text_html = strdup_withhtml(text);
4487                flags = oscar_encoding_check(text_html);
4488                if (flags & AIM_IMFLAGS_UNICODE) {
4489                        msg = g_convert(text_html, strlen(text_html), "UCS-2BE", "UTF-8", NULL, &msglen, NULL);
4490                        aim_bos_setprofile(od->sess, od->conn, "unicode-2-0", msg, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0, caps_aim);
4491                        g_free(msg);
4492                } else if (flags & AIM_IMFLAGS_ISO_8859_1) {
4493                        msg = g_convert(text_html, strlen(text_html), "ISO-8859-1", "UTF-8", NULL, &msglen, NULL);
4494                        aim_bos_setprofile(od->sess, od->conn, "iso-8859-1", msg, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0, caps_aim);
4495                        g_free(msg);
4496                } else {
4497                        msglen = strlen(text_html);
4498                        aim_bos_setprofile(od->sess, od->conn, "us-ascii", text_html, (msglen > od->rights.maxsiglen ? od->rights.maxsiglen : msglen), NULL, NULL, 0, caps_aim);
4499                }
4500
4501                if (msglen > od->rights.maxsiglen) {
4502                        gchar *errstr;
4503                        errstr = g_strdup_printf(ngettext("The maximum profile length of %d byte "
4504                                                                         "has been exceeded.  Gaim has truncated it for you.",
4505                                                                         "The maximum profile length of %d bytes "
4506                                                                         "has been exceeded.  Gaim has truncated it for you.",
4507                                                                         od->rights.maxsiglen), od->rights.maxsiglen);
4508                        gaim_notify_warning(gc, NULL, _("Profile too long."), errstr);
4509                        g_free(errstr);
4510                }
4511
4512                g_free(text_html);
4513
4514        }
4515
4516        return;
4517}
4518
4519static void oscar_set_away_aim(GaimConnection *gc, struct oscar_data *od, const char *text)
4520{
4521        fu32_t flags = 0;
4522        gchar *text_html = NULL;
4523        char *msg = NULL;
4524        gsize msglen = 0;
4525
4526        if (od->rights.maxawaymsglen == 0)
4527                gaim_notify_warning(gc, NULL, _("Unable to set AIM away message."),
4528                                                        _("You have probably requested to set your "
4529                                                          "away message before the login procedure "
4530                                                          "completed.  You remain in a \"present\" "
4531                                                          "state; try setting it again when you are "
4532                                                          "fully connected."));
4533
4534        if (gc->away) {
4535                g_free(gc->away);
4536                gc->away = NULL;
4537        }
4538
4539        if (!text) {
4540                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, NULL, "", 0, caps_aim);
4541                return;
4542        }
4543
4544        text_html = strdup_withhtml(text);
4545        flags = oscar_encoding_check(text_html);
4546        if (flags & AIM_IMFLAGS_UNICODE) {
4547                msg = g_convert(text_html, strlen(text_html), "UCS-2BE", "UTF-8", NULL, &msglen, NULL);
4548                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, "unicode-2-0", msg, 
4549                        (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen), caps_aim);
4550                g_free(msg);
4551                gc->away = g_strndup(text, od->rights.maxawaymsglen/2);
4552        } else if (flags & AIM_IMFLAGS_ISO_8859_1) {
4553                msg = g_convert(text_html, strlen(text_html), "ISO-8859-1", "UTF-8", NULL, &msglen, NULL);
4554                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, "iso-8859-1", msg, 
4555                        (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen), caps_aim);
4556                g_free(msg);
4557                gc->away = g_strndup(text_html, od->rights.maxawaymsglen);
4558        } else {
4559                msglen = strlen(text_html);
4560                aim_bos_setprofile(od->sess, od->conn, NULL, NULL, 0, "us-ascii", text_html, 
4561                        (msglen > od->rights.maxawaymsglen ? od->rights.maxawaymsglen : msglen), caps_aim);
4562                gc->away = g_strndup(text_html, od->rights.maxawaymsglen);
4563        }
4564
4565        if (msglen > od->rights.maxawaymsglen) {
4566                gchar *errstr;
4567
4568                errstr = g_strdup_printf(ngettext("The maximum away message length of %d byte "
4569                                                                 "has been exceeded.  Gaim has truncated it for you.",
4570                                                                 "The maximum away message length of %d bytes "
4571                                                                 "has been exceeded.  Gaim has truncated it for you.",
4572                                                                 od->rights.maxawaymsglen), od->rights.maxawaymsglen);
4573                gaim_notify_warning(gc, NULL, _("Away message too long."), errstr);
4574                g_free(errstr);
4575        }
4576       
4577        g_free(text_html);
4578        return;
4579}
4580
4581static void oscar_set_away_icq(GaimConnection *gc, struct oscar_data *od, const char *state, const char *message)
4582{
4583        GaimAccount *account = gaim_connection_get_account(gc);
4584        if (gc->away) {
4585                g_free(gc->away);
4586                gc->away = NULL;
4587        }
4588
4589        if (strcmp(state, _("Invisible"))) {
4590                if ((od->sess->ssi.received_data) && (aim_ssi_getpermdeny(od->sess->ssi.local) != account->perm_deny))
4591                        aim_ssi_setpermdeny(od->sess, account->perm_deny, 0xffffffff);
4592                account->perm_deny = 4;
4593        } else {
4594                if ((od->sess->ssi.received_data) && (aim_ssi_getpermdeny(od->sess->ssi.local) != 0x03))
4595                        aim_ssi_setpermdeny(od->sess, 0x03, 0xffffffff);
4596                account->perm_deny = 3;
4597        }
4598
4599        if (!strcmp(state, _("Online")))
4600                aim_setextstatus(od->sess, AIM_ICQ_STATE_NORMAL);
4601        else if (!strcmp(state, _("Away"))) {
4602                aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY);
4603                gc->away = g_strdup("");
4604        } else if (!strcmp(state, _("Do Not Disturb"))) {
4605                aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
4606                gc->away = g_strdup("");
4607        } else if (!strcmp(state, _("Not Available"))) {
4608                aim_setextstatus(od->sess, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
4609                gc->away = g_strdup("");
4610        } else if (!strcmp(state, _("Occupied"))) {
4611                aim_setextstatus(od->sess, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
4612                gc->away = g_strdup("");
4613        } else if (!strcmp(state, _("Free For Chat"))) {
4614                aim_setextstatus(od->sess, AIM_ICQ_STATE_CHAT);
4615                gc->away = g_strdup("");
4616        } else if (!strcmp(state, _("Invisible"))) {
4617                aim_setextstatus(od->sess, AIM_ICQ_STATE_INVISIBLE);
4618                gc->away = g_strdup("");
4619        } else if (!strcmp(state, GAIM_AWAY_CUSTOM)) {
4620                if (message) {
4621                        aim_setextstatus(od->sess, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
4622                        gc->away = g_strdup("");
4623                } else {
4624                        aim_setextstatus(od->sess, AIM_ICQ_STATE_NORMAL);
4625                }
4626        }
4627
4628        return;
4629}
4630
4631static void oscar_set_away(GaimConnection *gc, const char *state, const char *message)
4632{
4633        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4634
4635        if (od->icq)
4636                oscar_set_away_icq(gc, od, state, message);
4637        else
4638                oscar_set_away_aim(gc, od, message);
4639
4640        return;
4641}
4642
4643static void oscar_warn(GaimConnection *gc, const char *name, int anon) {
4644        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4645        aim_im_warn(od->sess, od->conn, name, anon ? AIM_WARN_ANON : 0);
4646}
4647
4648static void oscar_dir_search(GaimConnection *gc, const char *first, const char *middle, const char *last,
4649                             const char *maiden, const char *city, const char *state, const char *country, const char *email) {
4650        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4651        if (strlen(email))
4652                aim_search_address(od->sess, od->conn, email);
4653}
4654
4655static void oscar_add_buddy(GaimConnection *gc, const char *name, GaimGroup *g) {
4656        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4657#ifdef NOSSI
4658        aim_add_buddy(od->sess, od->conn, name);
4659#else
4660        if ((od->sess->ssi.received_data) && !(aim_ssi_itemlist_exists(od->sess->ssi.local, name))) {
4661                GaimBuddy *buddy = gaim_find_buddy(gc->account, name);
4662                GaimGroup *group = gaim_find_buddys_group(buddy);
4663                if (buddy && group) {
4664                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4665                                           "ssi: adding buddy %s to group %s\n", name, group->name);
4666                        aim_ssi_addbuddy(od->sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0);
4667                }
4668        }
4669#endif
4670        if (od->icq)
4671                aim_icq_getalias(od->sess, name);
4672}
4673
4674static void oscar_add_buddies(GaimConnection *gc, GList *buddies) {
4675        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4676#ifdef NOSSI
4677        char buf[MSG_LEN];
4678        int n=0;
4679        while (buddies) {
4680                if (n > MSG_LEN - 18) {
4681                        aim_bos_setbuddylist(od->sess, od->conn, buf);
4682                        n = 0;
4683                }
4684                n += g_snprintf(buf + n, sizeof(buf) - n, "%s&", (char *)buddies->data);
4685                buddies = buddies->next;
4686        }
4687        aim_bos_setbuddylist(od->sess, od->conn, buf);
4688#else
4689        if (od->sess->ssi.received_data) {
4690                while (buddies) {
4691                        GaimBuddy *buddy = gaim_find_buddy(gc->account, (const char *)buddies->data);
4692                        GaimGroup *group = gaim_find_buddys_group(buddy);
4693                        if (buddy && group) {
4694                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4695                                                   "ssi: adding buddy %s to group %s\n", (const char *)buddies->data, group->name);
4696                                aim_ssi_addbuddy(od->sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0);
4697                        }
4698                        buddies = buddies->next;
4699                }
4700        }
4701#endif
4702}
4703
4704static void oscar_remove_buddy(GaimConnection *gc, const char *name, const char *group) {
4705        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4706#ifdef NOSSI
4707        aim_remove_buddy(od->sess, od->conn, name);
4708#else
4709        if (od->sess->ssi.received_data) {
4710                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4711                                   "ssi: deleting buddy %s from group %s\n", name, group);
4712                aim_ssi_delbuddy(od->sess, name, group);
4713        }
4714#endif
4715}
4716
4717static void oscar_remove_buddies(GaimConnection *gc, GList *buddies, const char *group) {
4718        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4719#ifdef NOSSI
4720        GList *cur;
4721        for (cur=buddies; cur; cur=cur->next)
4722                aim_remove_buddy(od->sess, od->conn, cur->data);
4723#else
4724        if (od->sess->ssi.received_data) {
4725                while (buddies) {
4726                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4727                                           "ssi: deleting buddy %s from group %s\n", (char *)buddies->data, group);
4728                        aim_ssi_delbuddy(od->sess, buddies->data, group);
4729                        buddies = buddies->next;
4730                }
4731        }
4732#endif
4733}
4734
4735#ifndef NOSSI
4736static void oscar_move_buddy(GaimConnection *gc, const char *name, const char *old_group, const char *new_group) {
4737        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4738        if (od->sess->ssi.received_data && strcmp(old_group, new_group)) {
4739                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4740                                   "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group);
4741                aim_ssi_movebuddy(od->sess, old_group, new_group, name);
4742        }
4743}
4744
4745static void oscar_alias_buddy(GaimConnection *gc, const char *name, const char *alias) {
4746        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4747        if (od->sess->ssi.received_data) {
4748                char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, name);
4749                if (gname) {
4750                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4751                                           "ssi: changing the alias for buddy %s to %s\n", name, alias);
4752                        aim_ssi_aliasbuddy(od->sess, gname, name, alias);
4753                }
4754        }
4755}
4756
4757static void oscar_rename_group(GaimConnection *g, const char *old_group, const char *new_group, GList *members) {
4758        struct oscar_data *od = (struct oscar_data *)g->proto_data;
4759
4760        if (od->sess->ssi.received_data) {
4761                if (aim_ssi_itemlist_finditem(od->sess->ssi.local, new_group, NULL, AIM_SSI_TYPE_GROUP)) {
4762                        oscar_remove_buddies(g, members, old_group);
4763                        oscar_add_buddies(g, members);
4764                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4765                                           "ssi: moved all buddies from group %s to %s\n", old_group, new_group);
4766                } else {
4767                        aim_ssi_rename_group(od->sess, old_group, new_group);
4768                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4769                                           "ssi: renamed group %s to %s\n", old_group, new_group);
4770                }
4771        }
4772}
4773
4774static gboolean gaim_ssi_rerequestdata(gpointer data) {
4775        aim_session_t *sess = data;
4776        aim_ssi_reqdata(sess);
4777        return FALSE;
4778}
4779
4780static int gaim_ssi_parseerr(aim_session_t *sess, aim_frame_t *fr, ...) {
4781        GaimConnection *gc = sess->aux_data;
4782        struct oscar_data *od = gc->proto_data;
4783        va_list ap;
4784        fu16_t reason;
4785
4786        va_start(ap, fr);
4787        reason = (fu16_t)va_arg(ap, unsigned int);
4788        va_end(ap);
4789
4790        gaim_debug(GAIM_DEBUG_ERROR, "oscar", "ssi: SNAC error %hu\n", reason);
4791
4792        if (reason == 0x0005) {
4793                gaim_notify_error(gc, NULL, _("Unable To Retrieve Buddy List"),
4794                                                  _("Gaim was temporarily unable to retrieve your buddy list from the AIM servers.  Your buddy list is not lost, and will probably become available in a few hours."));
4795                od->getblisttimer = g_timeout_add(300000, gaim_ssi_rerequestdata, od->sess);
4796        }
4797
4798        /* Activate SSI */
4799        /* Sending the enable causes other people to be able to see you, and you to see them */
4800        /* Make sure your privacy setting/invisibility is set how you want it before this! */
4801        gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: activating server-stored buddy list\n");
4802        aim_ssi_enable(od->sess);
4803
4804        return 1;
4805}
4806
4807static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
4808        GaimConnection *gc = sess->aux_data;
4809        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4810        int numtypes, i;
4811        fu16_t *maxitems;
4812        va_list ap;
4813
4814        va_start(ap, fr);
4815        numtypes = va_arg(ap, int);
4816        maxitems = va_arg(ap, fu16_t *);
4817        va_end(ap);
4818
4819        gaim_debug(GAIM_DEBUG_MISC, "oscar", "ssi rights:");
4820
4821        for (i=0; i<numtypes; i++)
4822                gaim_debug(GAIM_DEBUG_MISC, NULL, " max type 0x%04x=%hd,",
4823                                   i, maxitems[i]);
4824
4825        gaim_debug(GAIM_DEBUG_MISC, NULL, "\n");
4826
4827        if (numtypes >= 0)
4828                od->rights.maxbuddies = maxitems[0];
4829        if (numtypes >= 1)
4830                od->rights.maxgroups = maxitems[1];
4831        if (numtypes >= 2)
4832                od->rights.maxpermits = maxitems[2];
4833        if (numtypes >= 3)
4834                od->rights.maxdenies = maxitems[3];
4835
4836        return 1;
4837}
4838
4839static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
4840        GaimConnection *gc = sess->aux_data;
4841        GaimAccount *account = gaim_connection_get_account(gc);
4842        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
4843        struct aim_ssi_item *curitem;
4844        int tmp;
4845        gboolean export = FALSE;
4846        /* XXX - use these?
4847        va_list ap;
4848
4849        va_start(ap, fr);
4850        fmtver = (fu16_t)va_arg(ap, int);
4851        numitems = (fu16_t)va_arg(ap, int);
4852        items = va_arg(ap, struct aim_ssi_item);
4853        timestamp = va_arg(ap, fu32_t);
4854        va_end(ap); */
4855
4856        gaim_debug(GAIM_DEBUG_INFO, "oscar",
4857                           "ssi: syncing local list and server list\n");
4858
4859        /* Clean the buddy list */
4860        aim_ssi_cleanlist(sess);
4861
4862        /* Add from server list to local list */
4863        for (curitem=sess->ssi.local; curitem; curitem=curitem->next) {
4864                switch (curitem->type) {
4865                        case 0x0000: { /* Buddy */
4866                                if (curitem->name) {
4867                                        char *gname = aim_ssi_itemlist_findparentname(sess->ssi.local, curitem->name);
4868                                        char *gname_utf8 = gaim_try_conv_to_utf8(gname);
4869                                        char *alias = aim_ssi_getalias(sess->ssi.local, gname, curitem->name);
4870                                        char *alias_utf8 = gaim_try_conv_to_utf8(alias);
4871                                        GaimBuddy *buddy = gaim_find_buddy(gc->account, curitem->name);
4872                                        /* Should gname be freed here? -- elb */
4873                                        /* Not with the current code, but that might be cleaner -- med */
4874                                        free(alias);
4875                                        if (buddy) {
4876                                                /* Get server stored alias */
4877                                                if (alias_utf8) {
4878                                                        g_free(buddy->alias);
4879                                                        buddy->alias = g_strdup(alias_utf8);
4880                                                }
4881                                        } else {
4882                                                GaimGroup *g;
4883                                                buddy = gaim_buddy_new(gc->account, curitem->name, alias_utf8);
4884
4885                                                if (!(g = gaim_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) {
4886                                                        g = gaim_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
4887                                                        gaim_blist_add_group(g, NULL);
4888                                                }
4889
4890                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4891                                                                   "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname_utf8 ? gname_utf8 : _("Orphans"));
4892                                                gaim_blist_add_buddy(buddy, NULL, g, NULL);
4893                                                export = TRUE;
4894                                        }
4895                                        free(gname_utf8);
4896                                        free(alias_utf8);
4897                                }
4898                        } break;
4899
4900                        case 0x0001: { /* Group */
4901                                /* Shouldn't add empty groups */
4902                        } break;
4903
4904                        case 0x0002: { /* Permit buddy */
4905                                if (curitem->name) {
4906                                        /* if (!find_permdeny_by_name(gc->permit, curitem->name)) { AAA */
4907                                        GSList *list;
4908                                        for (list=account->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
4909                                        if (!list) {
4910                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4911                                                                   "ssi: adding permit buddy %s to local list\n", curitem->name);
4912                                                gaim_privacy_permit_add(account, curitem->name, TRUE);
4913                                                export = TRUE;
4914                                        }
4915                                }
4916                        } break;
4917
4918                        case 0x0003: { /* Deny buddy */
4919                                if (curitem->name) {
4920                                        GSList *list;
4921                                        for (list=account->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
4922                                        if (!list) {
4923                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4924                                                                   "ssi: adding deny buddy %s to local list\n", curitem->name);
4925                                                gaim_privacy_deny_add(account, curitem->name, TRUE);
4926                                                export = TRUE;
4927                                        }
4928                                }
4929                        } break;
4930
4931                        case 0x0004: { /* Permit/deny setting */
4932                                if (curitem->data) {
4933                                        fu8_t permdeny;
4934                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.local)) && (permdeny != account->perm_deny)) {
4935                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4936                                                                   "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny);
4937                                                account->perm_deny = permdeny;
4938                                                if (od->icq && account->perm_deny == 0x03) {
4939                                                        serv_set_away(gc, "Invisible", "");
4940                                                }
4941                                                export = TRUE;
4942                                        }
4943                                }
4944                        } break;
4945
4946                        case 0x0005: { /* Presence setting */
4947                                /* We don't want to change Gaim's setting because it applies to all accounts */
4948                        } break;
4949                } /* End of switch on curitem->type */
4950        } /* End of for loop */
4951
4952        /* If changes were made, then flush buddy list to file */
4953        if (export)
4954                gaim_blist_save();
4955
4956        { /* Add from local list to server list */
4957                GaimBlistNode *gnode, *cnode, *bnode;
4958                GaimGroup *group;
4959                GaimBuddy *buddy;
4960                struct gaim_buddy_list *blist;
4961                GSList *cur;
4962
4963                /* Buddies */
4964                if ((blist = gaim_get_blist()))
4965                        for (gnode = blist->root; gnode; gnode = gnode->next) {
4966                                if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
4967                                        continue;
4968                                group = (GaimGroup *)gnode;
4969                                for (cnode = gnode->child; cnode; cnode = cnode->next) {
4970                                        if(!GAIM_BLIST_NODE_IS_CONTACT(cnode))
4971                                                continue;
4972                                        for (bnode = cnode->child; bnode; bnode = bnode->next) {
4973                                                if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
4974                                                        continue;
4975                                                buddy = (GaimBuddy *)bnode;
4976                                                if (buddy->account == gc->account) {
4977                                                        gchar *servernick = gaim_buddy_get_setting(buddy, "servernick");
4978                                                        if (servernick) {
4979                                                                serv_got_alias(gc, buddy->name, servernick);
4980                                                                g_free(servernick);
4981                                                        }
4982                                                        if (aim_ssi_itemlist_exists(sess->ssi.local, buddy->name)) {
4983                                                                /* Store local alias on server */
4984                                                                char *alias = aim_ssi_getalias(sess->ssi.local, group->name, buddy->name);
4985                                                                if (!alias && buddy->alias && strlen(buddy->alias))
4986                                                                        aim_ssi_aliasbuddy(sess, group->name, buddy->name, buddy->alias);
4987                                                                free(alias);
4988                                                        } else {
4989                                                                gaim_debug(GAIM_DEBUG_INFO, "oscar",
4990                                                                                "ssi: adding buddy %s from local list to server list\n", buddy->name);
4991                                                                aim_ssi_addbuddy(sess, buddy->name, group->name, gaim_get_buddy_alias_only(buddy), NULL, NULL, 0);
4992                                                        }
4993                                                }
4994                                        }
4995                                }
4996                        }
4997
4998                /* Permit list */
4999                if (gc->account->permit) {
5000                        for (cur=gc->account->permit; cur; cur=cur->next)
5001                                if (!aim_ssi_itemlist_finditem(sess->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) {
5002                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5003                                                        "ssi: adding permit %s from local list to server list\n", (char *)cur->data);
5004                                        aim_ssi_addpermit(sess, cur->data);
5005                                }
5006                }
5007
5008                /* Deny list */
5009                if (gc->account->deny) {
5010                        for (cur=gc->account->deny; cur; cur=cur->next)
5011                                if (!aim_ssi_itemlist_finditem(sess->ssi.local, NULL, cur->data, AIM_SSI_TYPE_DENY)) {
5012                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5013                                                        "ssi: adding deny %s from local list to server list\n", (char *)cur->data);
5014                                        aim_ssi_adddeny(sess, cur->data);
5015                                }
5016                }
5017                /* Presence settings (idle time visibility) */
5018                if ((tmp = aim_ssi_getpresence(sess->ssi.local)) != 0xFFFFFFFF)
5019                        if (!(tmp & 0x400))
5020                                aim_ssi_setpresence(sess, tmp | 0x400);
5021        } /* end adding buddies from local list to server list */
5022
5023        /* Set our ICQ status */
5024        if  (od->icq && !gc->away) {
5025                aim_setextstatus(sess, AIM_ICQ_STATE_NORMAL);
5026        }
5027
5028        /* Activate SSI */
5029        /* Sending the enable causes other people to be able to see you, and you to see them */
5030        /* Make sure your privacy setting/invisibility is set how you want it before this! */
5031        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5032                           "ssi: activating server-stored buddy list\n");
5033        aim_ssi_enable(sess);
5034
5035        return 1;
5036}
5037
5038static int gaim_ssi_parseack(aim_session_t *sess, aim_frame_t *fr, ...) {
5039        GaimConnection *gc = sess->aux_data;
5040        va_list ap;
5041        struct aim_ssi_tmp *retval;
5042
5043        va_start(ap, fr);
5044        retval = va_arg(ap, struct aim_ssi_tmp *);
5045        va_end(ap);
5046
5047        while (retval) {
5048                gaim_debug(GAIM_DEBUG_MISC, "oscar",
5049                                   "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack,  retval->action, retval->item ? (retval->item->name ? retval->item->name : "no name") : "no item");
5050
5051                if (retval->ack != 0xffff)
5052                switch (retval->ack) {
5053                        case 0x0000: { /* added successfully */
5054                        } break;
5055
5056                        case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */
5057                                gchar *buf;
5058                                buf = g_strdup_printf(_("Could not add the buddy %s because you have too many buddies in your buddy list.  Please remove one and try again."), (retval->name ? retval->name : _("(no name)")));
5059                                gaim_notify_error(gc, NULL, _("Unable To Add"), buf);
5060                                g_free(buf);
5061                        }
5062
5063                        case 0x000e: { /* contact requires authorization */
5064                                if ((retval->action == AIM_CB_SSI_ADD) && (retval->name))
5065                                        gaim_auth_sendrequest(gc, retval->name);
5066                        } break;
5067
5068                        default: { /* La la la */
5069                                gchar *buf;
5070                                gaim_debug(GAIM_DEBUG_ERROR, "oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack);
5071                                buf = g_strdup_printf(_("Could not add the buddy %s for an unknown reason.  The most common reason for this is that you have the maximum number of allowed buddies in your buddy list."), (retval->name ? retval->name : _("(no name)")));
5072                                gaim_notify_error(gc, NULL, _("Unable To Add"), buf);
5073                                g_free(buf);
5074                                /* XXX - Should remove buddy from local list */
5075                        } break;
5076                }
5077
5078                retval = retval->next;
5079        }
5080
5081        return 1;
5082}
5083
5084static int gaim_ssi_authgiven(aim_session_t *sess, aim_frame_t *fr, ...) {
5085        GaimConnection *gc = sess->aux_data;
5086        va_list ap;
5087        char *sn, *msg;
5088        gchar *dialog_msg, *nombre;
5089        struct name_data *data;
5090        GaimBuddy *buddy;
5091
5092        va_start(ap, fr);
5093        sn = va_arg(ap, char *);
5094        msg = va_arg(ap, char *);
5095        va_end(ap);
5096
5097        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5098                           "ssi: %s has given you permission to add him to your buddy list\n", sn);
5099
5100        buddy = gaim_find_buddy(gc->account, sn);
5101        if (buddy && (gaim_get_buddy_alias_only(buddy)))
5102                nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy));
5103        else
5104                nombre = g_strdup(sn);
5105
5106        dialog_msg = g_strdup_printf(_("The user %s has given you permission to add you to their buddy list.  Do you want to add them?"), nombre);
5107        data = g_new(struct name_data, 1);
5108        data->gc = gc;
5109        data->name = g_strdup(sn);
5110        data->nick = NULL;
5111
5112        gaim_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg,
5113                                                0, data,
5114                                                G_CALLBACK(gaim_icq_contactadd),
5115                                                G_CALLBACK(oscar_free_name_data));
5116
5117        g_free(dialog_msg);
5118        g_free(nombre);
5119
5120        return 1;
5121}
5122
5123static int gaim_ssi_authrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
5124        GaimConnection *gc = sess->aux_data;
5125        va_list ap;
5126        char *sn, *msg;
5127        gchar *dialog_msg, *nombre;
5128        struct name_data *data;
5129        GaimBuddy *buddy;
5130
5131        va_start(ap, fr);
5132        sn = va_arg(ap, char *);
5133        msg = va_arg(ap, char *);
5134        va_end(ap);
5135
5136        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5137                           "ssi: received authorization request from %s\n", sn);
5138
5139        buddy = gaim_find_buddy(gc->account, sn);
5140        if (buddy && (gaim_get_buddy_alias_only(buddy)))
5141                nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy));
5142        else
5143                nombre = g_strdup(sn);
5144
5145        dialog_msg = g_strdup_printf(_("The user %s wants to add you to their buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given."));
5146        data = g_new(struct name_data, 1);
5147        data->gc = gc;
5148        data->name = g_strdup(sn);
5149        data->nick = NULL;
5150
5151        gaim_request_action(gc, NULL, _("Authorization Request"), dialog_msg,
5152                                                0, data, 2,
5153                                                _("Authorize"), G_CALLBACK(gaim_auth_grant),
5154                                                _("Deny"), G_CALLBACK(gaim_auth_dontgrant_msgprompt));
5155
5156        g_free(dialog_msg);
5157        g_free(nombre);
5158
5159        return 1;
5160}
5161
5162static int gaim_ssi_authreply(aim_session_t *sess, aim_frame_t *fr, ...) {
5163        GaimConnection *gc = sess->aux_data;
5164        va_list ap;
5165        char *sn, *msg;
5166        gchar *dialog_msg, *nombre;
5167        fu8_t reply;
5168        GaimBuddy *buddy;
5169
5170        va_start(ap, fr);
5171        sn = va_arg(ap, char *);
5172        reply = (fu8_t)va_arg(ap, int);
5173        msg = va_arg(ap, char *);
5174        va_end(ap);
5175
5176        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5177                           "ssi: received authorization reply from %s.  Reply is 0x%04hhx\n", sn, reply);
5178
5179        buddy = gaim_find_buddy(gc->account, sn);
5180        if (buddy && (gaim_get_buddy_alias_only(buddy)))
5181                nombre = g_strdup_printf("%s (%s)", sn, gaim_get_buddy_alias_only(buddy));
5182        else
5183                nombre = g_strdup(sn);
5184
5185        if (reply) {
5186                /* Granted */
5187                dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your contact list."), nombre);
5188                gaim_notify_info(gc, NULL, _("Authorization Granted"), dialog_msg);
5189        } else {
5190                /* Denied */
5191                dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your contact list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given."));
5192                gaim_notify_info(gc, NULL, _("Authorization Denied"), dialog_msg);
5193        }
5194        g_free(dialog_msg);
5195        g_free(nombre);
5196
5197        return 1;
5198}
5199
5200static int gaim_ssi_gotadded(aim_session_t *sess, aim_frame_t *fr, ...) {
5201        GaimConnection *gc = sess->aux_data;
5202        va_list ap;
5203        char *sn;
5204        GaimBuddy *buddy;
5205
5206        va_start(ap, fr);
5207        sn = va_arg(ap, char *);
5208        va_end(ap);
5209
5210        buddy = gaim_find_buddy(gc->account, sn);
5211        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5212                           "ssi: %s added you to their buddy list\n", sn);
5213        show_got_added(gc, NULL, sn, (buddy ? gaim_get_buddy_alias_only(buddy) : NULL), NULL);
5214
5215        return 1;
5216}
5217#endif
5218
5219static GList *oscar_chat_info(GaimConnection *gc) {
5220        GList *m = NULL;
5221        struct proto_chat_entry *pce;
5222
5223        pce = g_new0(struct proto_chat_entry, 1);
5224        pce->label = _("Join what group:");
5225        pce->identifier = "room";
5226        m = g_list_append(m, pce);
5227
5228        pce = g_new0(struct proto_chat_entry, 1);
5229        pce->label = _("Exchange:");
5230        pce->identifier = "exchange";
5231        pce->is_int = TRUE;
5232        pce->min = 4;
5233        pce->max = 20;
5234        m = g_list_append(m, pce);
5235
5236        return m;
5237}
5238
5239static void oscar_join_chat(GaimConnection *g, GHashTable *data) {
5240        struct oscar_data *od = (struct oscar_data *)g->proto_data;
5241        aim_conn_t *cur;
5242        char *name, *exchange;
5243
5244        name = g_hash_table_lookup(data, "room");
5245        exchange = g_hash_table_lookup(data, "exchange");
5246
5247        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5248                           "Attempting to join chat room %s.\n", name);
5249        if ((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
5250                gaim_debug(GAIM_DEBUG_INFO, "oscar",
5251                                   "chatnav exists, creating room\n");
5252                aim_chatnav_createroom(od->sess, cur, name, atoi(exchange));
5253        } else {
5254                /* this gets tricky */
5255                struct create_room *cr = g_new0(struct create_room, 1);
5256                gaim_debug(GAIM_DEBUG_INFO, "oscar",
5257                                   "chatnav does not exist, opening chatnav\n");
5258                cr->exchange = atoi(exchange);
5259                cr->name = g_strdup(name);
5260                od->create_rooms = g_slist_append(od->create_rooms, cr);
5261                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
5262        }
5263}
5264
5265static void oscar_chat_invite(GaimConnection *g, int id, const char *message, const char *name) {
5266        struct oscar_data *od = (struct oscar_data *)g->proto_data;
5267        struct chat_connection *ccon = find_oscar_chat(g, id);
5268       
5269        if (!ccon)
5270                return;
5271       
5272        aim_chat_invite(od->sess, od->conn, name, message ? message : "",
5273                        ccon->exchange, ccon->name, 0x0);
5274}
5275
5276static void oscar_chat_leave(GaimConnection *g, int id) {
5277        struct oscar_data *od = g ? (struct oscar_data *)g->proto_data : NULL;
5278        GSList *bcs = g->buddy_chats;
5279        GaimConversation *b = NULL;
5280        struct chat_connection *c = NULL;
5281        int count = 0;
5282
5283        while (bcs) {
5284                count++;
5285                b = (GaimConversation *)bcs->data;
5286                if (id == gaim_chat_get_id(GAIM_CHAT(b)))
5287                        break;
5288                bcs = bcs->next;
5289                b = NULL;
5290        }
5291
5292        if (!b)
5293                return;
5294
5295        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5296                           "Attempting to leave room %s (currently in %d rooms)\n", b->name, count);
5297       
5298        c = find_oscar_chat(g, gaim_chat_get_id(GAIM_CHAT(b)));
5299        if (c != NULL) {
5300                if (od)
5301                        od->oscar_chats = g_slist_remove(od->oscar_chats, c);
5302                if (c->inpa > 0)
5303                        gaim_input_remove(c->inpa);
5304                if (g && od->sess)
5305                        aim_conn_kill(od->sess, &c->conn);
5306                g_free(c->name);
5307                g_free(c->show);
5308                g_free(c);
5309        }
5310        /* we do this because with Oscar it doesn't tell us we left */
5311        serv_got_chat_left(g, gaim_chat_get_id(GAIM_CHAT(b)));
5312}
5313
5314static int oscar_chat_send(GaimConnection *g, int id, const char *message) {
5315        struct oscar_data *od = (struct oscar_data *)g->proto_data;
5316        GSList *bcs = g->buddy_chats;
5317        GaimConversation *b = NULL;
5318        struct chat_connection *c = NULL;
5319        char *buf, *buf2;
5320        int i, j;
5321
5322        while (bcs) {
5323                b = (GaimConversation *)bcs->data;
5324                if (id == gaim_chat_get_id(GAIM_CHAT(b)))
5325                        break;
5326                bcs = bcs->next;
5327                b = NULL;
5328        }
5329        if (!b)
5330                return -EINVAL;
5331
5332        bcs = od->oscar_chats;
5333        while (bcs) {
5334                c = (struct chat_connection *)bcs->data;
5335                if (b == c->cnv)
5336                        break;
5337                bcs = bcs->next;
5338                c = NULL;
5339        }
5340        if (!c)
5341                return -EINVAL;
5342
5343        buf = g_malloc(strlen(message) * 4 + 1);
5344        for (i = 0, j = 0; i < strlen(message); i++) {
5345                if (message[i] == '\n') {
5346                        buf[j++] = '<';
5347                        buf[j++] = 'B';
5348                        buf[j++] = 'R';
5349                        buf[j++] = '>';
5350                } else {
5351                        buf[j++] = message[i];
5352                }
5353        }
5354        buf[j] = '\0';
5355
5356        if (strlen(buf) > c->maxlen)
5357                return -E2BIG;
5358
5359        buf2 = strip_html(buf);
5360        if (strlen(buf2) > c->maxvis) {
5361                g_free(buf2);
5362                return -E2BIG;
5363        }
5364        g_free(buf2);
5365
5366        aim_chat_send_im(od->sess, c->conn, 0, buf, strlen(buf));
5367        g_free(buf);
5368        return 0;
5369}
5370
5371static const char *oscar_list_icon(GaimAccount *a, GaimBuddy *b) {
5372        if (!b || (b && b->name && b->name[0] == '+')) {
5373                if (a != NULL && isdigit(*gaim_account_get_username(a)))
5374                        return "icq";
5375                else
5376                        return "aim";
5377        }
5378
5379        if (b != NULL && isdigit(b->name[0]))
5380                return "icq";
5381        return "aim";
5382}
5383
5384static void oscar_list_emblems(GaimBuddy *b, char **se, char **sw, char **nw, char **ne)
5385{
5386        char *emblems[4] = {NULL,NULL,NULL,NULL};
5387        int i = 0;
5388
5389        if (!GAIM_BUDDY_IS_ONLINE(b)) {
5390                GaimAccount *account;
5391                GaimConnection *gc;
5392                struct oscar_data *od;
5393                char *gname;
5394                if ((b->name) && (account = b->account) && (gc = account->gc) && 
5395                        (od = gc->proto_data) && (od->sess->ssi.received_data) && 
5396                        (gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, b->name)) && 
5397                        (aim_ssi_waitingforauth(od->sess->ssi.local, gname, b->name))) {
5398                        emblems[i++] = "notauthorized";
5399                } else {
5400                        emblems[i++] = "offline";
5401                }
5402        }
5403
5404        if (b->name && (b->uc & 0xffff0000) && isdigit(b->name[0])) {
5405                int uc = b->uc >> 16;
5406                if (uc & AIM_ICQ_STATE_INVISIBLE)
5407                        emblems[i++] = "invisible";
5408                else if (uc & AIM_ICQ_STATE_CHAT)
5409                        emblems[i++] = "freeforchat";
5410                else if (uc & AIM_ICQ_STATE_DND)
5411                        emblems[i++] = "dnd";
5412                else if (uc & AIM_ICQ_STATE_OUT)
5413                        emblems[i++] = "na";
5414                else if (uc & AIM_ICQ_STATE_BUSY)
5415                        emblems[i++] = "occupied";
5416                else if (uc & AIM_ICQ_STATE_AWAY)
5417                        emblems[i++] = "away";
5418        } else {
5419                if (b->uc & UC_UNAVAILABLE) 
5420                        emblems[i++] = "away";
5421        }
5422        if (b->uc & UC_WIRELESS)
5423                emblems[i++] = "wireless";
5424        if (b->uc & UC_AOL)
5425                emblems[i++] = "aol";
5426        if (b->uc & UC_ADMIN)
5427                emblems[i++] = "admin";
5428        if (b->uc & UC_AB && i < 4)
5429                emblems[i++] = "activebuddy";
5430        if (b->uc & UC_HIPTOP && i < 4)
5431                emblems[i++] = "hiptop";
5432/*      if (b->uc & UC_UNCONFIRMED && i < 4)
5433                emblems[i++] = "unconfirmed"; */
5434        *se = emblems[0];
5435        *sw = emblems[1];
5436        *nw = emblems[2];
5437        *ne = emblems[3];
5438}
5439
5440static char *oscar_tooltip_text(GaimBuddy *b) {
5441        GaimConnection *gc = b->account->gc;
5442        struct oscar_data *od = gc->proto_data;
5443        struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, normalize(b->name));
5444        gchar *tmp, *yay = g_strdup("");
5445
5446        if (GAIM_BUDDY_IS_ONLINE(b)) {
5447                if (isdigit(b->name[0])) {
5448                        char *tmp, *status;
5449                        status = gaim_icq_status((b->uc & 0xffff0000) >> 16);
5450                        tmp = yay;
5451                        yay = g_strconcat(tmp, _("<b>Status:</b> "), status, "\n", NULL);
5452                        g_free(tmp);
5453                        g_free(status);
5454                }
5455
5456                if (bi) {
5457                        char *tstr = sec_to_text(time(NULL) - bi->signon + 
5458                                (gc->login_time_official ? gc->login_time_official - gc->login_time : 0));
5459                        tmp = yay;
5460                        yay = g_strconcat(tmp, _("<b>Logged In:</b> "), tstr, "\n", NULL);
5461                        free(tmp);
5462                        free(tstr);
5463
5464                        if (bi->ipaddr) {
5465                                char *tstr =  g_strdup_printf("%hhd.%hhd.%hhd.%hhd",
5466                                                                (bi->ipaddr & 0xff000000) >> 24,
5467                                                                (bi->ipaddr & 0x00ff0000) >> 16,
5468                                                                (bi->ipaddr & 0x0000ff00) >> 8,
5469                                                                (bi->ipaddr & 0x000000ff));
5470                                tmp = yay;
5471                                yay = g_strconcat(tmp, _("<b>IP Address:</b> "), tstr, "\n", NULL);
5472                                free(tmp);
5473                                free(tstr);
5474                        }
5475
5476                        if (bi->caps) {
5477                                char *caps = caps_string(bi->caps);
5478                                tmp = yay;
5479                                yay = g_strconcat(tmp, _("<b>Capabilities:</b> "), caps, "\n", NULL);
5480                                free(tmp);
5481                        }
5482
5483                        if (bi->availmsg && !(b->uc & UC_UNAVAILABLE)) {
5484                                gchar *escaped = g_markup_escape_text(bi->availmsg, strlen(bi->availmsg));
5485                                tmp = yay;
5486                                yay = g_strconcat(tmp, _("<b>Available:</b> "), escaped, "\n", NULL);
5487                                free(tmp);
5488                                g_free(escaped);
5489                        }
5490                }
5491        } else {
5492                char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, b->name);
5493                if (aim_ssi_waitingforauth(od->sess->ssi.local, gname, b->name)) {
5494                        tmp = yay;
5495                        yay = g_strconcat(tmp, _("<b>Status:</b> Not Authorized"), "\n", NULL);
5496                        g_free(tmp);
5497                } else {
5498                        tmp = yay;
5499                        yay = g_strconcat(tmp, _("<b>Status:</b> Offline"), "\n", NULL);
5500                        g_free(tmp);
5501                }
5502        }
5503
5504        /* remove the trailing newline character */
5505        if (yay)
5506                yay[strlen(yay)-1] = '\0';
5507        return yay;
5508}
5509
5510static char *oscar_status_text(GaimBuddy *b) {
5511        GaimConnection *gc = b->account->gc;
5512        struct oscar_data *od = gc->proto_data;
5513        gchar *ret = NULL;
5514
5515        if ((b->uc & UC_UNAVAILABLE) || (((b->uc & 0xffff0000) >> 16) & AIM_ICQ_STATE_CHAT)) {
5516                if (isdigit(b->name[0]))
5517                        ret = gaim_icq_status((b->uc & 0xffff0000) >> 16);
5518                else
5519                        ret = g_strdup(_("Away"));
5520        } else if (GAIM_BUDDY_IS_ONLINE(b)) {
5521                struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, normalize(b->name));
5522                if (bi->availmsg)
5523                        ret = g_markup_escape_text(bi->availmsg, strlen(bi->availmsg));
5524        } else {
5525                char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, b->name);
5526                if (aim_ssi_waitingforauth(od->sess->ssi.local, gname, b->name))
5527                        ret = g_strdup(_("Not Authorized"));
5528                else
5529                        ret = g_strdup(_("Offline"));
5530        }
5531
5532        return ret;
5533}
5534
5535
5536static int oscar_icon_req(aim_session_t *sess, aim_frame_t *fr, ...) {
5537        GaimConnection *gc = sess->aux_data;
5538        struct oscar_data *od = gc->proto_data;
5539        va_list ap;
5540        fu16_t type;
5541        fu8_t flags = 0, length = 0;
5542        char *md5 = NULL;
5543
5544        va_start(ap, fr);
5545        type = va_arg(ap, int);
5546
5547        switch(type) {
5548                case 0x0000:
5549                case 0x0001: {
5550                        flags = va_arg(ap, int);
5551                        length = va_arg(ap, int);
5552                        md5 = va_arg(ap, char *);
5553
5554                        if (flags == 0x41) {
5555                                if (!aim_getconn_type(od->sess, AIM_CONN_TYPE_ICON) && !od->iconconnecting) {
5556                                        od->iconconnecting = TRUE;
5557                                        od->set_icon = TRUE;
5558                                        aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_ICON);
5559                                } else {
5560                                        struct stat st;
5561                                        const char *iconfile = gaim_account_get_buddy_icon(gaim_connection_get_account(gc));
5562                                        if (iconfile == NULL) {
5563                                                /* Set an empty icon, or something */
5564                                        } else if (!stat(iconfile, &st)) {
5565                                                char *buf = g_malloc(st.st_size);
5566                                                FILE *file = fopen(iconfile, "rb");
5567                                                if (file) {
5568                                                        fread(buf, 1, st.st_size, file);
5569                                                        fclose(file);
5570                                                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5571                                                                   "Uploading icon to icon server\n");
5572                                                        aim_bart_upload(od->sess, buf, st.st_size);
5573                                                } else
5574                                                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
5575                                                                   "Can't open buddy icon file!\n");
5576                                                g_free(buf);
5577                                        } else {
5578                                                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
5579                                                           "Can't stat buddy icon file!\n");
5580                                        }
5581                                }
5582                        } else if (flags == 0x81)
5583                                aim_ssi_seticon(od->sess, md5, length); 
5584                } break;
5585
5586                case 0x0002: { /* We just set an "available" message? */
5587                } break;
5588        }
5589
5590        va_end(ap);
5591
5592        return 0;
5593}
5594
5595/*
5596 * We have just established a socket with the other dude, so set up some handlers.
5597 */
5598static int gaim_odc_initiate(aim_session_t *sess, aim_frame_t *fr, ...) {
5599        GaimConnection *gc = sess->aux_data;
5600        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
5601        GaimConversation *cnv;
5602        struct direct_im *dim;
5603        char buf[256];
5604        char *sn;
5605        va_list ap;
5606        aim_conn_t *newconn, *listenerconn;
5607
5608        va_start(ap, fr);
5609        newconn = va_arg(ap, aim_conn_t *);
5610        listenerconn = va_arg(ap, aim_conn_t *);
5611        va_end(ap);
5612
5613        aim_conn_close(listenerconn);
5614        aim_conn_kill(sess, &listenerconn);
5615
5616        sn = g_strdup(aim_odc_getsn(newconn));
5617
5618        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5619                           "DirectIM: initiate success to %s\n", sn);
5620        dim = find_direct_im(od, sn);
5621
5622        cnv = gaim_conversation_new(GAIM_CONV_IM, dim->gc->account, sn);
5623        gaim_input_remove(dim->watcher);
5624        dim->conn = newconn;
5625        dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ, oscar_callback, dim->conn);
5626        dim->connected = TRUE;
5627        g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), sn);
5628        g_free(sn);
5629        gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
5630
5631        aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0);
5632        aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0);
5633        aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0);
5634
5635        return 1;
5636}
5637
5638/*
5639 * This is called when each chunk of an image is received.  It can be used to
5640 * update a progress bar, or to eat lots of dry cat food.  Wet cat food is
5641 * nasty, you sicko.
5642 */
5643static int gaim_odc_update_ui(aim_session_t *sess, aim_frame_t *fr, ...) {
5644        va_list ap;
5645        char *sn;
5646        double percent;
5647        GaimConnection *gc = sess->aux_data;
5648        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
5649        GaimConversation *c;
5650        struct direct_im *dim;
5651
5652        va_start(ap, fr);
5653        sn = va_arg(ap, char *);
5654        percent = va_arg(ap, double);
5655        va_end(ap);
5656
5657        if (!(dim = find_direct_im(od, sn)))
5658                return 1;
5659        if (dim->watcher) {
5660                gaim_input_remove(dim->watcher);   /* Otherwise, the callback will callback */
5661                dim->watcher = 0;
5662        }
5663        /* XXX is this really necessary? */
5664        while (gtk_events_pending())
5665                gtk_main_iteration();
5666
5667        c = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc));
5668        if (c != NULL)
5669                gaim_conversation_update_progress(c, percent);
5670        dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
5671                                      oscar_callback, dim->conn);
5672
5673        return 1;
5674}
5675
5676/*
5677 * This is called after a direct IM has been received in its entirety.  This
5678 * function is passed a long chunk of data which contains the IM with any
5679 * data chunks (images) appended to it.
5680 *
5681 * This function rips out all the data chunks and creates an imgstore for
5682 * each one.  In order to do this, it first goes through the IM and takes
5683 * out all the IMG tags.  When doing so, it rewrites the original IMG tag
5684 * with one compatable with the imgstore Gaim core code. For each one, we
5685 * then read in chunks of data from the end of the message and actually
5686 * create the img store using the given data.
5687 *
5688 * For somewhat easy reference, here's a sample message
5689 * (without the whitespace and asterisks):
5690 *
5691 * <HTML><BODY BGCOLOR="#ffffff">
5692 *     <FONT LANG="0">
5693 *     This is a really stupid picture:<BR>
5694 *     <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR>
5695 *     Yeah it is<BR>
5696 *     Here is another one:<BR>
5697 *     <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978">   
5698 *     </FONT>
5699 * </BODY></HTML>
5700 * <BINARY>
5701 *     <DATA ID="1" SIZE="9894">datadatadatadata</DATA>
5702 *     <DATA ID="2" SIZE="65978">datadatadatadata</DATA>
5703 * </BINARY>
5704 */
5705static int gaim_odc_incoming(aim_session_t *sess, aim_frame_t *fr, ...) {
5706        GaimConnection *gc = sess->aux_data;
5707        GaimImFlags imflags = 0;
5708        GString *newmsg = g_string_new("");
5709        GSList *images = NULL;
5710        va_list ap;
5711        const char *sn, *msg, *msgend, *binary;
5712        size_t len;
5713        int encoding, isawaymsg;
5714
5715        va_start(ap, fr);
5716        sn = va_arg(ap, const char *);
5717        msg = va_arg(ap, const char *);
5718        len = va_arg(ap, size_t);
5719        encoding = va_arg(ap, int);
5720        isawaymsg = va_arg(ap, int);
5721        va_end(ap);
5722        msgend = msg + len;
5723
5724        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5725                           "Got DirectIM message from %s\n", sn);
5726
5727        if (isawaymsg)
5728                imflags |= GAIM_IM_AUTO_RESP;
5729
5730        /* message has a binary trailer */
5731        if ((binary = gaim_strcasestr(msg, "<binary>"))) {
5732                GData *attribs;
5733                const char *tmp, *start, *end, *last = NULL;
5734
5735                tmp = msg;
5736
5737                /* for each valid image tag... */
5738                while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
5739                        const char *id, *src, *datasize;
5740                        const char *tag = NULL, *data = NULL;
5741                        size_t size;
5742                        int imgid = 0;
5743
5744                        /* update the location of the last img tag */
5745                        last = end;
5746
5747                        /* grab attributes */
5748                        id       = g_datalist_get_data(&attribs, "id");
5749                        src      = g_datalist_get_data(&attribs, "src");
5750                        datasize = g_datalist_get_data(&attribs, "datasize");
5751
5752                        /* if we have id & datasize, build the data tag */
5753                        if (id && datasize)
5754                                tag = g_strdup_printf("<data id=\"%s\" size=\"%s\">", id, datasize);
5755
5756                        /* if we have a tag, find the start of the data */
5757                        if (tag && (data = gaim_strcasestr(binary, tag)))
5758                                data += strlen(tag);
5759
5760                        /* check the data is here and store it */
5761                        if (data + (size = atoi(datasize)) <= msgend)
5762                                imgid = gaim_imgstore_add(data, size, src);
5763
5764                        /* if we have a stored image... */
5765                        if (imgid) {
5766                                /* append the message up to the tag */
5767                                newmsg = g_string_append_len(newmsg, tmp, start - tmp);
5768
5769                                /* write the new image tag */
5770                                g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid);
5771
5772                                /* and record the image number */
5773                                images = g_slist_append(images, GINT_TO_POINTER(imgid));
5774                        } else {
5775                                /* otherwise, copy up to the end of the tag */
5776                                newmsg = g_string_append_len(newmsg, tmp, (end + 1) - tmp);
5777                        }
5778
5779                        /* clear the attribute list */
5780                        g_datalist_clear(&attribs);
5781
5782                        /* continue from the end of the tag */
5783                        tmp = end + 1;
5784                }
5785
5786                /* append any remaining message data (without the > :-) */
5787                if (last++ && (last < binary))
5788                        newmsg = g_string_append_len(newmsg, last, binary - last);
5789
5790                /* set the flag if we caught any images */
5791                if (images)
5792                        imflags |= GAIM_IM_IMAGES;
5793        } else {
5794                g_string_append_len(newmsg, msg, len);
5795        }
5796
5797        /* XXX - I imagine Paco-Paco will want to do some voodoo with the encoding here */
5798        serv_got_im(gc, sn, newmsg->str, imflags, time(NULL));
5799
5800        /* free up the message */
5801        g_string_free(newmsg, TRUE);
5802
5803        /* unref any images we allocated */
5804        if (images) {
5805                GSList *tmp;
5806                int id;
5807
5808                for (tmp = images; tmp != NULL; tmp = tmp->next) {
5809                        id = GPOINTER_TO_INT(tmp->data);
5810                        gaim_imgstore_unref(id);
5811                }
5812
5813                g_slist_free(images);
5814        }
5815
5816        return 1;
5817}
5818
5819static int gaim_odc_typing(aim_session_t *sess, aim_frame_t *fr, ...) {
5820        va_list ap;
5821        char *sn;
5822        int typing;
5823        GaimConnection *gc = sess->aux_data;
5824
5825        va_start(ap, fr);
5826        sn = va_arg(ap, char *);
5827        typing = va_arg(ap, int);
5828        va_end(ap);
5829
5830        if (typing == 0x0002) {
5831                /* I had to leave this. It's just too funny. It reminds me of my sister. */
5832                gaim_debug(GAIM_DEBUG_INFO, "oscar",
5833                                   "ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", sn);
5834                serv_got_typing(gc, sn, 0, GAIM_TYPING);
5835        } else if (typing == 0x0001)
5836                serv_got_typing(gc, sn, 0, GAIM_TYPED);
5837        else
5838                serv_got_typing_stopped(gc, sn);
5839        return 1;
5840}
5841
5842static int gaim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *message, GaimImFlags imflags) {
5843        char *buf;
5844        size_t len;
5845        int ret;
5846
5847        if (imflags & GAIM_IM_IMAGES) {
5848                GString *msg = g_string_new("");
5849                GString *data = g_string_new("<BINARY>");
5850                GData *attribs;
5851                const char *tmp, *start, *end, *last = NULL;
5852                int oscar_id = 0;
5853
5854                tmp = message;
5855
5856                /* for each valid IMG tag... */
5857                while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
5858                        GaimStoredImage *image = NULL;
5859                        const char *id;
5860
5861                        last = end;
5862                        id = g_datalist_get_data(&attribs, "id");
5863
5864                        /* ... if it refers to a valid gaim image ... */
5865                        if (id && (image = gaim_imgstore_get(atoi(id)))) {
5866                                /* ... append the message from start to the tag ... */
5867                                msg = g_string_append_len(msg, tmp, start - tmp);
5868                                oscar_id++;
5869
5870                                /* ... insert a new img tag with the oscar id ... */
5871                                if (image->filename)
5872                                        g_string_append_printf(msg,
5873                                                "<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%d\">",
5874                                                image->filename, oscar_id, image->size);
5875                                else
5876                                        g_string_append_printf(msg,
5877                                                "<IMG ID=\"%d\" DATASIZE=\"%d\">",
5878                                                oscar_id, image->size);
5879
5880                                /* ... and append the data to the binary section ... */
5881                                g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%d\">",
5882                                        oscar_id, image->size);
5883                                data = g_string_append_len(data, image->data, image->size);
5884                                data = g_string_append(data, "</DATA>");
5885                        } else {
5886                                /* ... otherwise, allow the possibly invalid img tag through. */
5887                                /* should we do something else? */
5888                                msg = g_string_append_len(msg, tmp, (end + 1) - tmp);
5889                        }
5890
5891                        g_datalist_clear(&attribs);
5892
5893                        /* continue from the end of the tag */
5894                        tmp = end + 1;
5895                }
5896
5897                /* append any remaining message data (without the > :-) */
5898                if (last++ && *last)
5899                        msg = g_string_append(msg, last);
5900
5901                /* if we inserted any images in the binary section, append it */
5902                if (oscar_id) {
5903                        msg = g_string_append_len(msg, data->str, data->len);
5904                        msg = g_string_append(msg, "</BINARY>");
5905                }
5906
5907                len = msg->len;
5908                buf = msg->str;
5909                g_string_free(msg, FALSE);
5910                g_string_free(data, TRUE);
5911        } else {
5912                len = strlen(message);
5913                buf = g_memdup(message, len+1);
5914        }
5915
5916        /* XXX - The last parameter below is the encoding.  Let Paco-Paco do something with it. */
5917        if (imflags & GAIM_IM_AUTO_RESP)
5918                ret =  aim_odc_send_im(sess, conn, buf, len, 0, 1);
5919        else
5920                ret =  aim_odc_send_im(sess, conn, buf, len, 0, 0);
5921
5922        g_free(buf);
5923
5924        return ret;
5925}
5926
5927struct ask_do_dir_im {
5928        char *who;
5929        GaimConnection *gc;
5930};
5931
5932static void oscar_cancel_direct_im(struct ask_do_dir_im *data) {
5933        g_free(data->who);
5934        g_free(data);
5935}
5936
5937static void oscar_direct_im(struct ask_do_dir_im *data) {
5938        GaimConnection *gc = data->gc;
5939        struct oscar_data *od;
5940        struct direct_im *dim;
5941
5942        if (!g_list_find(gaim_connections_get_all(), gc)) {
5943                g_free(data->who);
5944                g_free(data);
5945                return;
5946        }
5947
5948        od = (struct oscar_data *)gc->proto_data;
5949
5950        dim = find_direct_im(od, data->who);
5951        if (dim) {
5952                if (!(dim->connected)) {  /* We'll free the old, unconnected dim, and start over */
5953                        od->direct_ims = g_slist_remove(od->direct_ims, dim);
5954                        gaim_input_remove(dim->watcher);
5955                        g_free(dim);
5956                        gaim_debug(GAIM_DEBUG_INFO, "oscar",
5957                                           "Gave up on old direct IM, trying again\n");
5958                } else {
5959                        gaim_notify_error(gc, NULL, "DirectIM already open.", NULL);
5960                        g_free(data->who);
5961                        g_free(data);
5962                        return;
5963                }
5964        }
5965        dim = g_new0(struct direct_im, 1);
5966        dim->gc = gc;
5967        g_snprintf(dim->name, sizeof dim->name, "%s", data->who);
5968
5969        dim->conn = aim_odc_initiate(od->sess, data->who);
5970        if (dim->conn != NULL) {
5971                od->direct_ims = g_slist_append(od->direct_ims, dim);
5972                dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
5973                                                oscar_callback, dim->conn);
5974                aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED,
5975                                        gaim_odc_initiate, 0);
5976        } else {
5977                gaim_notify_error(gc, NULL, _("Unable to open Direct IM"), NULL);
5978                g_free(dim);
5979        }
5980
5981        g_free(data->who);
5982        g_free(data);
5983}
5984
5985static void oscar_ask_direct_im(GaimConnection *gc, const char *who) {
5986        gchar *buf;
5987        struct ask_do_dir_im *data = g_new0(struct ask_do_dir_im, 1);
5988        data->who = g_strdup(who);
5989        data->gc = gc;
5990        buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."), who);
5991
5992        gaim_request_action(gc, NULL, buf,
5993                                                _("Because this reveals your IP address, it "
5994                                                  "may be considered a privacy risk.  Do you "
5995                                                  "wish to continue?"),
5996                                                0, data, 2,
5997                                                _("Connect"), G_CALLBACK(oscar_direct_im),
5998                                                _("Cancel"), G_CALLBACK(oscar_cancel_direct_im));
5999        g_free(buf);
6000}
6001
6002static void oscar_set_permit_deny(GaimConnection *gc) {
6003        GaimAccount *account = gaim_connection_get_account(gc);
6004        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
6005#ifdef NOSSI
6006        GSList *list, *g = gaim_blist_groups(), *g1;
6007        char buf[MAXMSGLEN];
6008        int at;
6009
6010        switch(account->perm_deny) {
6011        case 1:
6012                aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gaim_account_get_username(account));
6013                break;
6014        case 2:
6015                aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gaim_account_get_username(account));
6016                break;
6017        case 3:
6018                list = account->permit;
6019                at = 0;
6020                while (list) {
6021                        at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
6022                        list = list->next;
6023                }
6024                aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
6025                break;
6026        case 4:
6027                list = account->deny;
6028                at = 0;
6029                while (list) {
6030                        at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
6031                        list = list->next;
6032                }
6033                aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
6034                break;
6035        case 5:
6036                g1 = g;
6037                at = 0;
6038                while (g1) {
6039                        list = gaim_blist_members((struct group *)g->data);
6040                        GSList list1 = list;
6041                        while (list1) {
6042                                struct buddy *b = list1->data;
6043                                if(b->account == account)
6044                                        at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", b->name);
6045                                list1 = list1->next;
6046                        }
6047                        g_slist_free(list);
6048                        g1 = g1->next;
6049                }
6050                g_slist_free(g);
6051                aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
6052                break;
6053        default:
6054                break;
6055        }
6056        signoff_blocked(gc);
6057#else
6058        if (od->sess->ssi.received_data)
6059                aim_ssi_setpermdeny(od->sess, account->perm_deny, 0xffffffff);
6060#endif
6061}
6062
6063static void oscar_add_permit(GaimConnection *gc, const char *who) {
6064#ifdef NOSSI
6065        if (gc->account->permdeny == 3)
6066                oscar_set_permit_deny(gc);
6067#else
6068        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
6069        gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: About to add a permit\n");
6070        if (od->sess->ssi.received_data)
6071                aim_ssi_addpermit(od->sess, who);
6072#endif
6073}
6074
6075static void oscar_add_deny(GaimConnection *gc, const char *who) {
6076#ifdef NOSSI
6077        if (gc->account->permdeny == 4)
6078                oscar_set_permit_deny(gc);
6079#else
6080        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
6081        gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: About to add a deny\n");
6082        if (od->sess->ssi.received_data)
6083                aim_ssi_adddeny(od->sess, who);
6084#endif
6085}
6086
6087static void oscar_rem_permit(GaimConnection *gc, const char *who) {
6088#ifdef NOSSI
6089        if (gc->account->permdeny == 3)
6090                oscar_set_permit_deny(gc);
6091#else
6092        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
6093        gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: About to delete a permit\n");
6094        if (od->sess->ssi.received_data)
6095                aim_ssi_delpermit(od->sess, who);
6096#endif
6097}
6098
6099static void oscar_rem_deny(GaimConnection *gc, const char *who) {
6100#ifdef NOSSI
6101        if (gc->account->permdeny == 4)
6102                oscar_set_permit_deny(gc);
6103#else
6104        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
6105        gaim_debug(GAIM_DEBUG_INFO, "oscar", "ssi: About to delete a deny\n");
6106        if (od->sess->ssi.received_data)
6107                aim_ssi_deldeny(od->sess, who);
6108#endif
6109}
6110
6111static GList *oscar_away_states(GaimConnection *gc)
6112{
6113        struct oscar_data *od = gc->proto_data;
6114        GList *m = NULL;
6115
6116        if (!od->icq)
6117                return g_list_append(m, GAIM_AWAY_CUSTOM);
6118
6119        m = g_list_append(m, _("Online"));
6120        m = g_list_append(m, _("Away"));
6121        m = g_list_append(m, _("Do Not Disturb"));
6122        m = g_list_append(m, _("Not Available"));
6123        m = g_list_append(m, _("Occupied"));
6124        m = g_list_append(m, _("Free For Chat"));
6125        m = g_list_append(m, _("Invisible"));
6126
6127        return m;
6128}
6129
6130static GList *oscar_buddy_menu(GaimConnection *gc, const char *who) {
6131        struct oscar_data *od = gc->proto_data;
6132        GList *m = NULL;
6133        struct proto_buddy_menu *pbm;
6134
6135        if (od->icq) {
6136#if 0
6137                pbm = g_new0(struct proto_buddy_menu, 1);
6138                pbm->label = _("Get Status Msg");
6139                pbm->callback = oscar_get_icqstatusmsg;
6140                pbm->gc = gc;
6141                m = g_list_append(m, pbm);
6142#endif
6143        } else {
6144                GaimBuddy *b = gaim_find_buddy(gc->account, who);
6145                struct buddyinfo *bi;
6146
6147                if (b)
6148                        bi = g_hash_table_lookup(od->buddyinfo, normalize(b->name));
6149
6150                if (b && bi && aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), who) && GAIM_BUDDY_IS_ONLINE(b)) {
6151                        if (bi->caps & AIM_CAPS_DIRECTIM) {
6152                                pbm = g_new0(struct proto_buddy_menu, 1);
6153                                pbm->label = _("Direct IM");
6154                                pbm->callback = oscar_ask_direct_im;
6155                                pbm->gc = gc;
6156                                m = g_list_append(m, pbm);
6157                        }
6158
6159                        if (bi->caps & AIM_CAPS_SENDFILE) {
6160                                pbm = g_new0(struct proto_buddy_menu, 1);
6161                                pbm->label = _("Send File");
6162                                pbm->callback = oscar_ask_sendfile;
6163                                pbm->gc = gc;
6164                                m = g_list_append(m, pbm);
6165                        }
6166#if 0
6167                        if (bi->caps & AIM_CAPS_GETFILE) {
6168                                pbm = g_new0(struct proto_buddy_menu, 1);
6169                                pbm->label = _("Get File");
6170                                pbm->callback = oscar_ask_getfile;
6171                                pbm->gc = gc;
6172                                m = g_list_append(m, pbm);
6173                        }
6174#endif
6175                }
6176        }
6177
6178        if (od->sess->ssi.received_data) {
6179                char *gname = aim_ssi_itemlist_findparentname(od->sess->ssi.local, who);
6180                if (gname && aim_ssi_waitingforauth(od->sess->ssi.local, gname, who)) {
6181                        pbm = g_new0(struct proto_buddy_menu, 1);
6182                        pbm->label = _("Re-request Authorization");
6183                        pbm->callback = gaim_auth_sendrequest;
6184                        pbm->gc = gc;
6185                        m = g_list_append(m, pbm);
6186                }
6187        }
6188       
6189        return m;
6190}
6191
6192static void oscar_format_screenname(GaimConnection *gc, const char *nick) {
6193        struct oscar_data *od = gc->proto_data;
6194        if (!aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), nick)) {
6195                if (!aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH)) {
6196                        od->setnick = TRUE;
6197                        od->newsn = g_strdup(nick);
6198                        aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
6199                } else {
6200                        aim_admin_setnick(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH), nick);
6201                }
6202        } else {
6203                gaim_notify_error(gc, NULL, _("The new formatting is invalid."),
6204                                                  _("Screenname formatting can change only capitalization and whitespace."));
6205        }
6206}
6207
6208static void oscar_show_format_screenname(GaimConnection *gc)
6209{
6210        gaim_request_input(gc, NULL, _("New screenname formatting:"), NULL,
6211                                           gaim_connection_get_display_name(gc), FALSE, FALSE,
6212                                           _("OK"), G_CALLBACK(oscar_format_screenname),
6213                                           _("Cancel"), NULL,
6214                                           gc);
6215}
6216
6217static void oscar_confirm_account(GaimConnection *gc)
6218{
6219        struct oscar_data *od = gc->proto_data;
6220        aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH);
6221
6222        if (conn) {
6223                aim_admin_reqconfirm(od->sess, conn);
6224        } else {
6225                od->conf = TRUE;
6226                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
6227        }
6228}
6229
6230static void oscar_show_email(GaimConnection *gc)
6231{
6232        struct oscar_data *od = gc->proto_data;
6233        aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH);
6234
6235        if (conn) {
6236                aim_admin_getinfo(od->sess, conn, 0x11);
6237        } else {
6238                od->reqemail = TRUE;
6239                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
6240        }
6241}
6242
6243static void oscar_change_email(GaimConnection *gc, const char *email)
6244{
6245        struct oscar_data *od = gc->proto_data;
6246        aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH);
6247
6248        if (conn) {
6249                aim_admin_setemail(od->sess, conn, email);
6250        } else {
6251                od->setemail = TRUE;
6252                od->email = g_strdup(email);
6253                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
6254        }
6255}
6256
6257static void oscar_show_change_email(GaimConnection *gc)
6258{
6259        gaim_request_input(gc, NULL, _("Change Address To:"), NULL, NULL,
6260                                           FALSE, FALSE,
6261                                           _("OK"), G_CALLBACK(oscar_change_email),
6262                                           _("Cancel"), NULL,
6263                                           gc);
6264}
6265
6266static void oscar_show_awaitingauth(GaimConnection *gc)
6267{
6268        struct oscar_data *od = gc->proto_data;
6269        gchar *nombre, *text, *tmp;
6270        GaimBlistNode *gnode, *cnode, *bnode;
6271        int num=0;
6272
6273        text = g_strdup("");
6274
6275        for (gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
6276                GaimGroup *group = (GaimGroup *)gnode;
6277                if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
6278                        continue;
6279                for (cnode = gnode->child; cnode; cnode = cnode->next) {
6280                        if(!GAIM_BLIST_NODE_IS_CONTACT(cnode))
6281                                continue;
6282                        for (bnode = cnode->child; bnode; bnode = bnode->next) {
6283                                GaimBuddy *buddy = (GaimBuddy *)bnode;
6284                                if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
6285                                        continue;
6286                                if (buddy->account == gc->account && aim_ssi_waitingforauth(od->sess->ssi.local, group->name, buddy->name)) {
6287                                        if (gaim_get_buddy_alias_only(buddy))
6288                                                nombre = g_strdup_printf(" %s (%s)", buddy->name, gaim_get_buddy_alias_only(buddy));
6289                                        else
6290                                                nombre = g_strdup_printf(" %s", buddy->name);
6291                                        tmp = g_strdup_printf("%s%s<br>", text, nombre);
6292                                        g_free(text);
6293                                        text = tmp;
6294                                        g_free(nombre);
6295                                        num++;
6296                                }
6297                        }
6298                }
6299        }
6300
6301        if (!num) {
6302                g_free(text);
6303                text = g_strdup(_("<i>you are not waiting for authorization</i>"));
6304        }
6305
6306        gaim_notify_formatted(gc, NULL, _("You are awaiting authorization from "
6307                                                  "the following buddies"),     _("You can re-request "
6308                                                  "authorization from these buddies by "
6309                                                  "right-clicking on them and selecting "
6310                                                  "\"Re-request Authorization.\""), text, NULL, NULL);
6311        g_free(text);
6312}
6313
6314#if 0
6315static void oscar_setavailmsg(GaimConnection *gc, char *text) {
6316        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
6317
6318        aim_srv_setavailmsg(od->sess, text);
6319}
6320
6321static void oscar_show_setavailmsg(GaimConnection *gc)
6322{
6323        gaim_request_input(gc, NULL, _("Available Message:"),
6324                                           NULL, _("Please talk to me, I'm lonely! (and single)"), TRUE, FALSE,
6325                                           _("OK"), G_CALLBACK(oscar_setavailmsg),
6326                                           _("Cancel"), NULL,
6327                                           gc);
6328}
6329#endif
6330
6331static void oscar_show_chpassurl(GaimConnection *gc)
6332{
6333        struct oscar_data *od = gc->proto_data;
6334        gchar *substituted = gaim_strreplace(od->sess->authinfo->chpassurl, "%s", gaim_account_get_username(gaim_connection_get_account(gc)));
6335        gaim_notify_uri(gc, substituted);
6336        g_free(substituted);
6337}
6338
6339static void oscar_set_icon(GaimConnection *gc, const char *iconfile)
6340{
6341        struct oscar_data *od = gc->proto_data;
6342        aim_session_t *sess = od->sess;
6343        FILE *file;
6344        struct stat st;
6345
6346        if (iconfile == NULL) {
6347                /* Set an empty icon, or something */
6348        } else if (!stat(iconfile, &st)) {
6349                char *buf = g_malloc(st.st_size);
6350                file = fopen(iconfile, "rb");
6351                if (file) {
6352                        md5_state_t *state;
6353                        char md5[16];
6354                        int len = fread(buf, 1, st.st_size, file);
6355                        fclose(file);
6356                        state = g_malloc(sizeof(md5_state_t));
6357                        md5_init(state);
6358                        md5_append(state, buf, len);
6359                        md5_finish(state, md5);
6360                        g_free(state);
6361                        aim_ssi_seticon(sess, md5, 16);
6362                } else
6363                        gaim_debug(GAIM_DEBUG_ERROR, "oscar",
6364                                   "Can't open buddy icon file!\n");
6365                g_free(buf);
6366        } else
6367                gaim_debug(GAIM_DEBUG_ERROR, "oscar",
6368                           "Can't stat buddy icon file!\n");
6369}
6370
6371
6372static GList *oscar_actions(GaimConnection *gc)
6373{
6374        struct oscar_data *od = gc->proto_data;
6375        struct proto_actions_menu *pam;
6376        GList *m = NULL;
6377
6378        pam = g_new0(struct proto_actions_menu, 1);
6379        pam->label = _("Set User Info");
6380        pam->callback = show_set_info;
6381        pam->gc = gc;
6382        m = g_list_append(m, pam);
6383
6384#if 0
6385        pam = g_new0(struct proto_actions_menu, 1);
6386        pam->label = _("Set Available Message");
6387        pam->callback = oscar_show_setavailmsg;
6388        pam->gc = gc;
6389        m = g_list_append(m, pam);
6390#endif
6391
6392        pam = g_new0(struct proto_actions_menu, 1);
6393        pam->label = _("Change Password");
6394        pam->callback = show_change_passwd;
6395        pam->gc = gc;
6396        m = g_list_append(m, pam);
6397
6398        if (od->sess->authinfo->chpassurl) {
6399                pam = g_new0(struct proto_actions_menu, 1);
6400                pam->label = _("Change Password (URL)");
6401                pam->callback = oscar_show_chpassurl;
6402                pam->gc = gc;
6403                m = g_list_append(m, pam);
6404        }
6405
6406        if (!od->icq) {
6407                /* AIM actions */
6408                m = g_list_append(m, NULL);
6409
6410                pam = g_new0(struct proto_actions_menu, 1);
6411                pam->label = _("Format Screenname");
6412                pam->callback = oscar_show_format_screenname;
6413                pam->gc = gc;
6414                m = g_list_append(m, pam);
6415
6416                pam = g_new0(struct proto_actions_menu, 1);
6417                pam->label = _("Confirm Account");
6418                pam->callback = oscar_confirm_account;
6419                pam->gc = gc;
6420                m = g_list_append(m, pam);
6421
6422                pam = g_new0(struct proto_actions_menu, 1);
6423                pam->label = _("Display Current Registered Address");
6424                pam->callback = oscar_show_email;
6425                pam->gc = gc;
6426                m = g_list_append(m, pam);
6427
6428                pam = g_new0(struct proto_actions_menu, 1);
6429                pam->label = _("Change Current Registered Address");
6430                pam->callback = oscar_show_change_email;
6431                pam->gc = gc;
6432                m = g_list_append(m, pam);
6433        }
6434
6435        m = g_list_append(m, NULL);
6436
6437        pam = g_new0(struct proto_actions_menu, 1);
6438        pam->label = _("Show Buddies Awaiting Authorization");
6439        pam->callback = oscar_show_awaitingauth;
6440        pam->gc = gc;
6441        m = g_list_append(m, pam);
6442
6443        m = g_list_append(m, NULL);
6444
6445        pam = g_new0(struct proto_actions_menu, 1);
6446        pam->label = _("Search for Buddy by Email");
6447        pam->callback = show_find_email;
6448        pam->gc = gc;
6449        m = g_list_append(m, pam);
6450
6451/*      pam = g_new0(struct proto_actions_menu, 1);
6452        pam->label = _("Search for Buddy by Information");
6453        pam->callback = show_find_info;
6454        pam->gc = gc;
6455        m = g_list_append(m, pam); */
6456
6457        return m;
6458}
6459
6460static void oscar_change_passwd(GaimConnection *gc, const char *old, const char *new)
6461{
6462        struct oscar_data *od = gc->proto_data;
6463
6464        if (od->icq) {
6465                aim_icq_changepasswd(od->sess, new);
6466        } else {
6467                aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH);
6468                if (conn) {
6469                        aim_admin_changepasswd(od->sess, conn, new, old);
6470                } else {
6471                        od->chpass = TRUE;
6472                        od->oldp = g_strdup(old);
6473                        od->newp = g_strdup(new);
6474                        aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH);
6475                }
6476        }
6477}
6478
6479static void oscar_convo_closed(GaimConnection *gc, const char *who)
6480{
6481        struct oscar_data *od = gc->proto_data;
6482        struct direct_im *dim = find_direct_im(od, who);
6483
6484        if (!dim)
6485                return;
6486
6487        od->direct_ims = g_slist_remove(od->direct_ims, dim);
6488        gaim_input_remove(dim->watcher);
6489        aim_conn_kill(od->sess, &dim->conn);
6490        g_free(dim);
6491}
6492
6493static GaimPluginProtocolInfo prpl_info =
6494{
6495        GAIM_PROTO_OSCAR,
6496        OPT_PROTO_MAIL_CHECK | OPT_PROTO_BUDDY_ICON | OPT_PROTO_IM_IMAGE,
6497        NULL,
6498        NULL,
6499        oscar_list_icon,
6500        oscar_list_emblems,
6501        oscar_status_text,
6502        oscar_tooltip_text,
6503        oscar_away_states,
6504        oscar_actions,
6505        oscar_buddy_menu,
6506        oscar_chat_info,
6507        oscar_login,
6508        oscar_close,
6509        oscar_send_im,
6510        oscar_set_info,
6511        oscar_send_typing,
6512        oscar_get_info,
6513        oscar_set_away,
6514        oscar_get_away,
6515        oscar_set_dir,
6516        NULL,
6517        oscar_dir_search,
6518        oscar_set_idle,
6519        oscar_change_passwd,
6520        oscar_add_buddy,
6521        oscar_add_buddies,
6522        oscar_remove_buddy,
6523        oscar_remove_buddies,
6524        oscar_add_permit,
6525        oscar_add_deny,
6526        oscar_rem_permit,
6527        oscar_rem_deny,
6528        oscar_set_permit_deny,
6529        oscar_warn,
6530        oscar_join_chat,
6531        oscar_chat_invite,
6532        oscar_chat_leave,
6533        NULL,
6534        oscar_chat_send,
6535        oscar_keepalive,
6536        NULL,
6537        NULL,
6538        NULL,
6539#ifndef NOSSI
6540        oscar_alias_buddy,
6541        oscar_move_buddy,
6542        oscar_rename_group,
6543#else
6544        NULL,
6545        NULL,
6546        NULL,
6547#endif
6548        NULL,
6549        oscar_convo_closed,
6550        NULL,
6551        oscar_set_icon
6552};
6553
6554static GaimPluginInfo info =
6555{
6556        2,                                                /**< api_version    */
6557        GAIM_PLUGIN_PROTOCOL,                             /**< type           */
6558        NULL,                                             /**< ui_requirement */
6559        0,                                                /**< flags          */
6560        NULL,                                             /**< dependencies   */
6561        GAIM_PRIORITY_DEFAULT,                            /**< priority       */
6562
6563        "prpl-oscar",                                     /**< id             */
6564        "AIM/ICQ",                                        /**< name           */
6565        VERSION,                                          /**< version        */
6566                                                          /**  summary        */
6567        N_("AIM/ICQ Protocol Plugin"),
6568                                                          /**  description    */
6569        N_("AIM/ICQ Protocol Plugin"),
6570        NULL,                                             /**< author         */
6571        GAIM_WEBSITE,                                     /**< homepage       */
6572
6573        NULL,                                             /**< load           */
6574        NULL,                                             /**< unload         */
6575        NULL,                                             /**< destroy        */
6576
6577        NULL,                                             /**< ui_info        */
6578        &prpl_info                                        /**< extra_info     */
6579};
6580
6581static void
6582init_plugin(GaimPlugin *plugin)
6583{
6584        GaimAccountOption *option;
6585
6586        option = gaim_account_option_string_new(_("Auth host"), "server",
6587                                                                                        "login.oscar.aol.com");
6588        prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
6589                                                                                           option);
6590
6591        option = gaim_account_option_int_new(_("Auth port"), "port", 5190);
6592        prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
6593                                                                                           option);
6594
6595        my_protocol = plugin;
6596}
6597
6598GAIM_INIT_PLUGIN(oscar, init_plugin, info);
Note: See TracBrowser for help on using the repository browser.