source: protocols/oscar/oscar.c @ 19ac9c5

Last change on this file since 19ac9c5 was ba9edaa, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-05-10T17:34:46Z

Moved everything to the BitlBee event handling API.

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