source: protocols/oscar/oscar.c @ ca974d7

Last change on this file since ca974d7 was 6a45181, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-10-20T03:28:29Z

Clearer error msg when trying to add an existing OSCAR contact. Bug #828.

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