source: protocols/oscar/oscar.c @ ea728e6

Last change on this file since ea728e6 was 0ebf919, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-21T22:25:37Z

Pass "user is mobile" info coming from OSCAR up to BitlBee and show mobile
people as away=Mobile. Bug #462 (and others for other protocols).

  • Property mode set to 100644
File size: 74.3 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       
[0ebf919]901        /* Maybe this should be done just for AIM contacts, not sure. */
902        if (info->flags & AIM_FLAG_WIRELESS)
903                flags |= OPT_MOBILE;
904       
[b7d3cc34]905        if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
906                if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
907                      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
[6bbb939]908                        flags |= OPT_AWAY;
[b7d3cc34]909                }
[6bbb939]910               
911                if( info->icqinfo.status & AIM_ICQ_STATE_DND )
912                        state_string = "Do Not Disturb";
913                else if( info->icqinfo.status & AIM_ICQ_STATE_OUT )
914                        state_string = "Not Available";
915                else if( info->icqinfo.status & AIM_ICQ_STATE_BUSY )
916                        state_string = "Occupied";
917                else if( info->icqinfo.status & AIM_ICQ_STATE_INVISIBLE )
918                        state_string = "Invisible";
[b7d3cc34]919        }
920
921        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
922                time(&time_idle);
923                time_idle -= info->idletime*60;
924        }
925
926        if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
927                signon = time(NULL) - info->sessionlen;
928
[e64de00]929        if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR) {
930                uint32_t *uin = g_new0(uint32_t, 1);
931               
932                if (od->ips == NULL)
933                        od->ips = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL);
934               
935                if (sscanf(info->sn, "%d", uin) == 1)
936                        g_hash_table_insert(od->ips, uin, (gpointer) (long) info->icqinfo.ipaddr);
937        }
938
[c801d25]939        if (!aim_sncmp(ic->acc->user, info->sn))
[0da65d5]940                g_snprintf(ic->displayname, sizeof(ic->displayname), "%s", info->sn);
[b7d3cc34]941
[424e663]942        tmp = normalize(info->sn);
943        imcb_buddy_status(ic, tmp, flags, state_string, NULL);
[56699f0]944        imcb_buddy_times(ic, tmp, signon, time_idle);
[424e663]945
[b7d3cc34]946
947        return 1;
948}
949
950static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
951        aim_userinfo_t *info;
952        va_list ap;
[0da65d5]953        struct im_connection *ic = sess->aux_data;
[b7d3cc34]954
955        va_start(ap, fr);
956        info = va_arg(ap, aim_userinfo_t *);
957        va_end(ap);
958
[424e663]959        imcb_buddy_status(ic, normalize(info->sn), 0, NULL, NULL );
[b7d3cc34]960
961        return 1;
962}
963
964static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
965        char *tmp = g_malloc(BUF_LONG + 1);
[0da65d5]966        struct im_connection *ic = sess->aux_data;
[b7d3cc34]967        int flags = 0;
968       
969        if (args->icbmflags & AIM_IMFLAGS_AWAY)
[6bbb939]970                flags |= OPT_AWAY;
[b7d3cc34]971       
972        if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) {
973                char *src;
974               
975                if (args->icbmflags & AIM_IMFLAGS_UNICODE)
976                        src = "UNICODEBIG";
977                else
978                        src = "ISO8859-1";
979               
980                /* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */
981                if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) {
982                        // Successfully converted!
983                } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
984                        int i;
985                       
986                        for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
987                                unsigned short uni;
988                               
989                                uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
990       
991                                if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
992                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
993                                } else { /* something else, do UNICODE entity */
994                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
995                                }
996                        }
997                } else {
998                        g_snprintf(tmp, BUF_LONG, "%s", args->msg);
999                }
[be68d99]1000        } else if (args->mpmsg.numparts == 0) {
[b7d3cc34]1001                g_snprintf(tmp, BUF_LONG, "%s", args->msg);
[be68d99]1002        } else {
[f774e01]1003                aim_mpmsg_section_t *part;
[be68d99]1004               
1005                *tmp = 0;
[f774e01]1006                for (part = args->mpmsg.parts; part; part = part->next) {
1007                        if (part->data) {
1008                                g_strlcat(tmp, (char*) part->data, BUF_LONG);
1009                                g_strlcat(tmp, "\n", BUF_LONG);
1010                        }
[be68d99]1011                }
1012        }
[b7d3cc34]1013       
1014        strip_linefeed(tmp);
[424e663]1015        imcb_buddy_msg(ic, normalize(userinfo->sn), tmp, flags, 0);
[b7d3cc34]1016        g_free(tmp);
1017       
1018        return 1;
1019}
1020
[9143aeb]1021void oscar_accept_chat(void *data);
1022void oscar_reject_chat(void *data);
[b8ef1b1]1023       
[b7d3cc34]1024static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
[0da65d5]1025        struct im_connection *ic = sess->aux_data;
[b7d3cc34]1026
1027        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1028                return 1;
[b8ef1b1]1029
[b7d3cc34]1030        if (args->reqclass & AIM_CAPS_CHAT) {
1031                char *name = extract_name(args->info.chat.roominfo.name);
1032                int *exch = g_new0(int, 1);
1033                GList *m = NULL;
[11e090b]1034                char txt[1024];
1035                struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1);
1036
[b7d3cc34]1037                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1038                *exch = args->info.chat.roominfo.exchange;
1039                m = g_list_append(m, exch);
[b8ef1b1]1040
[d4bc2d9]1041                g_snprintf(txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg);
[b8ef1b1]1042
[0da65d5]1043                inv->ic = ic;
[b8ef1b1]1044                inv->exchange = *exch;
1045                inv->name = g_strdup(name);
1046               
[d4bc2d9]1047                imcb_ask(ic, txt, inv, oscar_accept_chat, oscar_reject_chat);
[b8ef1b1]1048       
[b7d3cc34]1049                if (name)
1050                        g_free(name);
[d4bc2d9]1051        } else if (args->reqclass & AIM_CAPS_ICQRTF) {
1052                // TODO: constify
[2334048]1053                char text[strlen(args->info.rtfmsg.rtfmsg)+1];
1054                strncpy(text, args->info.rtfmsg.rtfmsg, sizeof(text));
[d4bc2d9]1055                imcb_buddy_msg(ic, normalize(userinfo->sn), text, 0, 0);
[b7d3cc34]1056        }
[b8ef1b1]1057
[b7d3cc34]1058        return 1;
1059}
1060
[9143aeb]1061static void gaim_icq_authgrant(void *data_) {
1062        struct icq_auth *data = data_;
[b7d3cc34]1063        char *uin, message;
[0da65d5]1064        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
[b7d3cc34]1065       
1066        uin = g_strdup_printf("%u", data->uin);
1067        message = 0;
1068        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1069        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
[81e04e1]1070        imcb_ask_add(data->ic, uin, NULL);
[b7d3cc34]1071       
1072        g_free(uin);
1073        g_free(data);
1074}
1075
[9143aeb]1076static void gaim_icq_authdeny(void *data_) {
1077        struct icq_auth *data = data_;
[b7d3cc34]1078        char *uin, *message;
[0da65d5]1079        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
[b7d3cc34]1080       
1081        uin = g_strdup_printf("%u", data->uin);
1082        message = g_strdup_printf("No reason given.");
1083        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1084        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1085        g_free(message);
1086       
1087        g_free(uin);
1088        g_free(data);
1089}
1090
1091/*
1092 * For when other people ask you for authorization
1093 */
[0da65d5]1094static void gaim_icq_authask(struct im_connection *ic, guint32 uin, char *msg) {
[545d7c0]1095        struct icq_auth *data;
[b7d3cc34]1096        char *reason = NULL;
1097        char *dialog_msg;
[545d7c0]1098
1099        if (set_getbool(&ic->acc->set, "ignore_auth_requests"))
1100                return;
[b7d3cc34]1101       
[545d7c0]1102        data = g_new(struct icq_auth, 1);
1103
[b7d3cc34]1104        if (strlen(msg) > 6)
1105                reason = msg + 6;
1106       
[aefa533e]1107        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]1108        data->ic = ic;
[b7d3cc34]1109        data->uin = uin;
[84b045d]1110        imcb_ask(ic, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
[b7d3cc34]1111        g_free(dialog_msg);
1112}
1113
1114static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
[0da65d5]1115        struct im_connection *ic = sess->aux_data;
[b7d3cc34]1116
1117        switch (args->type) {
1118                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1119                        char *uin, *message;
1120                        uin = g_strdup_printf("%u", args->uin);
1121                        message = g_strdup(args->msg);
1122                        strip_linefeed(message);
[424e663]1123                        imcb_buddy_msg(ic, normalize(uin), message, 0, 0);
[b7d3cc34]1124                        g_free(uin);
1125                        g_free(message);
1126                } break;
1127
1128                case 0x0004: { /* Someone sent you a URL */
1129                        char *uin, *message;
1130                        char **m;
1131       
1132                        uin = g_strdup_printf("%u", args->uin);
1133                        m = g_strsplit(args->msg, "\376", 2);
1134
1135                        if ((strlen(m[0]) != 0)) {
1136                          message = g_strjoinv(" -- ", m);
1137                        } else {
1138                          message = m[1];
1139                        }
1140
1141                        strip_linefeed(message);
[424e663]1142                        imcb_buddy_msg(ic, normalize(uin), message, 0, 0);
[b7d3cc34]1143                        g_free(uin);
1144                        g_free(m);
1145                        g_free(message);
1146                } break;
1147               
1148                case 0x0006: { /* Someone requested authorization */
[0da65d5]1149                        gaim_icq_authask(ic, args->uin, args->msg);
[b7d3cc34]1150                } break;
1151
1152                case 0x0007: { /* Someone has denied you authorization */
[84b045d]1153                        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]1154                } break;
1155
1156                case 0x0008: { /* Someone has granted you authorization */
[84b045d]1157                        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]1158                } break;
1159
1160                case 0x0012: {
1161                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1162                } break;
1163
1164                default: {;
1165                } break;
1166        }
1167
1168        return 1;
1169}
[9cb9868]1170
[b7d3cc34]1171static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1172        int channel, ret = 0;
1173        aim_userinfo_t *userinfo;
1174        va_list ap;
1175
1176        va_start(ap, fr);
1177        channel = va_arg(ap, int);
1178        userinfo = va_arg(ap, aim_userinfo_t *);
1179
1180        switch (channel) {
1181                case 1: { /* standard message */
1182                        struct aim_incomingim_ch1_args *args;
1183                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1184                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1185                } break;
1186
1187                case 2: { /* rendevous */
1188                        struct aim_incomingim_ch2_args *args;
1189                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1190                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1191                } break;
1192
1193                case 4: { /* ICQ */
1194                        struct aim_incomingim_ch4_args *args;
1195                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1196                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1197                } break;
1198
1199                default: {;
1200                } break;
1201        }
1202
1203        va_end(ap);
1204
1205        return ret;
1206}
1207
1208static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1209        va_list ap;
1210        guint16 chan, nummissed, reason;
1211        aim_userinfo_t *userinfo;
1212
1213        va_start(ap, fr);
1214        chan = (guint16)va_arg(ap, unsigned int);
1215        userinfo = va_arg(ap, aim_userinfo_t *);
1216        nummissed = (guint16)va_arg(ap, unsigned int);
1217        reason = (guint16)va_arg(ap, unsigned int);
1218        va_end(ap);
1219
1220        switch(reason) {
1221                case 0:
1222                        /* Invalid (0) */
[84b045d]1223                        imcb_error(sess->aux_data,
[b7d3cc34]1224                                   nummissed == 1 ? 
1225                                   _("You missed %d message from %s because it was invalid.") :
1226                                   _("You missed %d messages from %s because they were invalid."),
1227                                   nummissed,
1228                                   userinfo->sn);
1229                        break;
1230                case 1:
1231                        /* Message too large */
[84b045d]1232                        imcb_error(sess->aux_data,
[b7d3cc34]1233                                   nummissed == 1 ?
1234                                   _("You missed %d message from %s because it was too large.") :
1235                                   _("You missed %d messages from %s because they were too large."),
1236                                   nummissed,
1237                                   userinfo->sn);
1238                        break;
1239                case 2:
1240                        /* Rate exceeded */
[84b045d]1241                        imcb_error(sess->aux_data,
[b7d3cc34]1242                                   nummissed == 1 ? 
1243                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1244                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1245                                   nummissed,
1246                                   userinfo->sn);
1247                        break;
1248                case 3:
1249                        /* Evil Sender */
[84b045d]1250                        imcb_error(sess->aux_data,
[b7d3cc34]1251                                   nummissed == 1 ?
1252                                   _("You missed %d message from %s because it was too evil.") : 
1253                                   _("You missed %d messages from %s because they are too evil."),
1254                                   nummissed,
1255                                   userinfo->sn);
1256                        break;
1257                case 4:
1258                        /* Evil Receiver */
[84b045d]1259                        imcb_error(sess->aux_data,
[b7d3cc34]1260                                   nummissed == 1 ? 
1261                                   _("You missed %d message from %s because you are too evil.") :
1262                                   _("You missed %d messages from %s because you are too evil."),
1263                                   nummissed,
1264                                   userinfo->sn);
1265                        break;
1266                default:
[84b045d]1267                        imcb_error(sess->aux_data,
[b7d3cc34]1268                                   nummissed == 1 ? 
1269                                   _("You missed %d message from %s for unknown reasons.") :
1270                                   _("You missed %d messages from %s for unknown reasons."),
1271                                   nummissed,
1272                                   userinfo->sn);
1273                        break;
1274        }
1275
1276        return 1;
1277}
1278
1279static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1280        va_list ap;
1281        guint16 reason;
1282
1283        va_start(ap, fr);
1284        reason = (guint16)va_arg(ap, unsigned int);
1285        va_end(ap);
1286
[84b045d]1287        imcb_error(sess->aux_data, _("SNAC threw error: %s"),
[aef4828]1288                  reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
[b7d3cc34]1289
1290        return 1;
1291}
1292
1293static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1294        va_list ap;
1295        char *destn;
1296        guint16 reason;
1297
1298        va_start(ap, fr);
1299        reason = (guint16)va_arg(ap, unsigned int);
1300        destn = va_arg(ap, char *);
1301        va_end(ap);
1302
[84b045d]1303        imcb_error(sess->aux_data, _("Your message to %s did not get sent: %s"), destn,
[b7d3cc34]1304                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1305
1306        return 1;
1307}
1308
1309static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1310        va_list ap;
1311        char *destn;
1312        guint16 reason;
1313
1314        va_start(ap, fr);
1315        reason = (guint16)va_arg(ap, unsigned int);
1316        destn = va_arg(ap, char *);
1317        va_end(ap);
1318
[84b045d]1319        imcb_error(sess->aux_data, _("User information for %s unavailable: %s"), destn,
[b7d3cc34]1320                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1321
1322        return 1;
1323}
1324
1325static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1326        char *msg;
1327        guint16 id;
1328        va_list ap;
1329
1330        va_start(ap, fr);
1331        id  = (guint16)va_arg(ap, unsigned int);
1332        msg = va_arg(ap, char *);
1333        va_end(ap);
1334
1335        if (id < 4)
[84b045d]1336                imcb_error(sess->aux_data, _("Your connection may be lost."));
[b7d3cc34]1337
1338        return 1;
1339}
1340
1341static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1342        va_list ap;
1343        guint16 type;
[0da65d5]1344        struct im_connection *ic = sess->aux_data;
1345        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1346
1347        va_start(ap, fr);
1348        type = (guint16)va_arg(ap, unsigned int);
1349
1350        switch(type) {
1351                case 0x0002: {
1352                        guint8 maxrooms;
1353                        struct aim_chat_exchangeinfo *exchanges;
1354                        int exchangecount; // i;
1355
1356                        maxrooms = (guint8)va_arg(ap, unsigned int);
1357                        exchangecount = va_arg(ap, int);
1358                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1359                        va_end(ap);
1360
1361                        while (odata->create_rooms) {
1362                                struct create_room *cr = odata->create_rooms->data;
1363                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1364                                g_free(cr->name);
1365                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1366                                g_free(cr);
1367                        }
1368                        }
1369                        break;
1370                case 0x0008: {
1371                        char *fqcn, *name, *ck;
1372                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1373                        guint8 createperms;
1374                        guint32 createtime;
1375
1376                        fqcn = va_arg(ap, char *);
1377                        instance = (guint16)va_arg(ap, unsigned int);
1378                        exchange = (guint16)va_arg(ap, unsigned int);
1379                        flags = (guint16)va_arg(ap, unsigned int);
1380                        createtime = va_arg(ap, guint32);
1381                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1382                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1383                        createperms = (guint8)va_arg(ap, int);
1384                        unknown = (guint16)va_arg(ap, unsigned int);
1385                        name = va_arg(ap, char *);
1386                        ck = va_arg(ap, char *);
1387                        va_end(ap);
1388
1389                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1390                        }
1391                        break;
1392                default:
1393                        va_end(ap);
1394                        break;
1395        }
1396        return 1;
1397}
1398
1399static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1400        va_list ap;
1401        int count, i;
1402        aim_userinfo_t *info;
[0da65d5]1403        struct im_connection *g = sess->aux_data;
[b7d3cc34]1404
1405        struct chat_connection *c = NULL;
1406
1407        va_start(ap, fr);
1408        count = va_arg(ap, int);
1409        info  = va_arg(ap, aim_userinfo_t *);
1410        va_end(ap);
1411
1412        c = find_oscar_chat_by_conn(g, fr->conn);
1413        if (!c)
1414                return 1;
1415
1416        for (i = 0; i < count; i++)
[424e663]1417                imcb_chat_add_buddy(c->cnv, normalize(info[i].sn));
[b7d3cc34]1418
1419        return 1;
1420}
1421
1422static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1423        va_list ap;
1424        int count, i;
1425        aim_userinfo_t *info;
[0da65d5]1426        struct im_connection *g = sess->aux_data;
[b7d3cc34]1427
1428        struct chat_connection *c = NULL;
1429
1430        va_start(ap, fr);
1431        count = va_arg(ap, int);
1432        info  = va_arg(ap, aim_userinfo_t *);
1433        va_end(ap);
1434
1435        c = find_oscar_chat_by_conn(g, fr->conn);
1436        if (!c)
1437                return 1;
1438
1439        for (i = 0; i < count; i++)
[424e663]1440                imcb_chat_remove_buddy(c->cnv, normalize(info[i].sn), NULL);
[b7d3cc34]1441
1442        return 1;
1443}
1444
1445static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1446        va_list ap;
1447        aim_userinfo_t *userinfo;
1448        struct aim_chat_roominfo *roominfo;
1449        char *roomname;
1450        int usercount;
1451        char *roomdesc;
1452        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1453        guint32 creationtime;
[0da65d5]1454        struct im_connection *ic = sess->aux_data;
1455        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
[b7d3cc34]1456
1457        va_start(ap, fr);
1458        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1459        roomname = va_arg(ap, char *);
1460        usercount= va_arg(ap, int);
1461        userinfo = va_arg(ap, aim_userinfo_t *);
1462        roomdesc = va_arg(ap, char *);
1463        unknown_c9 = (guint16)va_arg(ap, int);
1464        creationtime = (guint32)va_arg(ap, unsigned long);
1465        maxmsglen = (guint16)va_arg(ap, int);
1466        unknown_d2 = (guint16)va_arg(ap, int);
1467        unknown_d5 = (guint16)va_arg(ap, int);
1468        maxvisiblemsglen = (guint16)va_arg(ap, int);
1469        va_end(ap);
1470
1471        ccon->maxlen = maxmsglen;
1472        ccon->maxvis = maxvisiblemsglen;
1473
1474        return 1;
1475}
1476
1477static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1478        va_list ap;
1479        aim_userinfo_t *info;
1480        char *msg;
[0da65d5]1481        struct im_connection *ic = sess->aux_data;
1482        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
[b7d3cc34]1483        char *tmp;
1484
1485        va_start(ap, fr);
1486        info = va_arg(ap, aim_userinfo_t *);
1487        msg  = va_arg(ap, char *);
1488
1489        tmp = g_malloc(BUF_LONG);
1490        g_snprintf(tmp, BUF_LONG, "%s", msg);
[424e663]1491        imcb_chat_msg(ccon->cnv, normalize(info->sn), tmp, 0, 0);
[b7d3cc34]1492        g_free(tmp);
1493
1494        return 1;
1495}
1496
1497static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1498#if 0
1499        static const char *codes[5] = {
1500                "invalid",
1501                 "change",
1502                 "warning",
1503                 "limit",
1504                 "limit cleared",
1505        };
1506#endif
1507        va_list ap;
1508        guint16 code, rateclass;
1509        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1510
1511        va_start(ap, fr); 
1512        code = (guint16)va_arg(ap, unsigned int);
1513        rateclass= (guint16)va_arg(ap, unsigned int);
1514        windowsize = (guint32)va_arg(ap, unsigned long);
1515        clear = (guint32)va_arg(ap, unsigned long);
1516        alert = (guint32)va_arg(ap, unsigned long);
1517        limit = (guint32)va_arg(ap, unsigned long);
1518        disconnect = (guint32)va_arg(ap, unsigned long);
1519        currentavg = (guint32)va_arg(ap, unsigned long);
1520        maxavg = (guint32)va_arg(ap, unsigned long);
1521        va_end(ap);
1522
1523        /* XXX fix these values */
1524        if (code == AIM_RATE_CODE_CHANGE) {
1525                if (currentavg >= clear)
1526                        aim_conn_setlatency(fr->conn, 0);
1527        } else if (code == AIM_RATE_CODE_WARNING) {
1528                aim_conn_setlatency(fr->conn, windowsize/4);
1529        } else if (code == AIM_RATE_CODE_LIMIT) {
[84b045d]1530                imcb_error(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
[aef4828]1531                          "Please wait 10 seconds and try again."));
[b7d3cc34]1532                aim_conn_setlatency(fr->conn, windowsize/2);
1533        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1534                aim_conn_setlatency(fr->conn, 0);
1535        }
1536
1537        return 1;
1538}
1539
1540static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1541        va_list ap;
1542        aim_userinfo_t *info;
[0da65d5]1543        struct im_connection *ic = sess->aux_data;
[b7d3cc34]1544
1545        va_start(ap, fr);
1546        info = va_arg(ap, aim_userinfo_t *);
1547        va_end(ap);
1548
[0da65d5]1549        ic->evil = info->warnlevel/10;
1550        /* ic->correction_time = (info->onlinesince - ic->login_time); */
[b7d3cc34]1551
1552        return 1;
1553}
1554
1555static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1556
1557        aim_reqpersonalinfo(sess, fr->conn);
1558        aim_bos_reqlocaterights(sess, fr->conn);
1559        aim_bos_reqbuddyrights(sess, fr->conn);
1560
1561        aim_reqicbmparams(sess);
1562
1563        aim_bos_reqrights(sess, fr->conn);
1564        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1565        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1566                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1567
1568        return 1;
1569}
1570
1571static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
[0da65d5]1572        struct im_connection *ic = sess->aux_data;
1573        struct oscar_data *od = ic->proto_data;
[b7d3cc34]1574
1575        aim_clientready(sess, fr->conn);
1576
1577        if (od->chpass) {
1578                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1579                g_free(od->oldp);
1580                od->oldp = NULL;
1581                g_free(od->newp);
1582                od->newp = NULL;
1583                od->chpass = FALSE;
1584        }
1585        if (od->setnick) {
1586                aim_admin_setnick(sess, fr->conn, od->newsn);
1587                g_free(od->newsn);
1588                od->newsn = NULL;
1589                od->setnick = FALSE;
1590        }
1591        if (od->conf) {
1592                aim_admin_reqconfirm(sess, fr->conn);
1593                od->conf = FALSE;
1594        }
1595        if (od->reqemail) {
1596                aim_admin_getinfo(sess, fr->conn, 0x0011);
1597                od->reqemail = FALSE;
1598        }
1599        if (od->setemail) {
1600                aim_admin_setemail(sess, fr->conn, od->email);
1601                g_free(od->email);
1602                od->setemail = FALSE;
1603        }
1604
1605        return 1;
1606}
1607
1608static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1609        struct aim_icbmparameters *params;
1610        va_list ap;
1611
1612        va_start(ap, fr);
1613        params = va_arg(ap, struct aim_icbmparameters *);
1614        va_end(ap);
1615
1616        /* Maybe senderwarn and recverwarn should be user preferences... */
[3e1de87]1617        params->flags = 0x0000000b;
[b7d3cc34]1618        params->maxmsglen = 8000;
1619        params->minmsginterval = 0;
1620
1621        aim_seticbmparam(sess, params);
1622
1623        return 1;
1624}
1625
1626static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1627{
1628        va_list ap;
1629        guint16 maxsiglen;
[0da65d5]1630        struct im_connection *ic = sess->aux_data;
1631        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1632
1633        va_start(ap, fr);
1634        maxsiglen = va_arg(ap, int);
1635        va_end(ap);
1636
1637        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1638
[0a3c243]1639        /* FIXME: It seems we're not really using this, and it broke now that
1640           struct aim_user is dead.
[0da65d5]1641        aim_bos_setprofile(sess, fr->conn, ic->user->user_info, NULL, gaim_caps);
[0a3c243]1642        */
1643       
[b7d3cc34]1644        return 1;
1645}
1646
1647static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1648        va_list ap;
1649        guint16 maxbuddies, maxwatchers;
[0da65d5]1650        struct im_connection *ic = sess->aux_data;
1651        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1652
1653        va_start(ap, fr);
1654        maxbuddies = (guint16)va_arg(ap, unsigned int);
1655        maxwatchers = (guint16)va_arg(ap, unsigned int);
1656        va_end(ap);
1657
1658        odata->rights.maxbuddies = (guint)maxbuddies;
1659        odata->rights.maxwatchers = (guint)maxwatchers;
1660
1661        return 1;
1662}
1663
1664static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1665        guint16 maxpermits, maxdenies;
1666        va_list ap;
[0da65d5]1667        struct im_connection *ic = sess->aux_data;
1668        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1669
1670        va_start(ap, fr);
1671        maxpermits = (guint16)va_arg(ap, unsigned int);
1672        maxdenies = (guint16)va_arg(ap, unsigned int);
1673        va_end(ap);
1674
1675        odata->rights.maxpermits = (guint)maxpermits;
1676        odata->rights.maxdenies = (guint)maxdenies;
1677
1678        aim_clientready(sess, fr->conn);
1679
1680        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1681
1682        aim_ssi_reqrights(sess, fr->conn);
1683        aim_ssi_reqalldata(sess, fr->conn);
1684
1685        return 1;
1686}
1687
1688static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1689        va_list ap;
1690        struct aim_icq_offlinemsg *msg;
[0da65d5]1691        struct im_connection *ic = sess->aux_data;
[b7d3cc34]1692
1693        va_start(ap, fr);
1694        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1695        va_end(ap);
1696
1697        switch (msg->type) {
1698                case 0x0001: { /* Basic offline message */
1699                        char sender[32];
1700                        char *dialog_msg = g_strdup(msg->msg);
1701                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1702                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1703                        strip_linefeed(dialog_msg);
[424e663]1704                        imcb_buddy_msg(ic, normalize(sender), dialog_msg, 0, t);
[b7d3cc34]1705                        g_free(dialog_msg);
1706                } break;
1707
1708                case 0x0004: { /* Someone sent you a URL */
1709                        char sender[32];
1710                        char *dialog_msg;
1711                        char **m;
1712
1713                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1714                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1715
1716                        m = g_strsplit(msg->msg, "\376", 2);
1717
1718                        if ((strlen(m[0]) != 0)) {
1719                          dialog_msg = g_strjoinv(" -- ", m);
1720                        } else {
1721                          dialog_msg = m[1];
1722                        }
1723
1724                        strip_linefeed(dialog_msg);
[424e663]1725                        imcb_buddy_msg(ic, normalize(sender), dialog_msg, 0, t);
[b7d3cc34]1726                        g_free(dialog_msg);
1727                        g_free(m);
1728                } break;
1729               
1730                case 0x0006: { /* Authorization request */
[0da65d5]1731                        gaim_icq_authask(ic, msg->sender, msg->msg);
[b7d3cc34]1732                } break;
1733
1734                case 0x0007: { /* Someone has denied you authorization */
[84b045d]1735                        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]1736                } break;
1737
1738                case 0x0008: { /* Someone has granted you authorization */
[84b045d]1739                        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]1740                } break;
1741
1742                case 0x0012: {
1743                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1744                } break;
1745
1746                default: {;
1747                }
1748        }
1749
1750        return 1;
1751}
1752
1753static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1754{
1755        aim_icq_ackofflinemsgs(sess);
1756        return 1;
1757}
1758
[0da65d5]1759static void oscar_keepalive(struct im_connection *ic) {
1760        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1761        aim_flap_nop(odata->sess, odata->conn);
1762}
1763
[f6c963b]1764static int oscar_buddy_msg(struct im_connection *ic, char *name, char *message, int imflags) {
[0da65d5]1765        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1766        int ret = 0, len = strlen(message);
[6bbb939]1767        if (imflags & OPT_AWAY) {
[b7d3cc34]1768                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1769        } else {
1770                struct aim_sendimext_args args;
1771                char *s;
1772               
1773                args.flags = AIM_IMFLAGS_ACK;
1774                if (odata->icq)
1775                        args.flags |= AIM_IMFLAGS_OFFLINE;
1776                for (s = message; *s; s++)
1777                        if (*s & 128)
1778                                break;
1779               
1780                /* Message contains high ASCII chars, time for some translation! */
1781                if (*s) {
1782                        s = g_malloc(BUF_LONG);
1783                        /* Try if we can put it in an ISO8859-1 string first.
1784                           If we can't, fall back to UTF16. */
1785                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1786                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1787                                len = ret;
1788                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1789                                args.flags |= AIM_IMFLAGS_UNICODE;
1790                                len = ret;
1791                        } else {
1792                                /* OOF, translation failed... Oh well.. */
1793                                g_free( s );
1794                                s = message;
1795                        }
1796                } else {
1797                        s = message;
1798                }
1799               
1800                args.features = gaim_features;
1801                args.featureslen = sizeof(gaim_features);
1802               
1803                args.destsn = name;
1804                args.msg    = s;
1805                args.msglen = len;
1806               
1807                ret = aim_send_im_ext(odata->sess, &args);
1808               
1809                if (s != message) {
1810                        g_free(s);
1811                }
1812        }
1813        if (ret >= 0)
1814                return 1;
1815        return ret;
1816}
1817
[0da65d5]1818static void oscar_get_info(struct im_connection *g, char *name) {
[b7d3cc34]1819        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1820        if (odata->icq)
1821                aim_icq_getallinfo(odata->sess, name);
1822        else {
1823                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1824                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1825        }
1826}
1827
[0da65d5]1828static void oscar_get_away(struct im_connection *g, char *who) {
[b7d3cc34]1829        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1830        if (odata->icq) {
[81e04e1]1831                /** FIXME(wilmer): Hmm, lost the ability to get away msgs here, do we care to get that back?
[f0cb961]1832                struct buddy *budlight = imcb_find_buddy(g, who);
[b7d3cc34]1833                if (budlight)
1834                        if ((budlight->uc & 0xff80) >> 7)
1835                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1836                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
[81e04e1]1837                */
[b7d3cc34]1838        } else
1839                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1840}
1841
[0da65d5]1842static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
[b7d3cc34]1843{
[68198e9]1844        if (state == NULL)
1845                state = "";
[b7d3cc34]1846
1847        if (!g_strcasecmp(state, _("Visible"))) {
1848                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1849                return;
1850        } else if (!g_strcasecmp(state, _("Invisible"))) {
1851                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1852                return;
[17f9522]1853        } else if (message == NULL) {
1854                message = state;
1855        }
[b7d3cc34]1856
1857        if (od->rights.maxawaymsglen == 0)
[84b045d]1858                imcb_error(ic, "oscar_set_away_aim called before locate rights received");
[b7d3cc34]1859
1860        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1861
[c0c43fb]1862        g_free(ic->away);
[0da65d5]1863        ic->away = NULL;
[b7d3cc34]1864
1865        if (!message) {
1866                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1867                return;
1868        }
1869
1870        if (strlen(message) > od->rights.maxawaymsglen) {
[84b045d]1871                imcb_error(ic, "Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
[b7d3cc34]1872        }
1873
[0da65d5]1874        ic->away = g_strndup(message, od->rights.maxawaymsglen);
1875        aim_bos_setprofile(od->sess, od->conn, NULL, ic->away, gaim_caps);
[b7d3cc34]1876
1877        return;
1878}
1879
[0da65d5]1880static void oscar_set_away_icq(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
[b7d3cc34]1881{
[c0c43fb]1882        const char *msg = NULL;
[b7d3cc34]1883        gboolean no_message = FALSE;
1884
1885        /* clean old states */
[c0c43fb]1886        g_free(ic->away);
1887        ic->away = NULL;
[b7d3cc34]1888        od->sess->aim_icq_state = 0;
1889
1890        /* if no message, then use an empty message */
[c0c43fb]1891        if (message) {
1892                msg = message;
1893        } else {
1894                msg = "";
[b7d3cc34]1895                no_message = TRUE;
[c0c43fb]1896        }
[b7d3cc34]1897
[68198e9]1898        if (state == NULL) {
[b7d3cc34]1899                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1900        } else if (!g_strcasecmp(state, "Away")) {
1901                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
[c0c43fb]1902                ic->away = g_strdup(msg);
[b7d3cc34]1903                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1904        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1905                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
[c0c43fb]1906                ic->away = g_strdup(msg);
[b7d3cc34]1907                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1908        } else if (!g_strcasecmp(state, "Not Available")) {
1909                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
[c0c43fb]1910                ic->away = g_strdup(msg);
[b7d3cc34]1911                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1912        } else if (!g_strcasecmp(state, "Occupied")) {
1913                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
[c0c43fb]1914                ic->away = g_strdup(msg);
[b7d3cc34]1915                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1916        } else if (!g_strcasecmp(state, "Free For Chat")) {
1917                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
[c0c43fb]1918                ic->away = g_strdup(msg);
[b7d3cc34]1919                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1920        } else if (!g_strcasecmp(state, "Invisible")) {
1921                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
[c0c43fb]1922                ic->away = g_strdup(msg);
[17f9522]1923        } else {
[b7d3cc34]1924                if (no_message) {
1925                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1926                } else {
1927                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
[c0c43fb]1928                        ic->away = g_strdup(msg);
[b7d3cc34]1929                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1930                }
1931        }
1932
1933        return;
1934}
1935
[0da65d5]1936static void oscar_set_away(struct im_connection *ic, char *state, char *message)
[b7d3cc34]1937{
[0da65d5]1938        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1939
[c0c43fb]1940        oscar_set_away_aim(ic, od, state, message);
[b7d3cc34]1941        if (od->icq)
[0da65d5]1942                oscar_set_away_icq(ic, od, state, message);
[b7d3cc34]1943
1944        return;
1945}
1946
[0da65d5]1947static void oscar_add_buddy(struct im_connection *g, char *name, char *group) {
[b7d3cc34]1948        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
[42acba1]1949        bee_user_t *bu;
1950       
1951        if (group && (bu = bee_user_by_handle(g->bee, g, name)) && bu->group)
1952                aim_ssi_movebuddy(odata->sess, odata->conn, bu->group->name, group, name);
1953        else
1954                aim_ssi_addbuddies(odata->sess, odata->conn, group ? : OSCAR_GROUP, &name, 1, 0);
[b7d3cc34]1955}
1956
[0da65d5]1957static void oscar_remove_buddy(struct im_connection *g, char *name, char *group) {
[b7d3cc34]1958        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1959        struct aim_ssi_item *ssigroup;
1960        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
1961}
1962
1963static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
1964        return 1;
1965}
1966
1967static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
[0da65d5]1968        struct im_connection *ic = sess->aux_data;
[f7d12f7]1969        struct aim_ssi_item *curitem, *curgroup = NULL;
[b7d3cc34]1970        int tmp;
[424e663]1971        char *nrm;
[b7d3cc34]1972
1973        /* Add from server list to local list */
1974        tmp = 0;
1975        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
[fab3d2d]1976                nrm = curitem->name ? normalize(curitem->name) : NULL;
[de82335]1977               
[b7d3cc34]1978                switch (curitem->type) {
1979                        case 0x0000: /* Buddy */
[81e04e1]1980                                if ((curitem->name) && (!imcb_buddy_by_handle(ic, nrm))) {
[b7d3cc34]1981                                        char *realname = NULL;
1982
1983                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
1984                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
[5a673f3]1985                                       
[f7d12f7]1986                                        imcb_add_buddy(ic, nrm, curgroup ? (curgroup->gid == curitem->gid ? curgroup->name : NULL) : NULL);
[b7d3cc34]1987                                       
[f0cb961]1988                                        if (realname) {
[424e663]1989                                                imcb_buddy_nick_hint(ic, nrm, realname);
1990                                                imcb_rename_buddy(ic, nrm, realname);
[f0cb961]1991                                                g_free(realname);
1992                                        }
[b7d3cc34]1993                                }
1994                                break;
1995
[5a673f3]1996                        case 0x0001: /* Group */
1997                                curgroup = curitem;
1998                                break;
1999
[b7d3cc34]2000                        case 0x0002: /* Permit buddy */
2001                                if (curitem->name) {
2002                                        GSList *list;
[0da65d5]2003                                        for (list=ic->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
[b7d3cc34]2004                                        if (!list) {
2005                                                char *name;
[424e663]2006                                                name = g_strdup(nrm);
[0da65d5]2007                                                ic->permit = g_slist_append(ic->permit, name);
[b7d3cc34]2008                                                tmp++;
2009                                        }
2010                                }
2011                                break;
2012
2013                        case 0x0003: /* Deny buddy */
2014                                if (curitem->name) {
2015                                        GSList *list;
[0da65d5]2016                                        for (list=ic->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
[b7d3cc34]2017                                        if (!list) {
2018                                                char *name;
[424e663]2019                                                name = g_strdup(nrm);
[0da65d5]2020                                                ic->deny = g_slist_append(ic->deny, name);
[b7d3cc34]2021                                                tmp++;
2022                                        }
2023                                }
2024                                break;
2025
2026                        case 0x0004: /* Permit/deny setting */
2027                                if (curitem->data) {
2028                                        guint8 permdeny;
[0da65d5]2029                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != ic->permdeny)) {
2030                                                ic->permdeny = permdeny;
[b7d3cc34]2031                                                tmp++;
2032                                        }
2033                                }
2034                                break;
2035
2036                        case 0x0005: /* Presence setting */
2037                                /* We don't want to change Gaim's setting because it applies to all accounts */
2038                                break;
2039                } /* End of switch on curitem->type */
2040        } /* End of for loop */
2041
2042        aim_ssi_enable(sess, fr->conn);
2043       
2044        /* Request offline messages, now that the buddy list is complete. */
2045        aim_icq_reqofflinemsgs(sess);
2046       
2047        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
[84b045d]2048        imcb_connected(ic);
[b7d3cc34]2049       
2050        return 1;
2051}
2052
2053static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2054{
2055        aim_snac_t *origsnac;
2056        va_list ap;
2057
2058        va_start( ap, fr );
2059        origsnac = va_arg( ap, aim_snac_t * );
2060        va_end( ap );
2061       
2062        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2063        {
2064                int i, st, count = aim_bstream_empty( &fr->data );
2065                char *list;
2066               
2067                if( count & 1 )
2068                {
2069                        /* Hmm, the length should be even... */
[84b045d]2070                        imcb_error( sess->aux_data, "Received SSI ACK package with non-even length");
[b7d3cc34]2071                        return( 0 );
2072                }
2073                count >>= 1;
2074               
2075                list = (char *) origsnac->data;
2076                for( i = 0; i < count; i ++ )
2077                {
[42acba1]2078                        struct aim_ssi_item *ssigroup = aim_ssi_itemlist_findparent( sess->ssi.items, list );
2079                        char *group = ssigroup ? ssigroup->name : NULL;
2080                       
[b7d3cc34]2081                        st = aimbs_get16( &fr->data );
[f0cb961]2082                        if( st == 0x00 )
2083                        {
[42acba1]2084                                imcb_add_buddy( sess->aux_data, normalize(list), group );
[f0cb961]2085                        }
2086                        else if( st == 0x0E )
[b7d3cc34]2087                        {
[84b045d]2088                                imcb_log( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
[b7d3cc34]2089                               
2090                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2091                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2092                        }
[f0cb961]2093                        else
2094                        {
2095                                imcb_error( sess->aux_data, "Error while adding buddy: 0x%04x", st );
2096                        }
[b7d3cc34]2097                        list += strlen( list ) + 1;
2098                }
2099        }
2100       
2101        return( 1 );
2102}
2103
[0da65d5]2104static void oscar_set_permit_deny(struct im_connection *ic) {
2105        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2106        if (od->icq) {
2107                GSList *list;
2108                char buf[MAXMSGLEN];
2109                int at;
2110
[0da65d5]2111                switch(ic->permdeny) {
[b7d3cc34]2112                case 1:
[c2fb3809]2113                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, ic->acc->user);
[b7d3cc34]2114                        break;
2115                case 2:
[c2fb3809]2116                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, ic->acc->user);
[b7d3cc34]2117                        break;
2118                case 3:
[0da65d5]2119                        list = ic->permit;
[b7d3cc34]2120                        at = 0;
2121                        while (list) {
2122                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2123                                list = list->next;
2124                        }
2125                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2126                        break;
2127                case 4:
[0da65d5]2128                        list = ic->deny;
[b7d3cc34]2129                        at = 0;
2130                        while (list) {
2131                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2132                                list = list->next;
2133                        }
2134                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2135                        break;
2136                        default:
2137                        break;
2138                }
2139        } else {
2140                if (od->sess->ssi.received_data)
[0da65d5]2141                        aim_ssi_setpermdeny(od->sess, od->conn, ic->permdeny, 0xffffffff);
[b7d3cc34]2142        }
2143}
2144
[0da65d5]2145static void oscar_add_permit(struct im_connection *ic, char *who) {
2146        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2147        if (od->icq) {
2148                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2149        } else {
2150                if (od->sess->ssi.received_data)
2151                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2152        }
2153}
2154
[0da65d5]2155static void oscar_add_deny(struct im_connection *ic, char *who) {
2156        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2157        if (od->icq) {
2158                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2159        } else {
2160                if (od->sess->ssi.received_data)
2161                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2162        }
2163}
2164
[0da65d5]2165static void oscar_rem_permit(struct im_connection *ic, char *who) {
2166        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2167        if (!od->icq) {
2168                if (od->sess->ssi.received_data)
2169                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2170        }
2171}
2172
[0da65d5]2173static void oscar_rem_deny(struct im_connection *ic, char *who) {
2174        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2175        if (!od->icq) {
2176                if (od->sess->ssi.received_data)
2177                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2178        }
2179}
2180
[0da65d5]2181static GList *oscar_away_states(struct im_connection *ic)
[b7d3cc34]2182{
[0da65d5]2183        struct oscar_data *od = ic->proto_data;
[b7d3cc34]2184
[17f9522]2185        if (od->icq) {
2186                static GList *m = NULL;
2187                m = g_list_append(m, "Away");
2188                m = g_list_append(m, "Do Not Disturb");
2189                m = g_list_append(m, "Not Available");
2190                m = g_list_append(m, "Occupied");
2191                m = g_list_append(m, "Free For Chat");
2192                m = g_list_append(m, "Invisible");
2193                return m;
2194        } else {
2195                static GList *m = NULL;
2196                m = g_list_append(m, "Away");
2197                return m;
2198        }
[b7d3cc34]2199}
2200
2201static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2202{
[e64de00]2203        struct im_connection *ic = sess->aux_data;
2204        struct oscar_data *od = ic->proto_data;
2205        gchar who[16];
2206        GString *str;
2207        va_list ap;
2208        struct aim_icq_info *info;
2209        uint32_t ip;
2210
2211        va_start(ap, fr);
2212        info = va_arg(ap, struct aim_icq_info *);
2213        va_end(ap);
2214
2215        if (!info->uin)
2216                return 0;
2217
2218        str = g_string_sized_new(512);
2219        g_snprintf(who, sizeof(who), "%u", info->uin);
2220
2221        g_string_printf(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2222        info->nick ? info->nick : "-");
2223        g_string_append_printf(str, "\n%s: %s", _("First Name"), info->first);
2224        g_string_append_printf(str, "\n%s: %s", _("Last Name"), info->last);
2225        g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email);
2226        if (info->numaddresses && info->email2) {
2227                int i;
2228                for (i = 0; i < info->numaddresses; i++) {
2229                        g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email2[i]);
2230                }
2231        }
[5588edf5]2232        if (od->ips && (ip = (long) g_hash_table_lookup(od->ips, &info->uin)) != 0) {
[e64de00]2233                g_string_append_printf(str, "\n%s: %d.%d.%d.%d", _("Last used IP address"),
2234                                       (ip >> 24), (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
2235        }
2236        g_string_append_printf(str, "\n%s: %s", _("Mobile Phone"), info->mobile);
2237        if (info->gender != 0)
2238                g_string_append_printf(str, "\n%s: %s", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
2239        if (info->birthyear || info->birthmonth || info->birthday) {
2240                char date[30];
2241                struct tm tm;
[613cc55]2242                memset(&tm, 0, sizeof(struct tm));
[e64de00]2243                tm.tm_mday = (int)info->birthday;
2244                tm.tm_mon = (int)info->birthmonth-1;
2245                tm.tm_year = (int)info->birthyear%100;
2246                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2247                g_string_append_printf(str, "\n%s: %s", _("Birthday"), date);
2248        }
2249        if (info->age) {
2250                char age[5];
2251                g_snprintf(age, sizeof(age), "%hhd", info->age);
2252                g_string_append_printf(str, "\n%s: %s", _("Age"), age);
2253        }
2254        g_string_append_printf(str, "\n%s: %s", _("Personal Web Page"), info->personalwebpage);
2255        if (info->info && info->info[0]) {
2256                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2257                info->info, _("End of Additional Information"));
2258        }
2259        g_string_append_c(str, '\n');
2260        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2261                g_string_append_printf(str, "%s:", _("Home Address"));
2262                g_string_append_printf(str, "\n%s: %s", _("Address"), info->homeaddr);
2263                g_string_append_printf(str, "\n%s: %s", _("City"), info->homecity);
2264                g_string_append_printf(str, "\n%s: %s", _("State"), info->homestate); 
2265                g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->homezip);
2266                g_string_append_c(str, '\n');
2267        }
2268        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2269                g_string_append_printf(str, "%s:", _("Work Address"));
2270                g_string_append_printf(str, "\n%s: %s", _("Address"), info->workaddr);
2271                g_string_append_printf(str, "\n%s: %s", _("City"), info->workcity);
2272                g_string_append_printf(str, "\n%s: %s", _("State"), info->workstate);
2273                g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->workzip);
2274                g_string_append_c(str, '\n');
2275        }
2276        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2277                g_string_append_printf(str, "%s:", _("Work Information"));
2278                g_string_append_printf(str, "\n%s: %s", _("Company"), info->workcompany);
2279                g_string_append_printf(str, "\n%s: %s", _("Division"), info->workdivision);
2280                g_string_append_printf(str, "\n%s: %s", _("Position"), info->workposition);
2281                if (info->workwebpage && info->workwebpage[0]) {
2282                        g_string_append_printf(str, "\n%s: %s", _("Web Page"), info->workwebpage);
2283                }
2284                g_string_append_c(str, '\n');
2285        }
2286
2287        imcb_log(ic, "%s\n%s", _("User Info"), str->str);
2288        g_string_free(str, TRUE);
2289
2290        return 1;
[b7d3cc34]2291
2292}
2293
2294static char *oscar_encoding_extract(const char *encoding)
2295{
2296        char *ret = NULL;
2297        char *begin, *end;
2298
2299        g_return_val_if_fail(encoding != NULL, NULL);
2300
2301        /* Make sure encoding begins with charset= */
2302        if (strncmp(encoding, "text/plain; charset=", 20) &&
2303                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2304                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2305        {
2306                return NULL;
2307        }
2308
2309        begin = strchr(encoding, '"');
2310        end = strrchr(encoding, '"');
2311
2312        if ((begin == NULL) || (end == NULL) || (begin >= end))
2313                return NULL;
2314
2315        ret = g_strndup(begin+1, (end-1) - begin);
2316
2317        return ret;
2318}
2319
2320static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2321{
2322        char *utf8 = g_new0(char, 8192);
2323
2324        if ((encoding == NULL) || encoding[0] == '\0') {
2325                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2326        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2327                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2328        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2329                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2330        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2331                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2332        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2333                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2334                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2335                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2336        }
2337
2338        /*
2339         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2340         * we have been unable to convert the text to utf-8 from the encoding
2341         * that was specified.  So we assume it's UTF-8 and hope for the best.
2342         */
2343        if (*utf8 == 0) {
2344            strncpy(utf8, text, textlen);
2345        }
2346
2347        return utf8;
2348}
2349
2350static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2351{
[0da65d5]2352        struct im_connection *ic = sess->aux_data;
[b7d3cc34]2353        va_list ap;
2354        aim_userinfo_t *userinfo;
2355        guint16 infotype;
2356        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2357        guint16 text_length;
2358        char *utf8 = NULL;
2359
2360        va_start(ap, fr);
2361        userinfo = va_arg(ap, aim_userinfo_t *);
2362        infotype = va_arg(ap, int);
2363        text_encoding = va_arg(ap, char*);
2364        text = va_arg(ap, char*);
2365        text_length = va_arg(ap, int);
2366        va_end(ap);
2367
2368        if(text_encoding)
2369                extracted_encoding = oscar_encoding_extract(text_encoding);
2370        if(infotype == AIM_GETINFO_GENERALINFO) {
2371                /*Display idle time*/
2372                char buff[256];
2373                struct tm idletime;
2374                if(userinfo->idletime) {
2375                        memset(&idletime, 0, sizeof(struct tm));
2376                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2377                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2378                        idletime.tm_min = userinfo->idletime % 60;
2379                        idletime.tm_sec = 0;
2380                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
[84b045d]2381                        imcb_log(ic, "%s: %s", _("Idle Time"), buff);
[b7d3cc34]2382                }
2383               
2384                if(text) {
2385                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
[84b045d]2386                        imcb_log(ic, "%s\n%s", _("User Info"), utf8);
[b7d3cc34]2387                } else {
[84b045d]2388                        imcb_log(ic, _("No user info available."));
[b7d3cc34]2389                }
2390        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2391                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
[84b045d]2392                imcb_log(ic, "%s\n%s", _("Away Message"), utf8);
[b7d3cc34]2393        }
2394
2395        g_free(utf8);
2396   
2397        return 1;
2398}
2399
[3e1de87]2400int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)
2401{
[0da65d5]2402        struct im_connection * ic = sess->aux_data;
[3e1de87]2403        va_list ap;
2404        guint16 type1, type2;
2405        char * sn;
2406
2407        va_start(ap, fr);
2408        type1 = va_arg(ap, int);
2409        sn = va_arg(ap, char*);
2410        type2 = va_arg(ap, int);
2411        va_end(ap);
[e7f46c5]2412   
2413        if(type2 == 0x0002) {
2414                /* User is typing */
[424e663]2415                imcb_buddy_typing(ic, normalize(sn), OPT_TYPING);
[e7f46c5]2416        } 
2417        else if (type2 == 0x0001) {
2418                /* User has typed something, but is not actively typing (stale) */
[424e663]2419                imcb_buddy_typing(ic, normalize(sn), OPT_THINKING);
[e7f46c5]2420        }
2421        else {
2422                /* User has stopped typing */
[424e663]2423                imcb_buddy_typing(ic, normalize(sn), 0);
[e64de00]2424        }
[e7f46c5]2425       
[3e1de87]2426        return 1;
2427}
2428
[0da65d5]2429int oscar_send_typing(struct im_connection *ic, char * who, int typing)
[3e1de87]2430{
[0da65d5]2431        struct oscar_data *od = ic->proto_data;
[df1fb67]2432        return( aim_im_sendmtn(od->sess, 1, who, (typing & OPT_TYPING) ? 0x0002 : 0x0000) );
[3e1de87]2433}
2434
[f6c963b]2435void oscar_chat_msg(struct groupchat *c, char *message, int msgflags)
[b8ef1b1]2436{
[0da65d5]2437        struct im_connection *ic = c->ic;
2438        struct oscar_data * od = (struct oscar_data*)ic->proto_data;
[b8ef1b1]2439        struct chat_connection * ccon;
2440        int ret;
2441        guint8 len = strlen(message);
[73cf7fd]2442        guint16 flags;
[b8ef1b1]2443        char *s;
[11e090b]2444       
[e437366]2445        if (!(ccon = c->data))
2446                return;
[b8ef1b1]2447               
2448        for (s = message; *s; s++)
2449                if (*s & 128)
2450                        break;
[73cf7fd]2451       
2452        flags = AIM_CHATFLAGS_NOREFLECT;
2453       
[b8ef1b1]2454        /* Message contains high ASCII chars, time for some translation! */
2455        if (*s) {
2456                s = g_malloc(BUF_LONG);
2457                /* Try if we can put it in an ISO8859-1 string first.
2458                   If we can't, fall back to UTF16. */
2459                if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
[73cf7fd]2460                        flags |= AIM_CHATFLAGS_ISO_8859_1;
[b8ef1b1]2461                        len = ret;
2462                } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
[73cf7fd]2463                        flags |= AIM_CHATFLAGS_UNICODE;
[b8ef1b1]2464                        len = ret;
2465                } else {
2466                        /* OOF, translation failed... Oh well.. */
2467                        g_free( s );
2468                        s = message;
2469                }
2470        } else {
2471                s = message;
2472        }
2473               
[73cf7fd]2474        ret = aim_chat_send_im(od->sess, ccon->conn, flags, s, len);
[b8ef1b1]2475               
2476        if (s != message) {     
2477                g_free(s);
2478  }
2479 
[0da65d5]2480/*  return (ret >= 0); */
[b8ef1b1]2481}
2482
[c058ff9]2483void oscar_chat_invite(struct groupchat *c, char *who, char *message)
[b8ef1b1]2484{
[0da65d5]2485        struct im_connection *ic = c->ic;
2486        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
[e437366]2487        struct chat_connection *ccon;
2488       
2489        if (!(ccon = c->data))
2490                return;
[b8ef1b1]2491       
2492        aim_chat_invite(od->sess, od->conn, who, message ? message : "",
2493                                        ccon->exchange, ccon->name, 0x0);
2494}
2495
[0da65d5]2496void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc)
[b8ef1b1]2497{
[0da65d5]2498        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b8ef1b1]2499
2500        /* Notify the conversation window that we've left the chat */
[e35d1a1]2501        imcb_chat_free(cc->cnv);
[b8ef1b1]2502
2503        /* Destroy the chat_connection */
2504        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
2505        if (cc->inpa > 0)
[ba9edaa]2506                b_event_remove(cc->inpa);
[b8ef1b1]2507        aim_conn_kill(od->sess, &cc->conn);
2508        g_free(cc->name);
2509        g_free(cc->show);
2510        g_free(cc);
2511}
2512
[0da65d5]2513void oscar_chat_leave(struct groupchat *c)
[b8ef1b1]2514{
[e437366]2515        if (!c->data)
2516                return;
[0da65d5]2517        oscar_chat_kill(c->ic, c->data);
[b8ef1b1]2518}
2519
[03f3828]2520struct groupchat *oscar_chat_join_internal(struct im_connection *ic, const char *room,
2521        const char *nick, const char *password, int exchange_number)
[b8ef1b1]2522{
[0da65d5]2523        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
[7989d40d]2524        struct groupchat *ret = imcb_chat_new(ic, room);
[b8ef1b1]2525        aim_conn_t * cur;
2526
2527        if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
[c737ba7]2528                int st;
2529               
[03f3828]2530                st = aim_chatnav_createroom(od->sess, cur, room, exchange_number);
[c737ba7]2531               
[7989d40d]2532                return ret;
[b8ef1b1]2533        } else {
2534                struct create_room * cr = g_new0(struct create_room, 1);
[c737ba7]2535               
[03f3828]2536                cr->exchange = exchange_number;
[c737ba7]2537                cr->name = g_strdup(room);
[b8ef1b1]2538                od->create_rooms = g_slist_append(od->create_rooms, cr);
2539                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
[c737ba7]2540               
[7989d40d]2541                return ret;
[b8ef1b1]2542        }
2543}
2544
[03f3828]2545struct groupchat *oscar_chat_join(struct im_connection *ic, const char *room,
2546        const char *nick, const char *password, set_t **sets)
2547{
2548        return oscar_chat_join_internal(ic, room, nick, password, set_getint(sets, "exchange_number"));
2549}
2550
[0da65d5]2551struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)
[b8ef1b1]2552{
[0da65d5]2553        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
[c737ba7]2554        struct groupchat *ret;
[b8ef1b1]2555        static int chat_id = 0;
[e437366]2556        char * chatname, *s;
[75610c3]2557        struct groupchat *c;
[11e090b]2558       
[ef14a83]2559        chatname = g_strdup_printf("%s%s%d", isdigit(*ic->acc->user) ? "icq" : "",
[75610c3]2560                                   ic->acc->user, chat_id++);
2561       
[e437366]2562        for (s = chatname; *s; s ++)
2563                if (!isalnum(*s))
2564                        *s = '0';
2565       
[75610c3]2566        c = imcb_chat_new(ic, chatname);
[03f3828]2567        ret = oscar_chat_join_internal(ic, chatname, NULL, NULL, 4);
[b8ef1b1]2568        aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);
2569
2570        g_free(chatname);
2571       
[fa29d093]2572        return NULL;
[b8ef1b1]2573}
2574
[9143aeb]2575void oscar_accept_chat(void *data)
[b8ef1b1]2576{
[9143aeb]2577        struct aim_chat_invitation * inv = data;
2578       
[03f3828]2579        oscar_chat_join_internal(inv->ic, inv->name, NULL, NULL, 4);
[b8ef1b1]2580        g_free(inv->name);
2581        g_free(inv);
2582}
2583
[9143aeb]2584void oscar_reject_chat(void *data)
[b8ef1b1]2585{
[9143aeb]2586        struct aim_chat_invitation * inv = data;
2587       
[b8ef1b1]2588        g_free(inv->name);
2589        g_free(inv);
2590}
2591
[03f3828]2592void oscar_chat_add_settings(account_t *acc, set_t **head)
2593{
2594        set_add(head, "exchange_number", "4", set_eval_int, NULL);
2595}
2596
2597void oscar_chat_free_settings(account_t *acc, set_t **head)
2598{
2599        set_del(head, "exchange_number");
2600}
2601
[0da65d5]2602void oscar_initmodule() 
[7b23afd]2603{
2604        struct prpl *ret = g_new0(struct prpl, 1);
2605        ret->name = "oscar";
[b7d3cc34]2606        ret->away_states = oscar_away_states;
[0da65d5]2607        ret->init = oscar_init;
[b7d3cc34]2608        ret->login = oscar_login;
[0da65d5]2609        ret->keepalive = oscar_keepalive;
2610        ret->logout = oscar_logout;
[f6c963b]2611        ret->buddy_msg = oscar_buddy_msg;
[b7d3cc34]2612        ret->get_info = oscar_get_info;
2613        ret->set_away = oscar_set_away;
2614        ret->get_away = oscar_get_away;
2615        ret->add_buddy = oscar_add_buddy;
2616        ret->remove_buddy = oscar_remove_buddy;
[f6c963b]2617        ret->chat_msg = oscar_chat_msg;
[b8ef1b1]2618        ret->chat_invite = oscar_chat_invite;
2619        ret->chat_leave = oscar_chat_leave;
[0da65d5]2620        ret->chat_with = oscar_chat_with;
[c737ba7]2621        ret->chat_join = oscar_chat_join;
[03f3828]2622        ret->chat_add_settings = oscar_chat_add_settings;
2623        ret->chat_free_settings = oscar_chat_free_settings;
[b7d3cc34]2624        ret->add_permit = oscar_add_permit;
2625        ret->add_deny = oscar_add_deny;
2626        ret->rem_permit = oscar_rem_permit;
2627        ret->rem_deny = oscar_rem_deny;
2628        ret->set_permit_deny = oscar_set_permit_deny;
[3e1de87]2629        ret->send_typing = oscar_send_typing;
[5b52a48]2630       
2631        ret->handle_cmp = aim_sncmp;
[3e1de87]2632
[7b23afd]2633        register_protocol(ret);
[b7d3cc34]2634}
Note: See TracBrowser for help on using the repository browser.