source: protocols/oscar/oscar.c @ 386042c

Last change on this file since 386042c was 0eb971a, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-10-19T22:39:10Z

Removing some fully dead code.

  • Property mode set to 100644
File size: 73.2 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 |= ACC_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        if (!aim_sncmp(ic->acc->user, info->sn))
950                g_snprintf(ic->displayname, sizeof(ic->displayname), "%s", info->sn);
951
952        tmp = normalize(info->sn);
953        imcb_buddy_status(ic, tmp, flags, state_string, NULL);
954        imcb_buddy_times(ic, tmp, signon, time_idle);
955
956
957        return 1;
958}
959
960static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
961        aim_userinfo_t *info;
962        va_list ap;
963        struct im_connection *ic = sess->aux_data;
964
965        va_start(ap, fr);
966        info = va_arg(ap, aim_userinfo_t *);
967        va_end(ap);
968
969        imcb_buddy_status(ic, normalize(info->sn), 0, NULL, NULL );
970
971        return 1;
972}
973
974static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
975        char *tmp = g_malloc(BUF_LONG + 1);
976        struct im_connection *ic = sess->aux_data;
977        int flags = 0;
978       
979        if (args->icbmflags & AIM_IMFLAGS_AWAY)
980                flags |= OPT_AWAY;
981       
982        if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) {
983                char *src;
984               
985                if (args->icbmflags & AIM_IMFLAGS_UNICODE)
986                        src = "UNICODEBIG";
987                else
988                        src = "ISO8859-1";
989               
990                /* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */
991                if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) {
992                        // Successfully converted!
993                } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
994                        int i;
995                       
996                        for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
997                                unsigned short uni;
998                               
999                                uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1000       
1001                                if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1002                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
1003                                } else { /* something else, do UNICODE entity */
1004                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
1005                                }
1006                        }
1007                } else {
1008                        g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1009                }
1010        } else if (args->mpmsg.numparts == 0) {
1011                g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1012        } else {
1013                aim_mpmsg_section_t *part;
1014               
1015                *tmp = 0;
1016                for (part = args->mpmsg.parts; part; part = part->next) {
1017                        if (part->data) {
1018                                g_strlcat(tmp, (char*) part->data, BUF_LONG);
1019                                g_strlcat(tmp, "\n", BUF_LONG);
1020                        }
1021                }
1022        }
1023       
1024        strip_linefeed(tmp);
1025        imcb_buddy_msg(ic, normalize(userinfo->sn), tmp, flags, 0);
1026        g_free(tmp);
1027       
1028        return 1;
1029}
1030
1031void oscar_accept_chat(void *data);
1032void oscar_reject_chat(void *data);
1033       
1034static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
1035        struct im_connection *ic = sess->aux_data;
1036
1037        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1038                return 1;
1039
1040        if (args->reqclass & AIM_CAPS_CHAT) {
1041                char *name = extract_name(args->info.chat.roominfo.name);
1042                int *exch = g_new0(int, 1);
1043                GList *m = NULL;
1044                char txt[1024];
1045                struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1);
1046
1047                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1048                *exch = args->info.chat.roominfo.exchange;
1049                m = g_list_append(m, exch);
1050
1051                g_snprintf(txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg);
1052
1053                inv->ic = ic;
1054                inv->exchange = *exch;
1055                inv->name = g_strdup(name);
1056               
1057                imcb_ask(ic, txt, inv, oscar_accept_chat, oscar_reject_chat);
1058       
1059                if (name)
1060                        g_free(name);
1061        } else if (args->reqclass & AIM_CAPS_ICQRTF) {
1062                // TODO: constify
1063                char text[strlen(args->info.rtfmsg.rtfmsg)+1];
1064                strncpy(text, args->info.rtfmsg.rtfmsg, sizeof(text));
1065                imcb_buddy_msg(ic, normalize(userinfo->sn), text, 0, 0);
1066        }
1067
1068        return 1;
1069}
1070
1071static void gaim_icq_authgrant(void *data_) {
1072        struct icq_auth *data = data_;
1073        char *uin;
1074        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
1075       
1076        uin = g_strdup_printf("%u", data->uin);
1077        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1078        // char *message = 0;
1079        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
1080        imcb_ask_add(data->ic, uin, NULL);
1081       
1082        g_free(uin);
1083        g_free(data);
1084}
1085
1086static void gaim_icq_authdeny(void *data_) {
1087        struct icq_auth *data = data_;
1088        char *uin, *message;
1089        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
1090       
1091        uin = g_strdup_printf("%u", data->uin);
1092        message = g_strdup_printf("No reason given.");
1093        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1094        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1095        g_free(message);
1096       
1097        g_free(uin);
1098        g_free(data);
1099}
1100
1101/*
1102 * For when other people ask you for authorization
1103 */
1104static void gaim_icq_authask(struct im_connection *ic, guint32 uin, char *msg) {
1105        struct icq_auth *data;
1106        char *reason = NULL;
1107        char *dialog_msg;
1108
1109        if (set_getbool(&ic->acc->set, "ignore_auth_requests"))
1110                return;
1111       
1112        data = g_new(struct icq_auth, 1);
1113
1114        if (strlen(msg) > 6)
1115                reason = msg + 6;
1116       
1117        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.");
1118        data->ic = ic;
1119        data->uin = uin;
1120        imcb_ask(ic, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
1121        g_free(dialog_msg);
1122}
1123
1124static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
1125        struct im_connection *ic = sess->aux_data;
1126
1127        switch (args->type) {
1128                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1129                        char *uin, *message;
1130                        uin = g_strdup_printf("%u", args->uin);
1131                        message = g_strdup(args->msg);
1132                        strip_linefeed(message);
1133                        imcb_buddy_msg(ic, normalize(uin), message, 0, 0);
1134                        g_free(uin);
1135                        g_free(message);
1136                } break;
1137
1138                case 0x0004: { /* Someone sent you a URL */
1139                        char *uin, *message;
1140                        char **m;
1141       
1142                        uin = g_strdup_printf("%u", args->uin);
1143                        m = g_strsplit(args->msg, "\376", 2);
1144
1145                        if ((strlen(m[0]) != 0)) {
1146                          message = g_strjoinv(" -- ", m);
1147                        } else {
1148                          message = m[1];
1149                        }
1150
1151                        strip_linefeed(message);
1152                        imcb_buddy_msg(ic, normalize(uin), message, 0, 0);
1153                        g_free(uin);
1154                        g_free(m);
1155                        g_free(message);
1156                } break;
1157               
1158                case 0x0006: { /* Someone requested authorization */
1159                        gaim_icq_authask(ic, args->uin, args->msg);
1160                } break;
1161
1162                case 0x0007: { /* Someone has denied you authorization */
1163                        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.") );
1164                } break;
1165
1166                case 0x0008: { /* Someone has granted you authorization */
1167                        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.") );
1168                } break;
1169
1170                case 0x0012: {
1171                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1172                } break;
1173
1174                default: {;
1175                } break;
1176        }
1177
1178        return 1;
1179}
1180
1181static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1182        int channel, ret = 0;
1183        aim_userinfo_t *userinfo;
1184        va_list ap;
1185
1186        va_start(ap, fr);
1187        channel = va_arg(ap, int);
1188        userinfo = va_arg(ap, aim_userinfo_t *);
1189
1190        switch (channel) {
1191                case 1: { /* standard message */
1192                        struct aim_incomingim_ch1_args *args;
1193                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1194                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1195                } break;
1196
1197                case 2: { /* rendevous */
1198                        struct aim_incomingim_ch2_args *args;
1199                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1200                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1201                } break;
1202
1203                case 4: { /* ICQ */
1204                        struct aim_incomingim_ch4_args *args;
1205                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1206                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1207                } break;
1208
1209                default: {;
1210                } break;
1211        }
1212
1213        va_end(ap);
1214
1215        return ret;
1216}
1217
1218static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1219        va_list ap;
1220        guint16 nummissed, reason;
1221        aim_userinfo_t *userinfo;
1222
1223        va_start(ap, fr);
1224        va_arg(ap, unsigned int); /* chan */
1225        userinfo = va_arg(ap, aim_userinfo_t *);
1226        nummissed = (guint16)va_arg(ap, unsigned int);
1227        reason = (guint16)va_arg(ap, unsigned int);
1228        va_end(ap);
1229
1230        switch(reason) {
1231                case 0:
1232                        /* Invalid (0) */
1233                        imcb_error(sess->aux_data,
1234                                   nummissed == 1 ? 
1235                                   _("You missed %d message from %s because it was invalid.") :
1236                                   _("You missed %d messages from %s because they were invalid."),
1237                                   nummissed,
1238                                   userinfo->sn);
1239                        break;
1240                case 1:
1241                        /* Message too large */
1242                        imcb_error(sess->aux_data,
1243                                   nummissed == 1 ?
1244                                   _("You missed %d message from %s because it was too large.") :
1245                                   _("You missed %d messages from %s because they were too large."),
1246                                   nummissed,
1247                                   userinfo->sn);
1248                        break;
1249                case 2:
1250                        /* Rate exceeded */
1251                        imcb_error(sess->aux_data,
1252                                   nummissed == 1 ? 
1253                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1254                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1255                                   nummissed,
1256                                   userinfo->sn);
1257                        break;
1258                case 3:
1259                        /* Evil Sender */
1260                        imcb_error(sess->aux_data,
1261                                   nummissed == 1 ?
1262                                   _("You missed %d message from %s because it was too evil.") : 
1263                                   _("You missed %d messages from %s because they are too evil."),
1264                                   nummissed,
1265                                   userinfo->sn);
1266                        break;
1267                case 4:
1268                        /* Evil Receiver */
1269                        imcb_error(sess->aux_data,
1270                                   nummissed == 1 ? 
1271                                   _("You missed %d message from %s because you are too evil.") :
1272                                   _("You missed %d messages from %s because you are too evil."),
1273                                   nummissed,
1274                                   userinfo->sn);
1275                        break;
1276                default:
1277                        imcb_error(sess->aux_data,
1278                                   nummissed == 1 ? 
1279                                   _("You missed %d message from %s for unknown reasons.") :
1280                                   _("You missed %d messages from %s for unknown reasons."),
1281                                   nummissed,
1282                                   userinfo->sn);
1283                        break;
1284        }
1285
1286        return 1;
1287}
1288
1289static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1290        va_list ap;
1291        guint16 reason;
1292
1293        va_start(ap, fr);
1294        reason = (guint16)va_arg(ap, unsigned int);
1295        va_end(ap);
1296
1297        imcb_error(sess->aux_data, _("SNAC threw error: %s"),
1298                  reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1299
1300        return 1;
1301}
1302
1303static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1304        va_list ap;
1305        char *destn;
1306        guint16 reason;
1307
1308        va_start(ap, fr);
1309        reason = (guint16)va_arg(ap, unsigned int);
1310        destn = va_arg(ap, char *);
1311        va_end(ap);
1312
1313        imcb_error(sess->aux_data, _("Your message to %s did not get sent: %s"), destn,
1314                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1315
1316        return 1;
1317}
1318
1319static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1320        va_list ap;
1321        char *destn;
1322        guint16 reason;
1323
1324        va_start(ap, fr);
1325        reason = (guint16)va_arg(ap, unsigned int);
1326        destn = va_arg(ap, char *);
1327        va_end(ap);
1328
1329        imcb_error(sess->aux_data, _("User information for %s unavailable: %s"), destn,
1330                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1331
1332        return 1;
1333}
1334
1335static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1336        guint16 id;
1337        va_list ap;
1338
1339        va_start(ap, fr);
1340        id  = (guint16)va_arg(ap, unsigned int);
1341        va_arg(ap, char *); /* msg */
1342        va_end(ap);
1343
1344        if (id < 4)
1345                imcb_error(sess->aux_data, _("Your connection may be lost."));
1346
1347        return 1;
1348}
1349
1350static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1351        va_list ap;
1352        guint16 type;
1353        struct im_connection *ic = sess->aux_data;
1354        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1355
1356        va_start(ap, fr);
1357        type = (guint16)va_arg(ap, unsigned int);
1358
1359        switch(type) {
1360                case 0x0002: {
1361                        va_arg(ap, unsigned int); /* maxrooms */
1362                        va_arg(ap, int); /* exchangecount */
1363                        va_arg(ap, struct aim_chat_exchangeinfo *); /* exchanges */
1364                        va_end(ap);
1365
1366                        while (odata->create_rooms) {
1367                                struct create_room *cr = odata->create_rooms->data;
1368                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1369                                g_free(cr->name);
1370                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1371                                g_free(cr);
1372                        }
1373                        }
1374                        break;
1375                case 0x0008: {
1376                        char *ck;
1377                        guint16 instance, exchange;
1378
1379                        va_arg(ap, char *); /* fqcn */
1380                        instance = (guint16)va_arg(ap, unsigned int);
1381                        exchange = (guint16)va_arg(ap, unsigned int);
1382                        va_arg(ap, unsigned int); /* flags */
1383                        va_arg(ap, guint32); /* createtime */
1384                        va_arg(ap, unsigned int); /* maxmsglen */
1385                        va_arg(ap, unsigned int); /* maxoccupancy */
1386                        va_arg(ap, int); /* createperms */
1387                        va_arg(ap, unsigned int); /* unknown */
1388                        va_arg(ap, char *); /* name */
1389                        ck = va_arg(ap, char *);
1390                        va_end(ap);
1391
1392                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1393                        }
1394                        break;
1395                default:
1396                        va_end(ap);
1397                        break;
1398        }
1399        return 1;
1400}
1401
1402static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1403        va_list ap;
1404        int count, i;
1405        aim_userinfo_t *info;
1406        struct im_connection *g = sess->aux_data;
1407
1408        struct chat_connection *c = NULL;
1409
1410        va_start(ap, fr);
1411        count = va_arg(ap, int);
1412        info  = va_arg(ap, aim_userinfo_t *);
1413        va_end(ap);
1414
1415        c = find_oscar_chat_by_conn(g, fr->conn);
1416        if (!c)
1417                return 1;
1418
1419        for (i = 0; i < count; i++)
1420                imcb_chat_add_buddy(c->cnv, normalize(info[i].sn));
1421
1422        return 1;
1423}
1424
1425static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1426        va_list ap;
1427        int count, i;
1428        aim_userinfo_t *info;
1429        struct im_connection *g = sess->aux_data;
1430
1431        struct chat_connection *c = NULL;
1432
1433        va_start(ap, fr);
1434        count = va_arg(ap, int);
1435        info  = va_arg(ap, aim_userinfo_t *);
1436        va_end(ap);
1437
1438        c = find_oscar_chat_by_conn(g, fr->conn);
1439        if (!c)
1440                return 1;
1441
1442        for (i = 0; i < count; i++)
1443                imcb_chat_remove_buddy(c->cnv, normalize(info[i].sn), NULL);
1444
1445        return 1;
1446}
1447
1448static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1449        va_list ap;
1450        guint16 maxmsglen, maxvisiblemsglen;
1451        struct im_connection *ic = sess->aux_data;
1452        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
1453
1454        va_start(ap, fr);
1455        va_arg(ap, struct aim_chat_roominfo *); /* roominfo */
1456        va_arg(ap, char *); /* roomname */
1457        va_arg(ap, int); /* usercount */
1458        va_arg(ap, aim_userinfo_t *); /* userinfo */
1459        va_arg(ap, char *); /* roomdesc */
1460        va_arg(ap, int); /* unknown_c9 */
1461        va_arg(ap, unsigned long); /* creationtime */
1462        maxmsglen = (guint16)va_arg(ap, int);
1463        va_arg(ap, int); /* unknown_d2 */
1464        va_arg(ap, int); /* unknown_d5 */
1465        maxvisiblemsglen = (guint16)va_arg(ap, int);
1466        va_end(ap);
1467
1468        ccon->maxlen = maxmsglen;
1469        ccon->maxvis = maxvisiblemsglen;
1470
1471        return 1;
1472}
1473
1474static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1475        va_list ap;
1476        aim_userinfo_t *info;
1477        char *msg;
1478        struct im_connection *ic = sess->aux_data;
1479        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
1480        char *tmp;
1481
1482        va_start(ap, fr);
1483        info = va_arg(ap, aim_userinfo_t *);
1484        msg  = va_arg(ap, char *);
1485
1486        tmp = g_malloc(BUF_LONG);
1487        g_snprintf(tmp, BUF_LONG, "%s", msg);
1488        imcb_chat_msg(ccon->cnv, normalize(info->sn), tmp, 0, 0);
1489        g_free(tmp);
1490
1491        return 1;
1492}
1493
1494static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1495#if 0
1496        static const char *codes[5] = {
1497                "invalid",
1498                 "change",
1499                 "warning",
1500                 "limit",
1501                 "limit cleared",
1502        };
1503#endif
1504        va_list ap;
1505        guint16 code;
1506        guint32 windowsize, clear, currentavg;
1507
1508        va_start(ap, fr); 
1509        code = (guint16)va_arg(ap, unsigned int);
1510        va_arg(ap, unsigned int); /* rateclass */
1511        windowsize = (guint32)va_arg(ap, unsigned long);
1512        clear = (guint32)va_arg(ap, unsigned long);
1513        va_arg(ap, unsigned long); /* alert */
1514        va_arg(ap, unsigned long); /* limit */
1515        va_arg(ap, unsigned long); /* disconnect */
1516        currentavg = (guint32)va_arg(ap, unsigned long);
1517        va_arg(ap, unsigned long); /* maxavg */
1518        va_end(ap);
1519
1520        /* XXX fix these values */
1521        if (code == AIM_RATE_CODE_CHANGE) {
1522                if (currentavg >= clear)
1523                        aim_conn_setlatency(fr->conn, 0);
1524        } else if (code == AIM_RATE_CODE_WARNING) {
1525                aim_conn_setlatency(fr->conn, windowsize/4);
1526        } else if (code == AIM_RATE_CODE_LIMIT) {
1527                imcb_error(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1528                          "Please wait 10 seconds and try again."));
1529                aim_conn_setlatency(fr->conn, windowsize/2);
1530        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1531                aim_conn_setlatency(fr->conn, 0);
1532        }
1533
1534        return 1;
1535}
1536
1537static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1538        va_list ap;
1539        aim_userinfo_t *info;
1540        struct im_connection *ic = sess->aux_data;
1541
1542        va_start(ap, fr);
1543        info = va_arg(ap, aim_userinfo_t *);
1544        va_end(ap);
1545
1546        ic->evil = info->warnlevel/10;
1547        /* ic->correction_time = (info->onlinesince - ic->login_time); */
1548
1549        return 1;
1550}
1551
1552static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1553
1554        aim_reqpersonalinfo(sess, fr->conn);
1555        aim_bos_reqlocaterights(sess, fr->conn);
1556        aim_bos_reqbuddyrights(sess, fr->conn);
1557
1558        aim_reqicbmparams(sess);
1559
1560        aim_bos_reqrights(sess, fr->conn);
1561        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1562        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1563                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1564
1565        return 1;
1566}
1567
1568static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1569        struct im_connection *ic = sess->aux_data;
1570        struct oscar_data *od = ic->proto_data;
1571
1572        aim_clientready(sess, fr->conn);
1573
1574        if (od->chpass) {
1575                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1576                g_free(od->oldp);
1577                od->oldp = NULL;
1578                g_free(od->newp);
1579                od->newp = NULL;
1580                od->chpass = FALSE;
1581        }
1582        if (od->setnick) {
1583                aim_admin_setnick(sess, fr->conn, od->newsn);
1584                g_free(od->newsn);
1585                od->newsn = NULL;
1586                od->setnick = FALSE;
1587        }
1588        if (od->conf) {
1589                aim_admin_reqconfirm(sess, fr->conn);
1590                od->conf = FALSE;
1591        }
1592        if (od->reqemail) {
1593                aim_admin_getinfo(sess, fr->conn, 0x0011);
1594                od->reqemail = FALSE;
1595        }
1596        if (od->setemail) {
1597                aim_admin_setemail(sess, fr->conn, od->email);
1598                g_free(od->email);
1599                od->setemail = FALSE;
1600        }
1601
1602        return 1;
1603}
1604
1605static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1606        struct aim_icbmparameters *params;
1607        va_list ap;
1608
1609        va_start(ap, fr);
1610        params = va_arg(ap, struct aim_icbmparameters *);
1611        va_end(ap);
1612
1613        /* Maybe senderwarn and recverwarn should be user preferences... */
1614        params->flags = 0x0000000b;
1615        params->maxmsglen = 8000;
1616        params->minmsginterval = 0;
1617
1618        aim_seticbmparam(sess, params);
1619
1620        return 1;
1621}
1622
1623static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1624{
1625        va_list ap;
1626        guint16 maxsiglen;
1627        struct im_connection *ic = sess->aux_data;
1628        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1629
1630        va_start(ap, fr);
1631        maxsiglen = va_arg(ap, int);
1632        va_end(ap);
1633
1634        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1635
1636        /* FIXME: It seems we're not really using this, and it broke now that
1637           struct aim_user is dead.
1638        aim_bos_setprofile(sess, fr->conn, ic->user->user_info, NULL, gaim_caps);
1639        */
1640       
1641        return 1;
1642}
1643
1644static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1645        va_list ap;
1646        guint16 maxbuddies, maxwatchers;
1647        struct im_connection *ic = sess->aux_data;
1648        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1649
1650        va_start(ap, fr);
1651        maxbuddies = (guint16)va_arg(ap, unsigned int);
1652        maxwatchers = (guint16)va_arg(ap, unsigned int);
1653        va_end(ap);
1654
1655        odata->rights.maxbuddies = (guint)maxbuddies;
1656        odata->rights.maxwatchers = (guint)maxwatchers;
1657
1658        return 1;
1659}
1660
1661static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1662        guint16 maxpermits, maxdenies;
1663        va_list ap;
1664        struct im_connection *ic = sess->aux_data;
1665        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1666
1667        va_start(ap, fr);
1668        maxpermits = (guint16)va_arg(ap, unsigned int);
1669        maxdenies = (guint16)va_arg(ap, unsigned int);
1670        va_end(ap);
1671
1672        odata->rights.maxpermits = (guint)maxpermits;
1673        odata->rights.maxdenies = (guint)maxdenies;
1674
1675        aim_clientready(sess, fr->conn);
1676
1677        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1678
1679        aim_ssi_reqrights(sess, fr->conn);
1680        aim_ssi_reqalldata(sess, fr->conn);
1681
1682        return 1;
1683}
1684
1685static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1686        va_list ap;
1687        struct aim_icq_offlinemsg *msg;
1688        struct im_connection *ic = sess->aux_data;
1689
1690        va_start(ap, fr);
1691        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1692        va_end(ap);
1693
1694        switch (msg->type) {
1695                case 0x0001: { /* Basic offline message */
1696                        char sender[32];
1697                        char *dialog_msg = g_strdup(msg->msg);
1698                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1699                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1700                        strip_linefeed(dialog_msg);
1701                        imcb_buddy_msg(ic, normalize(sender), dialog_msg, 0, t);
1702                        g_free(dialog_msg);
1703                } break;
1704
1705                case 0x0004: { /* Someone sent you a URL */
1706                        char sender[32];
1707                        char *dialog_msg;
1708                        char **m;
1709
1710                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1711                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1712
1713                        m = g_strsplit(msg->msg, "\376", 2);
1714
1715                        if ((strlen(m[0]) != 0)) {
1716                          dialog_msg = g_strjoinv(" -- ", m);
1717                        } else {
1718                          dialog_msg = m[1];
1719                        }
1720
1721                        strip_linefeed(dialog_msg);
1722                        imcb_buddy_msg(ic, normalize(sender), dialog_msg, 0, t);
1723                        g_free(dialog_msg);
1724                        g_free(m);
1725                } break;
1726               
1727                case 0x0006: { /* Authorization request */
1728                        gaim_icq_authask(ic, msg->sender, msg->msg);
1729                } break;
1730
1731                case 0x0007: { /* Someone has denied you authorization */
1732                        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.") );
1733                } break;
1734
1735                case 0x0008: { /* Someone has granted you authorization */
1736                        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.") );
1737                } break;
1738
1739                case 0x0012: {
1740                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1741                } break;
1742
1743                default: {;
1744                }
1745        }
1746
1747        return 1;
1748}
1749
1750static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1751{
1752        aim_icq_ackofflinemsgs(sess);
1753        return 1;
1754}
1755
1756static void oscar_keepalive(struct im_connection *ic) {
1757        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1758        aim_flap_nop(odata->sess, odata->conn);
1759}
1760
1761static int oscar_buddy_msg(struct im_connection *ic, char *name, char *message, int imflags) {
1762        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1763        int ret = 0, len = strlen(message);
1764        if (imflags & OPT_AWAY) {
1765                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1766        } else {
1767                struct aim_sendimext_args args;
1768                char *s;
1769               
1770                args.flags = AIM_IMFLAGS_ACK;
1771                if (odata->icq)
1772                        args.flags |= AIM_IMFLAGS_OFFLINE;
1773                for (s = message; *s; s++)
1774                        if (*s & 128)
1775                                break;
1776               
1777                /* Message contains high ASCII chars, time for some translation! */
1778                if (*s) {
1779                        s = g_malloc(BUF_LONG);
1780                        /* Try if we can put it in an ISO8859-1 string first.
1781                           If we can't, fall back to UTF16. */
1782                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1783                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1784                                len = ret;
1785                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1786                                args.flags |= AIM_IMFLAGS_UNICODE;
1787                                len = ret;
1788                        } else {
1789                                /* OOF, translation failed... Oh well.. */
1790                                g_free( s );
1791                                s = message;
1792                        }
1793                } else {
1794                        s = message;
1795                }
1796               
1797                args.features = gaim_features;
1798                args.featureslen = sizeof(gaim_features);
1799               
1800                args.destsn = name;
1801                args.msg    = s;
1802                args.msglen = len;
1803               
1804                ret = aim_send_im_ext(odata->sess, &args);
1805               
1806                if (s != message) {
1807                        g_free(s);
1808                }
1809        }
1810        if (ret >= 0)
1811                return 1;
1812        return ret;
1813}
1814
1815static void oscar_get_info(struct im_connection *g, char *name) {
1816        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1817        if (odata->icq)
1818                aim_icq_getallinfo(odata->sess, name);
1819        else {
1820                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1821                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1822        }
1823}
1824
1825static void oscar_get_away(struct im_connection *g, char *who) {
1826        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1827        if (odata->icq) {
1828                /** FIXME(wilmer): Hmm, lost the ability to get away msgs here, do we care to get that back?
1829                struct buddy *budlight = imcb_find_buddy(g, who);
1830                if (budlight)
1831                        if ((budlight->uc & 0xff80) >> 7)
1832                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1833                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1834                */
1835        } else
1836                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1837}
1838
1839static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
1840{
1841        if (state == NULL)
1842                state = "";
1843
1844        if (!g_strcasecmp(state, _("Visible"))) {
1845                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1846                return;
1847        } else if (!g_strcasecmp(state, _("Invisible"))) {
1848                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1849                return;
1850        } else if (message == NULL) {
1851                message = state;
1852        }
1853
1854        if (od->rights.maxawaymsglen == 0)
1855                imcb_error(ic, "oscar_set_away_aim called before locate rights received");
1856
1857        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1858
1859        g_free(ic->away);
1860        ic->away = NULL;
1861
1862        if (!message) {
1863                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1864                return;
1865        }
1866
1867        if (strlen(message) > od->rights.maxawaymsglen) {
1868                imcb_error(ic, "Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
1869        }
1870
1871        ic->away = g_strndup(message, od->rights.maxawaymsglen);
1872        aim_bos_setprofile(od->sess, od->conn, NULL, ic->away, gaim_caps);
1873
1874        return;
1875}
1876
1877static void oscar_set_away_icq(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
1878{
1879        const char *msg = NULL;
1880        gboolean no_message = FALSE;
1881
1882        /* clean old states */
1883        g_free(ic->away);
1884        ic->away = NULL;
1885        od->sess->aim_icq_state = 0;
1886
1887        /* if no message, then use an empty message */
1888        if (message) {
1889                msg = message;
1890        } else {
1891                msg = "";
1892                no_message = TRUE;
1893        }
1894
1895        if (state == NULL) {
1896                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1897        } else if (!g_strcasecmp(state, "Away")) {
1898                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1899                ic->away = g_strdup(msg);
1900                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1901        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1902                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
1903                ic->away = g_strdup(msg);
1904                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1905        } else if (!g_strcasecmp(state, "Not Available")) {
1906                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
1907                ic->away = g_strdup(msg);
1908                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1909        } else if (!g_strcasecmp(state, "Occupied")) {
1910                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
1911                ic->away = g_strdup(msg);
1912                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1913        } else if (!g_strcasecmp(state, "Free For Chat")) {
1914                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
1915                ic->away = g_strdup(msg);
1916                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1917        } else if (!g_strcasecmp(state, "Invisible")) {
1918                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1919                ic->away = g_strdup(msg);
1920        } else {
1921                if (no_message) {
1922                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1923                } else {
1924                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1925                        ic->away = g_strdup(msg);
1926                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1927                }
1928        }
1929
1930        return;
1931}
1932
1933static void oscar_set_away(struct im_connection *ic, char *state, char *message)
1934{
1935        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
1936
1937        oscar_set_away_aim(ic, od, state, message);
1938        if (od->icq)
1939                oscar_set_away_icq(ic, od, state, message);
1940
1941        return;
1942}
1943
1944static void oscar_add_buddy(struct im_connection *g, char *name, char *group) {
1945        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1946        bee_user_t *bu;
1947       
1948        if (group && (bu = bee_user_by_handle(g->bee, g, name)) && bu->group)
1949                aim_ssi_movebuddy(odata->sess, odata->conn, bu->group->name, group, name);
1950        else
1951                aim_ssi_addbuddies(odata->sess, odata->conn, group ? : OSCAR_GROUP, &name, 1, 0);
1952}
1953
1954static void oscar_remove_buddy(struct im_connection *g, char *name, char *group) {
1955        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1956        struct aim_ssi_item *ssigroup;
1957        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
1958}
1959
1960static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
1961        return 1;
1962}
1963
1964static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
1965        struct im_connection *ic = sess->aux_data;
1966        struct aim_ssi_item *curitem, *curgroup = NULL;
1967        int tmp;
1968        char *nrm;
1969
1970        /* Add from server list to local list */
1971        tmp = 0;
1972        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
1973                nrm = curitem->name ? normalize(curitem->name) : NULL;
1974               
1975                switch (curitem->type) {
1976                        case 0x0000: /* Buddy */
1977                                if ((curitem->name) && (!imcb_buddy_by_handle(ic, nrm))) {
1978                                        char *realname = NULL;
1979
1980                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
1981                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
1982                                       
1983                                        imcb_add_buddy(ic, nrm, curgroup ? (curgroup->gid == curitem->gid ? curgroup->name : NULL) : NULL);
1984                                       
1985                                        if (realname) {
1986                                                imcb_buddy_nick_hint(ic, nrm, realname);
1987                                                imcb_rename_buddy(ic, nrm, realname);
1988                                                g_free(realname);
1989                                        }
1990                                }
1991                                break;
1992
1993                        case 0x0001: /* Group */
1994                                curgroup = curitem;
1995                                break;
1996
1997                        case 0x0002: /* Permit buddy */
1998                                if (curitem->name) {
1999                                        GSList *list;
2000                                        for (list=ic->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2001                                        if (!list) {
2002                                                char *name;
2003                                                name = g_strdup(nrm);
2004                                                ic->permit = g_slist_append(ic->permit, name);
2005                                                tmp++;
2006                                        }
2007                                }
2008                                break;
2009
2010                        case 0x0003: /* Deny buddy */
2011                                if (curitem->name) {
2012                                        GSList *list;
2013                                        for (list=ic->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2014                                        if (!list) {
2015                                                char *name;
2016                                                name = g_strdup(nrm);
2017                                                ic->deny = g_slist_append(ic->deny, name);
2018                                                tmp++;
2019                                        }
2020                                }
2021                                break;
2022
2023                        case 0x0004: /* Permit/deny setting */
2024                                if (curitem->data) {
2025                                        guint8 permdeny;
2026                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != ic->permdeny)) {
2027                                                ic->permdeny = permdeny;
2028                                                tmp++;
2029                                        }
2030                                }
2031                                break;
2032
2033                        case 0x0005: /* Presence setting */
2034                                /* We don't want to change Gaim's setting because it applies to all accounts */
2035                                break;
2036                } /* End of switch on curitem->type */
2037        } /* End of for loop */
2038
2039        aim_ssi_enable(sess, fr->conn);
2040       
2041        /* Request offline messages, now that the buddy list is complete. */
2042        aim_icq_reqofflinemsgs(sess);
2043       
2044        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
2045        imcb_connected(ic);
2046       
2047        return 1;
2048}
2049
2050static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2051{
2052        aim_snac_t *origsnac;
2053        va_list ap;
2054
2055        va_start( ap, fr );
2056        origsnac = va_arg( ap, aim_snac_t * );
2057        va_end( ap );
2058       
2059        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2060        {
2061                int i, st, count = aim_bstream_empty( &fr->data );
2062                char *list;
2063               
2064                if( count & 1 )
2065                {
2066                        /* Hmm, the length should be even... */
2067                        imcb_error( sess->aux_data, "Received SSI ACK package with non-even length");
2068                        return( 0 );
2069                }
2070                count >>= 1;
2071               
2072                list = (char *) origsnac->data;
2073                for( i = 0; i < count; i ++ )
2074                {
2075                        struct aim_ssi_item *ssigroup = aim_ssi_itemlist_findparent( sess->ssi.items, list );
2076                        char *group = ssigroup ? ssigroup->name : NULL;
2077                       
2078                        st = aimbs_get16( &fr->data );
2079                        if( st == 0x00 )
2080                        {
2081                                imcb_add_buddy( sess->aux_data, normalize(list), group );
2082                        }
2083                        else if( st == 0x0E )
2084                        {
2085                                imcb_log( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
2086                               
2087                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2088                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2089                        }
2090                        else if( st == 0x0A )
2091                        {
2092                                imcb_error( sess->aux_data, "Buddy %s is already in your list", list );
2093                        }
2094                        else
2095                        {
2096                                imcb_error( sess->aux_data, "Error while adding buddy: 0x%04x", st );
2097                        }
2098                        list += strlen( list ) + 1;
2099                }
2100        }
2101       
2102        return( 1 );
2103}
2104
2105static void oscar_add_permit(struct im_connection *ic, char *who) {
2106        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2107        if (od->icq) {
2108                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2109        } else {
2110                if (od->sess->ssi.received_data)
2111                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2112        }
2113}
2114
2115static void oscar_add_deny(struct im_connection *ic, char *who) {
2116        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2117        if (od->icq) {
2118                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2119        } else {
2120                if (od->sess->ssi.received_data)
2121                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2122        }
2123}
2124
2125static void oscar_rem_permit(struct im_connection *ic, char *who) {
2126        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2127        if (!od->icq) {
2128                if (od->sess->ssi.received_data)
2129                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2130        }
2131}
2132
2133static void oscar_rem_deny(struct im_connection *ic, char *who) {
2134        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2135        if (!od->icq) {
2136                if (od->sess->ssi.received_data)
2137                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2138        }
2139}
2140
2141static GList *oscar_away_states(struct im_connection *ic)
2142{
2143        struct oscar_data *od = ic->proto_data;
2144
2145        if (od->icq) {
2146                static GList *m = NULL;
2147                m = g_list_append(m, "Away");
2148                m = g_list_append(m, "Do Not Disturb");
2149                m = g_list_append(m, "Not Available");
2150                m = g_list_append(m, "Occupied");
2151                m = g_list_append(m, "Free For Chat");
2152                m = g_list_append(m, "Invisible");
2153                return m;
2154        } else {
2155                static GList *m = NULL;
2156                m = g_list_append(m, "Away");
2157                return m;
2158        }
2159}
2160
2161static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2162{
2163        struct im_connection *ic = sess->aux_data;
2164        struct oscar_data *od = ic->proto_data;
2165        gchar who[16];
2166        GString *str;
2167        va_list ap;
2168        struct aim_icq_info *info;
2169        uint32_t ip;
2170
2171        va_start(ap, fr);
2172        info = va_arg(ap, struct aim_icq_info *);
2173        va_end(ap);
2174
2175        if (!info->uin)
2176                return 0;
2177
2178        str = g_string_sized_new(512);
2179        g_snprintf(who, sizeof(who), "%u", info->uin);
2180
2181        g_string_printf(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2182        info->nick ? info->nick : "-");
2183        g_string_append_printf(str, "\n%s: %s", _("First Name"), info->first);
2184        g_string_append_printf(str, "\n%s: %s", _("Last Name"), info->last);
2185        g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email);
2186        if (info->numaddresses && info->email2) {
2187                int i;
2188                for (i = 0; i < info->numaddresses; i++) {
2189                        g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email2[i]);
2190                }
2191        }
2192        if (od->ips && (ip = (long) g_hash_table_lookup(od->ips, &info->uin)) != 0) {
2193                g_string_append_printf(str, "\n%s: %d.%d.%d.%d", _("Last used IP address"),
2194                                       (ip >> 24), (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
2195        }
2196        g_string_append_printf(str, "\n%s: %s", _("Mobile Phone"), info->mobile);
2197        if (info->gender != 0)
2198                g_string_append_printf(str, "\n%s: %s", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
2199        if (info->birthyear || info->birthmonth || info->birthday) {
2200                char date[30];
2201                struct tm tm;
2202                memset(&tm, 0, sizeof(struct tm));
2203                tm.tm_mday = (int)info->birthday;
2204                tm.tm_mon = (int)info->birthmonth-1;
2205                tm.tm_year = (int)info->birthyear%100;
2206                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2207                g_string_append_printf(str, "\n%s: %s", _("Birthday"), date);
2208        }
2209        if (info->age) {
2210                char age[5];
2211                g_snprintf(age, sizeof(age), "%hhd", info->age);
2212                g_string_append_printf(str, "\n%s: %s", _("Age"), age);
2213        }
2214        g_string_append_printf(str, "\n%s: %s", _("Personal Web Page"), info->personalwebpage);
2215        if (info->info && info->info[0]) {
2216                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2217                info->info, _("End of Additional Information"));
2218        }
2219        g_string_append_c(str, '\n');
2220        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2221                g_string_append_printf(str, "%s:", _("Home Address"));
2222                g_string_append_printf(str, "\n%s: %s", _("Address"), info->homeaddr);
2223                g_string_append_printf(str, "\n%s: %s", _("City"), info->homecity);
2224                g_string_append_printf(str, "\n%s: %s", _("State"), info->homestate); 
2225                g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->homezip);
2226                g_string_append_c(str, '\n');
2227        }
2228        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2229                g_string_append_printf(str, "%s:", _("Work Address"));
2230                g_string_append_printf(str, "\n%s: %s", _("Address"), info->workaddr);
2231                g_string_append_printf(str, "\n%s: %s", _("City"), info->workcity);
2232                g_string_append_printf(str, "\n%s: %s", _("State"), info->workstate);
2233                g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->workzip);
2234                g_string_append_c(str, '\n');
2235        }
2236        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2237                g_string_append_printf(str, "%s:", _("Work Information"));
2238                g_string_append_printf(str, "\n%s: %s", _("Company"), info->workcompany);
2239                g_string_append_printf(str, "\n%s: %s", _("Division"), info->workdivision);
2240                g_string_append_printf(str, "\n%s: %s", _("Position"), info->workposition);
2241                if (info->workwebpage && info->workwebpage[0]) {
2242                        g_string_append_printf(str, "\n%s: %s", _("Web Page"), info->workwebpage);
2243                }
2244                g_string_append_c(str, '\n');
2245        }
2246
2247        imcb_log(ic, "%s\n%s", _("User Info"), str->str);
2248        g_string_free(str, TRUE);
2249
2250        return 1;
2251
2252}
2253
2254static char *oscar_encoding_extract(const char *encoding)
2255{
2256        char *ret = NULL;
2257        char *begin, *end;
2258
2259        g_return_val_if_fail(encoding != NULL, NULL);
2260
2261        /* Make sure encoding begins with charset= */
2262        if (strncmp(encoding, "text/plain; charset=", 20) &&
2263                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2264                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2265        {
2266                return NULL;
2267        }
2268
2269        begin = strchr(encoding, '"');
2270        end = strrchr(encoding, '"');
2271
2272        if ((begin == NULL) || (end == NULL) || (begin >= end))
2273                return NULL;
2274
2275        ret = g_strndup(begin+1, (end-1) - begin);
2276
2277        return ret;
2278}
2279
2280static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2281{
2282        char *utf8 = g_new0(char, 8192);
2283
2284        if ((encoding == NULL) || encoding[0] == '\0') {
2285                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2286        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2287                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2288        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2289                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2290        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2291                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2292        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2293                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2294                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2295                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2296        }
2297
2298        /*
2299         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2300         * we have been unable to convert the text to utf-8 from the encoding
2301         * that was specified.  So we assume it's UTF-8 and hope for the best.
2302         */
2303        if (*utf8 == 0) {
2304            strncpy(utf8, text, textlen);
2305        }
2306
2307        return utf8;
2308}
2309
2310static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2311{
2312        struct im_connection *ic = sess->aux_data;
2313        va_list ap;
2314        aim_userinfo_t *userinfo;
2315        guint16 infotype;
2316        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2317        guint16 text_length;
2318        char *utf8 = NULL;
2319
2320        va_start(ap, fr);
2321        userinfo = va_arg(ap, aim_userinfo_t *);
2322        infotype = va_arg(ap, int);
2323        text_encoding = va_arg(ap, char*);
2324        text = va_arg(ap, char*);
2325        text_length = va_arg(ap, int);
2326        va_end(ap);
2327
2328        if(text_encoding)
2329                extracted_encoding = oscar_encoding_extract(text_encoding);
2330        if(infotype == AIM_GETINFO_GENERALINFO) {
2331                /*Display idle time*/
2332                char buff[256];
2333                struct tm idletime;
2334                if(userinfo->idletime) {
2335                        memset(&idletime, 0, sizeof(struct tm));
2336                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2337                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2338                        idletime.tm_min = userinfo->idletime % 60;
2339                        idletime.tm_sec = 0;
2340                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
2341                        imcb_log(ic, "%s: %s", _("Idle Time"), buff);
2342                }
2343               
2344                if(text) {
2345                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2346                        imcb_log(ic, "%s\n%s", _("User Info"), utf8);
2347                } else {
2348                        imcb_log(ic, _("No user info available."));
2349                }
2350        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2351                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2352                imcb_log(ic, "%s\n%s", _("Away Message"), utf8);
2353        }
2354
2355        g_free(utf8);
2356   
2357        return 1;
2358}
2359
2360int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)
2361{
2362        struct im_connection * ic = sess->aux_data;
2363        va_list ap;
2364        guint16 type2;
2365        char * sn;
2366
2367        va_start(ap, fr);
2368        va_arg(ap, int); /* type1 */
2369        sn = va_arg(ap, char*);
2370        type2 = va_arg(ap, int);
2371        va_end(ap);
2372   
2373        if(type2 == 0x0002) {
2374                /* User is typing */
2375                imcb_buddy_typing(ic, normalize(sn), OPT_TYPING);
2376        } 
2377        else if (type2 == 0x0001) {
2378                /* User has typed something, but is not actively typing (stale) */
2379                imcb_buddy_typing(ic, normalize(sn), OPT_THINKING);
2380        }
2381        else {
2382                /* User has stopped typing */
2383                imcb_buddy_typing(ic, normalize(sn), 0);
2384        }
2385       
2386        return 1;
2387}
2388
2389int oscar_send_typing(struct im_connection *ic, char * who, int typing)
2390{
2391        struct oscar_data *od = ic->proto_data;
2392        return( aim_im_sendmtn(od->sess, 1, who, (typing & OPT_TYPING) ? 0x0002 : 0x0000) );
2393}
2394
2395void oscar_chat_msg(struct groupchat *c, char *message, int msgflags)
2396{
2397        struct im_connection *ic = c->ic;
2398        struct oscar_data * od = (struct oscar_data*)ic->proto_data;
2399        struct chat_connection * ccon;
2400        int ret;
2401        guint8 len = strlen(message);
2402        guint16 flags;
2403        char *s;
2404       
2405        if (!(ccon = c->data))
2406                return;
2407               
2408        for (s = message; *s; s++)
2409                if (*s & 128)
2410                        break;
2411       
2412        flags = AIM_CHATFLAGS_NOREFLECT;
2413       
2414        /* Message contains high ASCII chars, time for some translation! */
2415        if (*s) {
2416                s = g_malloc(BUF_LONG);
2417                /* Try if we can put it in an ISO8859-1 string first.
2418                   If we can't, fall back to UTF16. */
2419                if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
2420                        flags |= AIM_CHATFLAGS_ISO_8859_1;
2421                        len = ret;
2422                } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
2423                        flags |= AIM_CHATFLAGS_UNICODE;
2424                        len = ret;
2425                } else {
2426                        /* OOF, translation failed... Oh well.. */
2427                        g_free( s );
2428                        s = message;
2429                }
2430        } else {
2431                s = message;
2432        }
2433               
2434        ret = aim_chat_send_im(od->sess, ccon->conn, flags, s, len);
2435               
2436        if (s != message) {     
2437                g_free(s);
2438  }
2439 
2440/*  return (ret >= 0); */
2441}
2442
2443void oscar_chat_invite(struct groupchat *c, char *who, char *message)
2444{
2445        struct im_connection *ic = c->ic;
2446        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2447        struct chat_connection *ccon;
2448       
2449        if (!(ccon = c->data))
2450                return;
2451       
2452        aim_chat_invite(od->sess, od->conn, who, message ? message : "",
2453                                        ccon->exchange, ccon->name, 0x0);
2454}
2455
2456void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc)
2457{
2458        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2459
2460        /* Notify the conversation window that we've left the chat */
2461        imcb_chat_free(cc->cnv);
2462
2463        /* Destroy the chat_connection */
2464        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
2465        if (cc->inpa > 0)
2466                b_event_remove(cc->inpa);
2467        aim_conn_kill(od->sess, &cc->conn);
2468        g_free(cc->name);
2469        g_free(cc->show);
2470        g_free(cc);
2471}
2472
2473void oscar_chat_leave(struct groupchat *c)
2474{
2475        if (!c->data)
2476                return;
2477        oscar_chat_kill(c->ic, c->data);
2478}
2479
2480struct groupchat *oscar_chat_join_internal(struct im_connection *ic, const char *room,
2481        const char *nick, const char *password, int exchange_number)
2482{
2483        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2484        struct groupchat *ret = imcb_chat_new(ic, room);
2485        aim_conn_t * cur;
2486
2487        if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
2488                aim_chatnav_createroom(od->sess, cur, room, exchange_number);
2489               
2490                return ret;
2491        } else {
2492                struct create_room * cr = g_new0(struct create_room, 1);
2493               
2494                cr->exchange = exchange_number;
2495                cr->name = g_strdup(room);
2496                od->create_rooms = g_slist_append(od->create_rooms, cr);
2497                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
2498               
2499                return ret;
2500        }
2501}
2502
2503struct groupchat *oscar_chat_join(struct im_connection *ic, const char *room,
2504        const char *nick, const char *password, set_t **sets)
2505{
2506        return oscar_chat_join_internal(ic, room, nick, password, set_getint(sets, "exchange_number"));
2507}
2508
2509struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)
2510{
2511        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2512        struct groupchat *ret;
2513        static int chat_id = 0;
2514        char * chatname, *s;
2515       
2516        chatname = g_strdup_printf("%s%s%d", isdigit(*ic->acc->user) ? "icq" : "",
2517                                   ic->acc->user, chat_id++);
2518       
2519        for (s = chatname; *s; s ++)
2520                if (!isalnum(*s))
2521                        *s = '0';
2522       
2523        ret = oscar_chat_join_internal(ic, chatname, NULL, NULL, 4);
2524        aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);
2525
2526        g_free(chatname);
2527       
2528        return ret;
2529}
2530
2531void oscar_accept_chat(void *data)
2532{
2533        struct aim_chat_invitation * inv = data;
2534       
2535        oscar_chat_join_internal(inv->ic, inv->name, NULL, NULL, 4);
2536        g_free(inv->name);
2537        g_free(inv);
2538}
2539
2540void oscar_reject_chat(void *data)
2541{
2542        struct aim_chat_invitation * inv = data;
2543       
2544        g_free(inv->name);
2545        g_free(inv);
2546}
2547
2548void oscar_chat_add_settings(account_t *acc, set_t **head)
2549{
2550        set_add(head, "exchange_number", "4", set_eval_int, NULL);
2551}
2552
2553void oscar_chat_free_settings(account_t *acc, set_t **head)
2554{
2555        set_del(head, "exchange_number");
2556}
2557
2558void oscar_initmodule() 
2559{
2560        struct prpl *ret = g_new0(struct prpl, 1);
2561        ret->name = "oscar";
2562    ret->mms = 2343;       /* this guess taken from libotr UPGRADING file */
2563        ret->away_states = oscar_away_states;
2564        ret->init = oscar_init;
2565        ret->login = oscar_login;
2566        ret->keepalive = oscar_keepalive;
2567        ret->logout = oscar_logout;
2568        ret->buddy_msg = oscar_buddy_msg;
2569        ret->get_info = oscar_get_info;
2570        ret->set_away = oscar_set_away;
2571        ret->get_away = oscar_get_away;
2572        ret->add_buddy = oscar_add_buddy;
2573        ret->remove_buddy = oscar_remove_buddy;
2574        ret->chat_msg = oscar_chat_msg;
2575        ret->chat_invite = oscar_chat_invite;
2576        ret->chat_leave = oscar_chat_leave;
2577        ret->chat_with = oscar_chat_with;
2578        ret->chat_join = oscar_chat_join;
2579        ret->chat_add_settings = oscar_chat_add_settings;
2580        ret->chat_free_settings = oscar_chat_free_settings;
2581        ret->add_permit = oscar_add_permit;
2582        ret->add_deny = oscar_add_deny;
2583        ret->rem_permit = oscar_rem_permit;
2584        ret->rem_deny = oscar_rem_deny;
2585        ret->send_typing = oscar_send_typing;
2586       
2587        ret->handle_cmp = aim_sncmp;
2588
2589        register_protocol(ret);
2590}
Note: See TracBrowser for help on using the repository browser.