source: protocols/oscar/oscar.c @ 71074ac

Last change on this file since 71074ac was 3d45471, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-02-22T14:49:58Z

Remove a few stale functions from struct prpl.

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