source: protocols/oscar/oscar.c @ eb6df6a

Last change on this file since eb6df6a was 3759849, checked in by Sven Moritz Hallberg <pesco@…>, at 2010-06-03T22:08:23Z

merge in bitlbee head

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