source: protocols/oscar/oscar.c @ 552e641

Last change on this file since 552e641 was aef4828, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-04-06T05:20:31Z

More cleanups, mainly in the callbacks. Replaced things like
do_error_dialog() and (set|hide)_login_progress(_error)?() with things
that hopefully make more sense.

Although it's still not really great...

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