source: protocols/oscar/oscar.c @ 921ea8b

Last change on this file since 921ea8b was a39ede7, checked in by dequis <dx@…>, at 2018-03-31T02:04:45Z

oscar: remove old_icq_auth (XOR login, default off since 2010)

Holy crap this login method is *bad*

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