source: protocols/oscar/oscar.c @ e5abfd4

Last change on this file since e5abfd4 was e5abfd4, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-09T11:26:57Z

Safety check for yesterday's fixes: Double-check that a groupchat struct
isn't claimed already.

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