source: protocols/oscar/oscar.c @ 7b05842

Last change on this file since 7b05842 was af496d8, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-05-06T15:13:49Z

Use UCS-2BE instead of UNICODEBIG as the UTF16 charset used by the OSCAR
module. Might improve compatibility with non-glibc iconv libs.

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