source: protocols/oscar/oscar.c @ 75610c3

Last change on this file since 75610c3 was 75610c3, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-09T00:40:54Z

Fixed up OSCAR to work with the new way of setting up groupchats.

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