source: protocols/oscar/oscar.c @ b52e478

Last change on this file since b52e478 was 68198e9, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-07T00:31:34Z

Two more fixes in OSCAR to avoid NULL pointers being passed to
g_strcasecmp().

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