source: protocols/oscar/oscar.c @ c9c7ca7

Last change on this file since c9c7ca7 was e900442, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-07-05T23:39:47Z

Merging from devel. Added documentation for the join_chat command, adding
a debian/ tree and a merge from Jelmer (mainly unittest stuff).

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