source: protocols/oscar/oscar.c @ d4bc2d9

Last change on this file since d4bc2d9 was d4bc2d9, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-16T09:46:27Z

Handle AIM_CAPS_ICQRTF messages (sometimes sent by certain mobile clients).
Not sure if this needs any stripping but I guess we'll find out.

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