source: protocols/oscar/oscar.c @ 07874be

Last change on this file since 07874be was f7d12f7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-05T20:20:44Z

Some fixes for compiler warnings that only show up when compiling with -O2,
and some additions to the Debian package description.

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