source: protocols/oscar/oscar.c @ d7f8500

Last change on this file since d7f8500 was 56699f0, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-07T15:11:09Z

Show idle + login time info in /WHOIS (if available).

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