source: protocols/oscar/oscar.c @ 0b5cc72

Last change on this file since 0b5cc72 was 81e04e1, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-01T02:32:25Z

nogaim.c is close to doing something useful again without speaking any IRC
itself.

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