source: protocols/oscar/oscar.c @ bda2975

Last change on this file since bda2975 was 85693e6, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-21T16:17:24Z

Merging in killerbee stuff (just file transfers and maybe a few things from
mainline). Once I add ft support glue to protocols/purple/ I guess this will
all go into killerbee.

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