source: protocols/oscar/oscar.c @ f68fd5f

3.0.2
Last change on this file since f68fd5f was 6b96de6, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-11-15T23:00:32Z

Un-deprecate login.icq.com, ICQ is back on its own server thanks to the
acquisition from a while ago. I was warned about two weeks ago that this
was going to happen ... apparently sooner than expected.

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