source: protocols/oscar/oscar.c @ ee5c355

Last change on this file since ee5c355 was d5ccd83, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-08-13T19:15:23Z

Extra comments in set.h and now properly using set_getbool() instead of
set_getint().

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