source: protocols/oscar/oscar.c @ 57a65600

Last change on this file since 57a65600 was b041b52, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-06T21:50:43Z

Merging compiler warning fixes from vmiklos.

This change also uncovered one bug in groupchat handling in OSCAR, which
I fixed during the merge.

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