source: protocols/oscar/oscar.c @ 3429b58

Last change on this file since 3429b58 was 3429b58, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-13T22:05:13Z

Mainline merge.

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