source: protocols/oscar/oscar.c @ e248c7f

Last change on this file since e248c7f was e046390, checked in by Wilmer van der Gaast <wilmer@…>, at 2009-10-10T23:25:54Z

Make purple use BitlBee's event handling API. Since the APIs never really
diverged too much this is fairly transparent. I did rename and redefine
GAIM_INPUT_* variables to really make it work without adding another stupid
layer in between.

One problem left, the new libpurple input API doesn't care about return
values. Fixing that in the next CL.

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