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

Last change on this file since 7a9d968 was e88fe7da, checked in by Veres Lajos <vlajos@…>, at 2015-08-07T21:53:25Z

typofix - https://github.com/vlajos/misspell_fixer

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