source: protocols/oscar/oscar.c @ ba3233c

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

Oi, don't leak that precious memory!

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