source: protocols/oscar/oscar.c @ df61c35a

Last change on this file since df61c35a was df61c35a, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-30T19:24:28Z

Warn the user when his/her AIM/ICQ password seems to be too long. For some
people with long passwords the removal of old ICQ-only authentication
introduced login issues since the extra chars in the password upset the
server.

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