source: protocols/oscar/oscar.c @ 181e47a

Last change on this file since 181e47a was 181e47a, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-01-10T00:31:38Z

Now setting odata->icq properly again, this got lost some time ago, which
broke the info command and probably more things.

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