source: protocols/oscar/oscar.c @ 84b045d

Last change on this file since 84b045d was 84b045d, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-04-16T01:03:08Z

s/imc/imcb/ for callback functions. Moved things aroundin nogaim.h a
little bit, grouping things by category instead of original Gaim 0.58
filename.

  • Property mode set to 100644
File size: 74.7 KB
Line 
1/*
2 * gaim
3 *
4 * Some code copyright (C) 2002-2006, Jelmer Vernooij <jelmer@samba.org>
5 *                                    and the BitlBee team.
6 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
7 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 *
23 */
24
25#include <errno.h>
26#include <ctype.h>
27#include <string.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <time.h>
31#include <sys/stat.h>
32#include <glib.h>
33#include "nogaim.h"
34#include "bitlbee.h"
35#include "proxy.h"
36#include "sock.h"
37
38#include "aim.h"
39#include "icq.h"
40#include "bos.h"
41#include "ssi.h"
42#include "im.h"
43#include "info.h"
44#include "buddylist.h"
45#include "chat.h"
46#include "chatnav.h"
47
48/* constants to identify proto_opts */
49#define USEROPT_AUTH      0
50#define USEROPT_AUTHPORT  1
51
52#define UC_AOL          0x02
53#define UC_ADMIN        0x04
54#define UC_UNCONFIRMED  0x08
55#define UC_NORMAL       0x10
56#define UC_AB           0x20
57#define UC_WIRELESS     0x40
58
59#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
60
61#define OSCAR_GROUP "Friends"
62
63/* Don't know if support for UTF8 is really working. For now it's UTF16 here.
64   static int gaim_caps = AIM_CAPS_UTF8; */
65
66static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY | AIM_CAPS_CHAT;
67static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02};
68
69struct oscar_data {
70        aim_session_t *sess;
71        aim_conn_t *conn;
72
73        guint cnpa;
74        guint paspa;
75
76        GSList *create_rooms;
77
78        gboolean conf;
79        gboolean reqemail;
80        gboolean setemail;
81        char *email;
82        gboolean setnick;
83        char *newsn;
84        gboolean chpass;
85        char *oldp;
86        char *newp;
87
88        GSList *oscar_chats;
89
90        gboolean killme;
91        gboolean icq;
92        GSList *evilhack;
93
94        struct {
95                guint maxbuddies; /* max users you can watch */
96                guint maxwatchers; /* max users who can watch you */
97                guint maxpermits; /* max users on permit list */
98                guint maxdenies; /* max users on deny list */
99                guint maxsiglen; /* max size (bytes) of profile */
100                guint maxawaymsglen; /* max size (bytes) of posted away message */
101        } rights;
102};
103
104struct create_room {
105        char *name;
106        int exchange;
107};
108
109struct chat_connection {
110        char *name;
111        char *show; /* AOL did something funny to us */
112        guint16 exchange;
113        guint16 instance;
114        int fd; /* this is redundant since we have the conn below */
115        aim_conn_t *conn;
116        int inpa;
117        int id;
118        struct im_connection *ic; /* i hate this. */
119        struct groupchat *cnv; /* bah. */
120        int maxlen;
121        int maxvis;
122};
123
124struct ask_direct {
125        struct im_connection *ic;
126        char *sn;
127        char ip[64];
128        guint8 cookie[8];
129};
130
131struct icq_auth {
132        struct im_connection *ic;
133        guint32 uin;
134};
135
136static char *extract_name(const char *name) {
137        char *tmp;
138        int i, j;
139        char *x = strchr(name, '-');
140        if (!x) return g_strdup(name);
141        x = strchr(++x, '-');
142        if (!x) return g_strdup(name);
143        tmp = g_strdup(++x);
144
145        for (i = 0, j = 0; x[i]; i++) {
146                char hex[3];
147                if (x[i] != '%') {
148                        tmp[j++] = x[i];
149                        continue;
150                }
151                strncpy(hex, x + ++i, 2); hex[2] = 0;
152                i++;
153                tmp[j++] = (char)strtol(hex, NULL, 16);
154        }
155
156        tmp[j] = 0;
157        return tmp;
158}
159
160static struct chat_connection *find_oscar_chat_by_conn(struct im_connection *ic,
161                                                        aim_conn_t *conn) {
162        GSList *g = ((struct oscar_data *)ic->proto_data)->oscar_chats;
163        struct chat_connection *c = NULL;
164
165        while (g) {
166                c = (struct chat_connection *)g->data;
167                if (c->conn == conn)
168                        break;
169                g = g->next;
170                c = NULL;
171        }
172
173        return c;
174}
175
176static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
177static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
178static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
179static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
180static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
181static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
182static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
183static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
184static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
185static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
186static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
187static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
188static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
189static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
190static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
191static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
192static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
193static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
194static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
195static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
196static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
197static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
198static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
199static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
200static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
201static int gaim_memrequest       (aim_session_t *, aim_frame_t *, ...);
202static int gaim_selfinfo         (aim_session_t *, aim_frame_t *, ...);
203static int gaim_offlinemsg       (aim_session_t *, aim_frame_t *, ...);
204static int gaim_offlinemsgdone   (aim_session_t *, aim_frame_t *, ...);
205static int gaim_ssi_parserights  (aim_session_t *, aim_frame_t *, ...);
206static int gaim_ssi_parselist    (aim_session_t *, aim_frame_t *, ...);
207static int gaim_ssi_parseack     (aim_session_t *, aim_frame_t *, ...);
208static int gaim_parsemtn         (aim_session_t *, aim_frame_t *, ...);
209static int gaim_icqinfo          (aim_session_t *, aim_frame_t *, ...);
210static int gaim_parseaiminfo     (aim_session_t *, aim_frame_t *, ...);
211
212static char *msgerrreason[] = {
213        "Invalid error",
214        "Invalid SNAC",
215        "Rate to host",
216        "Rate to client",
217        "Not logged in",
218        "Service unavailable",
219        "Service not defined",
220        "Obsolete SNAC",
221        "Not supported by host",
222        "Not supported by client",
223        "Refused by client",
224        "Reply too big",
225        "Responses lost",
226        "Request denied",
227        "Busted SNAC payload",
228        "Insufficient rights",
229        "In local permit/deny",
230        "Too evil (sender)",
231        "Too evil (receiver)",
232        "User temporarily unavailable",
233        "No match",
234        "List overflow",
235        "Request ambiguous",
236        "Queue full",
237        "Not while on AOL"
238};
239static int msgerrreasonlen = 25;
240
241static gboolean oscar_callback(gpointer data, gint source,
242                                b_input_condition condition) {
243        aim_conn_t *conn = (aim_conn_t *)data;
244        aim_session_t *sess = aim_conn_getsess(conn);
245        struct im_connection *ic = sess ? sess->aux_data : NULL;
246        struct oscar_data *odata;
247
248        if (!ic) {
249                /* ic is null. we return, else we seg SIGSEG on next line. */
250                return FALSE;
251        }
252     
253        if (!g_slist_find(get_connections(), ic)) {
254                /* oh boy. this is probably bad. i guess the only thing we
255                 * can really do is return? */
256                return FALSE;
257        }
258
259        odata = (struct oscar_data *)ic->proto_data;
260
261        if (condition & GAIM_INPUT_READ) {
262                if (aim_get_command(odata->sess, conn) >= 0) {
263                        aim_rxdispatch(odata->sess);
264                               if (odata->killme)
265                                       imc_logout(ic, 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_CONN_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 = serv_got_joined_chat(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 type = 0;
971        int caps = 0;
972        char *tmp;
973
974        va_list ap;
975        va_start(ap, fr);
976        info = va_arg(ap, aim_userinfo_t *);
977        va_end(ap);
978
979        if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
980                caps = info->capabilities;
981        if (info->flags & AIM_FLAG_ACTIVEBUDDY)
982                type |= UC_AB;
983
984        if ((!od->icq) && (info->present & AIM_USERINFO_PRESENT_FLAGS)) {
985                if (info->flags & AIM_FLAG_UNCONFIRMED)
986                        type |= UC_UNCONFIRMED;
987                if (info->flags & AIM_FLAG_ADMINISTRATOR)
988                        type |= UC_ADMIN;
989                if (info->flags & AIM_FLAG_AOL)
990                        type |= UC_AOL;
991                if (info->flags & AIM_FLAG_FREE)
992                        type |= UC_NORMAL;
993                if (info->flags & AIM_FLAG_AWAY)
994                        type |= UC_UNAVAILABLE;
995                if (info->flags & AIM_FLAG_WIRELESS)
996                        type |= UC_WIRELESS;
997        }
998        if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
999                type = (info->icqinfo.status << 7);
1000                if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
1001                      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
1002                        type |= UC_UNAVAILABLE;
1003                }
1004        }
1005
1006        if (caps & AIM_CAPS_ICQ)
1007                caps ^= AIM_CAPS_ICQ;
1008
1009        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
1010                time(&time_idle);
1011                time_idle -= info->idletime*60;
1012        }
1013
1014        if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
1015                signon = time(NULL) - info->sessionlen;
1016
1017        tmp = g_strdup(normalize(ic->acc->user));
1018        if (!strcmp(tmp, normalize(info->sn)))
1019                g_snprintf(ic->displayname, sizeof(ic->displayname), "%s", info->sn);
1020        g_free(tmp);
1021
1022        serv_got_update(ic, info->sn, 1, info->warnlevel/10, signon,
1023                        time_idle, type, caps);
1024
1025        return 1;
1026}
1027
1028static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
1029        aim_userinfo_t *info;
1030        va_list ap;
1031        struct im_connection *ic = sess->aux_data;
1032
1033        va_start(ap, fr);
1034        info = va_arg(ap, aim_userinfo_t *);
1035        va_end(ap);
1036
1037        serv_got_update(ic, info->sn, 0, 0, 0, 0, 0, 0);
1038
1039        return 1;
1040}
1041
1042static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
1043        char *tmp = g_malloc(BUF_LONG + 1);
1044        struct im_connection *ic = sess->aux_data;
1045        int flags = 0;
1046       
1047        if (args->icbmflags & AIM_IMFLAGS_AWAY)
1048                flags |= IM_FLAG_AWAY;
1049       
1050        if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) {
1051                char *src;
1052               
1053                if (args->icbmflags & AIM_IMFLAGS_UNICODE)
1054                        src = "UNICODEBIG";
1055                else
1056                        src = "ISO8859-1";
1057               
1058                /* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */
1059                if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) {
1060                        // Successfully converted!
1061                } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
1062                        int i;
1063                       
1064                        for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
1065                                unsigned short uni;
1066                               
1067                                uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1068       
1069                                if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1070                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
1071                                } else { /* something else, do UNICODE entity */
1072                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
1073                                }
1074                        }
1075                } else {
1076                        g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1077                }
1078        } else
1079                g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1080       
1081        strip_linefeed(tmp);
1082        serv_got_im(ic, userinfo->sn, tmp, flags, time(NULL), -1);
1083        g_free(tmp);
1084       
1085        return 1;
1086}
1087
1088void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv);
1089void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv);
1090       
1091static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
1092        struct im_connection *ic = sess->aux_data;
1093
1094        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1095                return 1;
1096
1097        if (args->reqclass & AIM_CAPS_CHAT) {
1098                char *name = extract_name(args->info.chat.roominfo.name);
1099                int *exch = g_new0(int, 1);
1100                GList *m = NULL;
1101                char txt[1024];
1102                struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1);
1103
1104                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1105                *exch = args->info.chat.roominfo.exchange;
1106                m = g_list_append(m, exch);
1107
1108                g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg );
1109
1110                inv->ic = ic;
1111                inv->exchange = *exch;
1112                inv->name = g_strdup(name);
1113               
1114                imcb_ask( ic, txt, inv, oscar_accept_chat, oscar_reject_chat);
1115       
1116                if (name)
1117                        g_free(name);
1118        }
1119
1120        return 1;
1121}
1122
1123static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
1124        char *uin, message;
1125        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
1126       
1127        uin = g_strdup_printf("%u", data->uin);
1128        message = 0;
1129        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1130        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
1131        if(find_buddy(data->ic, uin) == NULL)
1132                imcb_ask_add(data->ic, uin, NULL);
1133       
1134        g_free(uin);
1135        g_free(data);
1136}
1137
1138static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
1139        char *uin, *message;
1140        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
1141       
1142        uin = g_strdup_printf("%u", data->uin);
1143        message = g_strdup_printf("No reason given.");
1144        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1145        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1146        g_free(message);
1147       
1148        g_free(uin);
1149        g_free(data);
1150}
1151
1152/*
1153 * For when other people ask you for authorization
1154 */
1155static void gaim_icq_authask(struct im_connection *ic, guint32 uin, char *msg) {
1156        struct icq_auth *data = g_new(struct icq_auth, 1);
1157        char *reason = NULL;
1158        char *dialog_msg;
1159       
1160        if (strlen(msg) > 6)
1161                reason = msg + 6;
1162       
1163        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.");
1164        data->ic = ic;
1165        data->uin = uin;
1166        imcb_ask(ic, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
1167        g_free(dialog_msg);
1168}
1169
1170static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
1171        struct im_connection *ic = sess->aux_data;
1172
1173        switch (args->type) {
1174                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1175                        char *uin, *message;
1176                        uin = g_strdup_printf("%u", args->uin);
1177                        message = g_strdup(args->msg);
1178                        strip_linefeed(message);
1179                        serv_got_im(ic, uin, message, 0, time(NULL), -1);
1180                        g_free(uin);
1181                        g_free(message);
1182                } break;
1183
1184                case 0x0004: { /* Someone sent you a URL */
1185                        char *uin, *message;
1186                        char **m;
1187       
1188                        uin = g_strdup_printf("%u", args->uin);
1189                        m = g_strsplit(args->msg, "\376", 2);
1190
1191                        if ((strlen(m[0]) != 0)) {
1192                          message = g_strjoinv(" -- ", m);
1193                        } else {
1194                          message = m[1];
1195                        }
1196
1197                        strip_linefeed(message);
1198                        serv_got_im(ic, uin, message, 0, time(NULL), -1);
1199                        g_free(uin);
1200                        g_free(m);
1201                        g_free(message);
1202                } break;
1203               
1204                case 0x0006: { /* Someone requested authorization */
1205                        gaim_icq_authask(ic, args->uin, args->msg);
1206                } break;
1207
1208                case 0x0007: { /* Someone has denied you authorization */
1209                        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.") );
1210                } break;
1211
1212                case 0x0008: { /* Someone has granted you authorization */
1213                        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.") );
1214                } break;
1215
1216                case 0x0012: {
1217                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1218                } break;
1219
1220                default: {;
1221                } break;
1222        }
1223
1224        return 1;
1225}
1226
1227static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1228        int channel, ret = 0;
1229        aim_userinfo_t *userinfo;
1230        va_list ap;
1231
1232        va_start(ap, fr);
1233        channel = va_arg(ap, int);
1234        userinfo = va_arg(ap, aim_userinfo_t *);
1235
1236        switch (channel) {
1237                case 1: { /* standard message */
1238                        struct aim_incomingim_ch1_args *args;
1239                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1240                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1241                } break;
1242
1243                case 2: { /* rendevous */
1244                        struct aim_incomingim_ch2_args *args;
1245                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1246                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1247                } break;
1248
1249                case 4: { /* ICQ */
1250                        struct aim_incomingim_ch4_args *args;
1251                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1252                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1253                } break;
1254
1255                default: {;
1256                } break;
1257        }
1258
1259        va_end(ap);
1260
1261        return ret;
1262}
1263
1264static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1265        va_list ap;
1266        guint16 chan, nummissed, reason;
1267        aim_userinfo_t *userinfo;
1268
1269        va_start(ap, fr);
1270        chan = (guint16)va_arg(ap, unsigned int);
1271        userinfo = va_arg(ap, aim_userinfo_t *);
1272        nummissed = (guint16)va_arg(ap, unsigned int);
1273        reason = (guint16)va_arg(ap, unsigned int);
1274        va_end(ap);
1275
1276        switch(reason) {
1277                case 0:
1278                        /* Invalid (0) */
1279                        imcb_error(sess->aux_data,
1280                                   nummissed == 1 ? 
1281                                   _("You missed %d message from %s because it was invalid.") :
1282                                   _("You missed %d messages from %s because they were invalid."),
1283                                   nummissed,
1284                                   userinfo->sn);
1285                        break;
1286                case 1:
1287                        /* Message too large */
1288                        imcb_error(sess->aux_data,
1289                                   nummissed == 1 ?
1290                                   _("You missed %d message from %s because it was too large.") :
1291                                   _("You missed %d messages from %s because they were too large."),
1292                                   nummissed,
1293                                   userinfo->sn);
1294                        break;
1295                case 2:
1296                        /* Rate exceeded */
1297                        imcb_error(sess->aux_data,
1298                                   nummissed == 1 ? 
1299                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1300                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1301                                   nummissed,
1302                                   userinfo->sn);
1303                        break;
1304                case 3:
1305                        /* Evil Sender */
1306                        imcb_error(sess->aux_data,
1307                                   nummissed == 1 ?
1308                                   _("You missed %d message from %s because it was too evil.") : 
1309                                   _("You missed %d messages from %s because they are too evil."),
1310                                   nummissed,
1311                                   userinfo->sn);
1312                        break;
1313                case 4:
1314                        /* Evil Receiver */
1315                        imcb_error(sess->aux_data,
1316                                   nummissed == 1 ? 
1317                                   _("You missed %d message from %s because you are too evil.") :
1318                                   _("You missed %d messages from %s because you are too evil."),
1319                                   nummissed,
1320                                   userinfo->sn);
1321                        break;
1322                default:
1323                        imcb_error(sess->aux_data,
1324                                   nummissed == 1 ? 
1325                                   _("You missed %d message from %s for unknown reasons.") :
1326                                   _("You missed %d messages from %s for unknown reasons."),
1327                                   nummissed,
1328                                   userinfo->sn);
1329                        break;
1330        }
1331
1332        return 1;
1333}
1334
1335static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1336        va_list ap;
1337        guint16 reason;
1338
1339        va_start(ap, fr);
1340        reason = (guint16)va_arg(ap, unsigned int);
1341        va_end(ap);
1342
1343        imcb_error(sess->aux_data, _("SNAC threw error: %s"),
1344                  reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1345
1346        return 1;
1347}
1348
1349static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1350        va_list ap;
1351        char *destn;
1352        guint16 reason;
1353
1354        va_start(ap, fr);
1355        reason = (guint16)va_arg(ap, unsigned int);
1356        destn = va_arg(ap, char *);
1357        va_end(ap);
1358
1359        imcb_error(sess->aux_data, _("Your message to %s did not get sent: %s"), destn,
1360                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1361
1362        return 1;
1363}
1364
1365static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1366        va_list ap;
1367        char *destn;
1368        guint16 reason;
1369
1370        va_start(ap, fr);
1371        reason = (guint16)va_arg(ap, unsigned int);
1372        destn = va_arg(ap, char *);
1373        va_end(ap);
1374
1375        imcb_error(sess->aux_data, _("User information for %s unavailable: %s"), destn,
1376                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1377
1378        return 1;
1379}
1380
1381static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1382        char *msg;
1383        guint16 id;
1384        va_list ap;
1385
1386        va_start(ap, fr);
1387        id  = (guint16)va_arg(ap, unsigned int);
1388        msg = va_arg(ap, char *);
1389        va_end(ap);
1390
1391        if (id < 4)
1392                imcb_error(sess->aux_data, _("Your connection may be lost."));
1393
1394        return 1;
1395}
1396
1397static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1398        va_list ap;
1399        guint16 type;
1400        struct im_connection *ic = sess->aux_data;
1401        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1402
1403        va_start(ap, fr);
1404        type = (guint16)va_arg(ap, unsigned int);
1405
1406        switch(type) {
1407                case 0x0002: {
1408                        guint8 maxrooms;
1409                        struct aim_chat_exchangeinfo *exchanges;
1410                        int exchangecount; // i;
1411
1412                        maxrooms = (guint8)va_arg(ap, unsigned int);
1413                        exchangecount = va_arg(ap, int);
1414                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1415                        va_end(ap);
1416
1417                        while (odata->create_rooms) {
1418                                struct create_room *cr = odata->create_rooms->data;
1419                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1420                                g_free(cr->name);
1421                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1422                                g_free(cr);
1423                        }
1424                        }
1425                        break;
1426                case 0x0008: {
1427                        char *fqcn, *name, *ck;
1428                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1429                        guint8 createperms;
1430                        guint32 createtime;
1431
1432                        fqcn = va_arg(ap, char *);
1433                        instance = (guint16)va_arg(ap, unsigned int);
1434                        exchange = (guint16)va_arg(ap, unsigned int);
1435                        flags = (guint16)va_arg(ap, unsigned int);
1436                        createtime = va_arg(ap, guint32);
1437                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1438                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1439                        createperms = (guint8)va_arg(ap, int);
1440                        unknown = (guint16)va_arg(ap, unsigned int);
1441                        name = va_arg(ap, char *);
1442                        ck = va_arg(ap, char *);
1443                        va_end(ap);
1444
1445                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1446                        }
1447                        break;
1448                default:
1449                        va_end(ap);
1450                        break;
1451        }
1452        return 1;
1453}
1454
1455static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1456        va_list ap;
1457        int count, i;
1458        aim_userinfo_t *info;
1459        struct im_connection *g = sess->aux_data;
1460
1461        struct chat_connection *c = NULL;
1462
1463        va_start(ap, fr);
1464        count = va_arg(ap, int);
1465        info  = va_arg(ap, aim_userinfo_t *);
1466        va_end(ap);
1467
1468        c = find_oscar_chat_by_conn(g, fr->conn);
1469        if (!c)
1470                return 1;
1471
1472        for (i = 0; i < count; i++)
1473                add_chat_buddy(c->cnv, info[i].sn);
1474
1475        return 1;
1476}
1477
1478static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1479        va_list ap;
1480        int count, i;
1481        aim_userinfo_t *info;
1482        struct im_connection *g = sess->aux_data;
1483
1484        struct chat_connection *c = NULL;
1485
1486        va_start(ap, fr);
1487        count = va_arg(ap, int);
1488        info  = va_arg(ap, aim_userinfo_t *);
1489        va_end(ap);
1490
1491        c = find_oscar_chat_by_conn(g, fr->conn);
1492        if (!c)
1493                return 1;
1494
1495        for (i = 0; i < count; i++)
1496                remove_chat_buddy(c->cnv, info[i].sn, NULL);
1497
1498        return 1;
1499}
1500
1501static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1502        va_list ap;
1503        aim_userinfo_t *userinfo;
1504        struct aim_chat_roominfo *roominfo;
1505        char *roomname;
1506        int usercount;
1507        char *roomdesc;
1508        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1509        guint32 creationtime;
1510        struct im_connection *ic = sess->aux_data;
1511        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
1512
1513        va_start(ap, fr);
1514        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1515        roomname = va_arg(ap, char *);
1516        usercount= va_arg(ap, int);
1517        userinfo = va_arg(ap, aim_userinfo_t *);
1518        roomdesc = va_arg(ap, char *);
1519        unknown_c9 = (guint16)va_arg(ap, int);
1520        creationtime = (guint32)va_arg(ap, unsigned long);
1521        maxmsglen = (guint16)va_arg(ap, int);
1522        unknown_d2 = (guint16)va_arg(ap, int);
1523        unknown_d5 = (guint16)va_arg(ap, int);
1524        maxvisiblemsglen = (guint16)va_arg(ap, int);
1525        va_end(ap);
1526
1527        ccon->maxlen = maxmsglen;
1528        ccon->maxvis = maxvisiblemsglen;
1529
1530        return 1;
1531}
1532
1533static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1534        va_list ap;
1535        aim_userinfo_t *info;
1536        char *msg;
1537        struct im_connection *ic = sess->aux_data;
1538        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
1539        char *tmp;
1540
1541        va_start(ap, fr);
1542        info = va_arg(ap, aim_userinfo_t *);
1543        msg  = va_arg(ap, char *);
1544
1545        tmp = g_malloc(BUF_LONG);
1546        g_snprintf(tmp, BUF_LONG, "%s", msg);
1547        serv_got_chat_in(ccon->cnv, info->sn, 0, tmp, time((time_t)NULL));
1548        g_free(tmp);
1549
1550        return 1;
1551}
1552
1553static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1554#if 0
1555        static const char *codes[5] = {
1556                "invalid",
1557                 "change",
1558                 "warning",
1559                 "limit",
1560                 "limit cleared",
1561        };
1562#endif
1563        va_list ap;
1564        guint16 code, rateclass;
1565        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1566
1567        va_start(ap, fr); 
1568        code = (guint16)va_arg(ap, unsigned int);
1569        rateclass= (guint16)va_arg(ap, unsigned int);
1570        windowsize = (guint32)va_arg(ap, unsigned long);
1571        clear = (guint32)va_arg(ap, unsigned long);
1572        alert = (guint32)va_arg(ap, unsigned long);
1573        limit = (guint32)va_arg(ap, unsigned long);
1574        disconnect = (guint32)va_arg(ap, unsigned long);
1575        currentavg = (guint32)va_arg(ap, unsigned long);
1576        maxavg = (guint32)va_arg(ap, unsigned long);
1577        va_end(ap);
1578
1579        /* XXX fix these values */
1580        if (code == AIM_RATE_CODE_CHANGE) {
1581                if (currentavg >= clear)
1582                        aim_conn_setlatency(fr->conn, 0);
1583        } else if (code == AIM_RATE_CODE_WARNING) {
1584                aim_conn_setlatency(fr->conn, windowsize/4);
1585        } else if (code == AIM_RATE_CODE_LIMIT) {
1586                imcb_error(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1587                          "Please wait 10 seconds and try again."));
1588                aim_conn_setlatency(fr->conn, windowsize/2);
1589        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1590                aim_conn_setlatency(fr->conn, 0);
1591        }
1592
1593        return 1;
1594}
1595
1596static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1597        va_list ap;
1598        aim_userinfo_t *info;
1599        struct im_connection *ic = sess->aux_data;
1600
1601        va_start(ap, fr);
1602        info = va_arg(ap, aim_userinfo_t *);
1603        va_end(ap);
1604
1605        ic->evil = info->warnlevel/10;
1606        /* ic->correction_time = (info->onlinesince - ic->login_time); */
1607
1608        return 1;
1609}
1610
1611static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1612
1613        aim_reqpersonalinfo(sess, fr->conn);
1614        aim_bos_reqlocaterights(sess, fr->conn);
1615        aim_bos_reqbuddyrights(sess, fr->conn);
1616
1617        aim_reqicbmparams(sess);
1618
1619        aim_bos_reqrights(sess, fr->conn);
1620        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1621        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1622                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1623
1624        return 1;
1625}
1626
1627static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1628        struct im_connection *ic = sess->aux_data;
1629        struct oscar_data *od = ic->proto_data;
1630
1631        aim_clientready(sess, fr->conn);
1632
1633        if (od->chpass) {
1634                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1635                g_free(od->oldp);
1636                od->oldp = NULL;
1637                g_free(od->newp);
1638                od->newp = NULL;
1639                od->chpass = FALSE;
1640        }
1641        if (od->setnick) {
1642                aim_admin_setnick(sess, fr->conn, od->newsn);
1643                g_free(od->newsn);
1644                od->newsn = NULL;
1645                od->setnick = FALSE;
1646        }
1647        if (od->conf) {
1648                aim_admin_reqconfirm(sess, fr->conn);
1649                od->conf = FALSE;
1650        }
1651        if (od->reqemail) {
1652                aim_admin_getinfo(sess, fr->conn, 0x0011);
1653                od->reqemail = FALSE;
1654        }
1655        if (od->setemail) {
1656                aim_admin_setemail(sess, fr->conn, od->email);
1657                g_free(od->email);
1658                od->setemail = FALSE;
1659        }
1660
1661        return 1;
1662}
1663
1664static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1665        struct aim_icbmparameters *params;
1666        va_list ap;
1667
1668        va_start(ap, fr);
1669        params = va_arg(ap, struct aim_icbmparameters *);
1670        va_end(ap);
1671
1672        /* Maybe senderwarn and recverwarn should be user preferences... */
1673        params->flags = 0x0000000b;
1674        params->maxmsglen = 8000;
1675        params->minmsginterval = 0;
1676
1677        aim_seticbmparam(sess, params);
1678
1679        return 1;
1680}
1681
1682static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1683{
1684        va_list ap;
1685        guint16 maxsiglen;
1686        struct im_connection *ic = sess->aux_data;
1687        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1688
1689        va_start(ap, fr);
1690        maxsiglen = va_arg(ap, int);
1691        va_end(ap);
1692
1693        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1694
1695        /* FIXME: It seems we're not really using this, and it broke now that
1696           struct aim_user is dead.
1697        aim_bos_setprofile(sess, fr->conn, ic->user->user_info, NULL, gaim_caps);
1698        */
1699       
1700        return 1;
1701}
1702
1703static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1704        va_list ap;
1705        guint16 maxbuddies, maxwatchers;
1706        struct im_connection *ic = sess->aux_data;
1707        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1708
1709        va_start(ap, fr);
1710        maxbuddies = (guint16)va_arg(ap, unsigned int);
1711        maxwatchers = (guint16)va_arg(ap, unsigned int);
1712        va_end(ap);
1713
1714        odata->rights.maxbuddies = (guint)maxbuddies;
1715        odata->rights.maxwatchers = (guint)maxwatchers;
1716
1717        return 1;
1718}
1719
1720static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1721        guint16 maxpermits, maxdenies;
1722        va_list ap;
1723        struct im_connection *ic = sess->aux_data;
1724        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1725
1726        va_start(ap, fr);
1727        maxpermits = (guint16)va_arg(ap, unsigned int);
1728        maxdenies = (guint16)va_arg(ap, unsigned int);
1729        va_end(ap);
1730
1731        odata->rights.maxpermits = (guint)maxpermits;
1732        odata->rights.maxdenies = (guint)maxdenies;
1733
1734        aim_clientready(sess, fr->conn);
1735
1736        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1737
1738        aim_ssi_reqrights(sess, fr->conn);
1739        aim_ssi_reqalldata(sess, fr->conn);
1740
1741        return 1;
1742}
1743
1744static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1745        va_list ap;
1746        struct aim_icq_offlinemsg *msg;
1747        struct im_connection *ic = sess->aux_data;
1748
1749        va_start(ap, fr);
1750        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1751        va_end(ap);
1752
1753        switch (msg->type) {
1754                case 0x0001: { /* Basic offline message */
1755                        char sender[32];
1756                        char *dialog_msg = g_strdup(msg->msg);
1757                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1758                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1759                        strip_linefeed(dialog_msg);
1760                        serv_got_im(ic, sender, dialog_msg, 0, t, -1);
1761                        g_free(dialog_msg);
1762                } break;
1763
1764                case 0x0004: { /* Someone sent you a URL */
1765                        char sender[32];
1766                        char *dialog_msg;
1767                        char **m;
1768
1769                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1770                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1771
1772                        m = g_strsplit(msg->msg, "\376", 2);
1773
1774                        if ((strlen(m[0]) != 0)) {
1775                          dialog_msg = g_strjoinv(" -- ", m);
1776                        } else {
1777                          dialog_msg = m[1];
1778                        }
1779
1780                        strip_linefeed(dialog_msg);
1781                        serv_got_im(ic, sender, dialog_msg, 0, t, -1);
1782                        g_free(dialog_msg);
1783                        g_free(m);
1784                } break;
1785               
1786                case 0x0006: { /* Authorization request */
1787                        gaim_icq_authask(ic, msg->sender, msg->msg);
1788                } break;
1789
1790                case 0x0007: { /* Someone has denied you authorization */
1791                        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.") );
1792                } break;
1793
1794                case 0x0008: { /* Someone has granted you authorization */
1795                        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.") );
1796                } break;
1797
1798                case 0x0012: {
1799                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1800                } break;
1801
1802                default: {;
1803                }
1804        }
1805
1806        return 1;
1807}
1808
1809static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1810{
1811        aim_icq_ackofflinemsgs(sess);
1812        return 1;
1813}
1814
1815static void oscar_keepalive(struct im_connection *ic) {
1816        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1817        aim_flap_nop(odata->sess, odata->conn);
1818}
1819
1820static int oscar_send_im(struct im_connection *ic, char *name, char *message, int imflags) {
1821        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1822        int ret = 0, len = strlen(message);
1823        if (imflags & IM_FLAG_AWAY) {
1824                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1825        } else {
1826                struct aim_sendimext_args args;
1827                char *s;
1828               
1829                args.flags = AIM_IMFLAGS_ACK;
1830                if (odata->icq)
1831                        args.flags |= AIM_IMFLAGS_OFFLINE;
1832                for (s = message; *s; s++)
1833                        if (*s & 128)
1834                                break;
1835               
1836                /* Message contains high ASCII chars, time for some translation! */
1837                if (*s) {
1838                        s = g_malloc(BUF_LONG);
1839                        /* Try if we can put it in an ISO8859-1 string first.
1840                           If we can't, fall back to UTF16. */
1841                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1842                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1843                                len = ret;
1844                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1845                                args.flags |= AIM_IMFLAGS_UNICODE;
1846                                len = ret;
1847                        } else {
1848                                /* OOF, translation failed... Oh well.. */
1849                                g_free( s );
1850                                s = message;
1851                        }
1852                } else {
1853                        s = message;
1854                }
1855               
1856                args.features = gaim_features;
1857                args.featureslen = sizeof(gaim_features);
1858               
1859                args.destsn = name;
1860                args.msg    = s;
1861                args.msglen = len;
1862               
1863                ret = aim_send_im_ext(odata->sess, &args);
1864               
1865                if (s != message) {
1866                        g_free(s);
1867                }
1868        }
1869        if (ret >= 0)
1870                return 1;
1871        return ret;
1872}
1873
1874static void oscar_get_info(struct im_connection *g, char *name) {
1875        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1876        if (odata->icq)
1877                aim_icq_getallinfo(odata->sess, name);
1878        else {
1879                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1880                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1881        }
1882}
1883
1884static void oscar_get_away(struct im_connection *g, char *who) {
1885        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1886        if (odata->icq) {
1887                struct buddy *budlight = find_buddy(g, who);
1888                if (budlight)
1889                        if ((budlight->uc & 0xff80) >> 7)
1890                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1891                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1892        } else
1893                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1894}
1895
1896static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
1897{
1898
1899        if (!g_strcasecmp(state, _("Visible"))) {
1900                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1901                return;
1902        } else if (!g_strcasecmp(state, _("Invisible"))) {
1903                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1904                return;
1905        } /* else... */
1906
1907        if (od->rights.maxawaymsglen == 0)
1908                imcb_error(ic, "oscar_set_away_aim called before locate rights received");
1909
1910        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1911
1912        if (ic->away)
1913                g_free(ic->away);
1914        ic->away = NULL;
1915
1916        if (!message) {
1917                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1918                return;
1919        }
1920
1921        if (strlen(message) > od->rights.maxawaymsglen) {
1922                imcb_error(ic, "Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
1923        }
1924
1925        ic->away = g_strndup(message, od->rights.maxawaymsglen);
1926        aim_bos_setprofile(od->sess, od->conn, NULL, ic->away, gaim_caps);
1927
1928        return;
1929}
1930
1931static void oscar_set_away_icq(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
1932{
1933    const char *msg = NULL;
1934        gboolean no_message = FALSE;
1935
1936        /* clean old states */
1937    if (ic->away) {
1938                g_free(ic->away);
1939                ic->away = NULL;
1940    }
1941        od->sess->aim_icq_state = 0;
1942
1943        /* if no message, then use an empty message */
1944    if (message) {
1945        msg = message;
1946    } else {
1947        msg = "";
1948                no_message = TRUE;
1949    }
1950
1951        if (!g_strcasecmp(state, "Online")) {
1952                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1953        } else if (!g_strcasecmp(state, "Away")) {
1954                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1955        ic->away = g_strdup(msg);
1956                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1957        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1958                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
1959        ic->away = g_strdup(msg);
1960                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1961        } else if (!g_strcasecmp(state, "Not Available")) {
1962                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
1963        ic->away = g_strdup(msg);
1964                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1965        } else if (!g_strcasecmp(state, "Occupied")) {
1966                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
1967        ic->away = g_strdup(msg);
1968                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1969        } else if (!g_strcasecmp(state, "Free For Chat")) {
1970                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
1971        ic->away = g_strdup(msg);
1972                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1973        } else if (!g_strcasecmp(state, "Invisible")) {
1974                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1975        ic->away = g_strdup(msg);
1976        } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
1977                if (no_message) {
1978                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1979                } else {
1980                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1981            ic->away = g_strdup(msg);
1982                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1983                }
1984        }
1985
1986        return;
1987}
1988
1989static void oscar_set_away(struct im_connection *ic, char *state, char *message)
1990{
1991        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
1992
1993    oscar_set_away_aim(ic, od, state, message);
1994        if (od->icq)
1995                oscar_set_away_icq(ic, od, state, message);
1996
1997        return;
1998}
1999
2000static void oscar_add_buddy(struct im_connection *g, char *name, char *group) {
2001        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2002        aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
2003}
2004
2005static void oscar_remove_buddy(struct im_connection *g, char *name, char *group) {
2006        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2007        struct aim_ssi_item *ssigroup;
2008        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
2009}
2010
2011static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
2012        return 1;
2013}
2014
2015static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
2016        struct im_connection *ic = sess->aux_data;
2017        struct aim_ssi_item *curitem;
2018        int tmp;
2019
2020        /* Add from server list to local list */
2021        tmp = 0;
2022        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
2023                switch (curitem->type) {
2024                        case 0x0000: /* Buddy */
2025                                if ((curitem->name) && (!find_buddy(ic, curitem->name))) {
2026                                        char *realname = NULL;
2027
2028                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
2029                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
2030                                               
2031                                        add_buddy(ic, NULL, curitem->name, realname);
2032                                       
2033                                        if (realname)
2034                                            g_free(realname);
2035                                }
2036                                break;
2037
2038                        case 0x0002: /* Permit buddy */
2039                                if (curitem->name) {
2040                                        GSList *list;
2041                                        for (list=ic->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2042                                        if (!list) {
2043                                                char *name;
2044                                                name = g_strdup(normalize(curitem->name));
2045                                                ic->permit = g_slist_append(ic->permit, name);
2046                                                tmp++;
2047                                        }
2048                                }
2049                                break;
2050
2051                        case 0x0003: /* Deny buddy */
2052                                if (curitem->name) {
2053                                        GSList *list;
2054                                        for (list=ic->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2055                                        if (!list) {
2056                                                char *name;
2057                                                name = g_strdup(normalize(curitem->name));
2058                                                ic->deny = g_slist_append(ic->deny, name);
2059                                                tmp++;
2060                                        }
2061                                }
2062                                break;
2063
2064                        case 0x0004: /* Permit/deny setting */
2065                                if (curitem->data) {
2066                                        guint8 permdeny;
2067                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != ic->permdeny)) {
2068                                                ic->permdeny = permdeny;
2069                                                tmp++;
2070                                        }
2071                                }
2072                                break;
2073
2074                        case 0x0005: /* Presence setting */
2075                                /* We don't want to change Gaim's setting because it applies to all accounts */
2076                                break;
2077                } /* End of switch on curitem->type */
2078        } /* End of for loop */
2079
2080        aim_ssi_enable(sess, fr->conn);
2081       
2082        /* Request offline messages, now that the buddy list is complete. */
2083        aim_icq_reqofflinemsgs(sess);
2084       
2085        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
2086        imcb_connected(ic);
2087       
2088        return 1;
2089}
2090
2091static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2092{
2093        aim_snac_t *origsnac;
2094        va_list ap;
2095
2096        va_start( ap, fr );
2097        origsnac = va_arg( ap, aim_snac_t * );
2098        va_end( ap );
2099       
2100        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2101        {
2102                int i, st, count = aim_bstream_empty( &fr->data );
2103                char *list;
2104               
2105                if( count & 1 )
2106                {
2107                        /* Hmm, the length should be even... */
2108                        imcb_error( sess->aux_data, "Received SSI ACK package with non-even length");
2109                        return( 0 );
2110                }
2111                count >>= 1;
2112               
2113                list = (char *) origsnac->data;
2114                for( i = 0; i < count; i ++ )
2115                {
2116                        st = aimbs_get16( &fr->data );
2117                        if( st == 0x0E )
2118                        {
2119                                imcb_log( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
2120                               
2121                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2122                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2123                        }
2124                        list += strlen( list ) + 1;
2125                }
2126        }
2127       
2128        return( 1 );
2129}
2130
2131static void oscar_set_permit_deny(struct im_connection *ic) {
2132        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2133        if (od->icq) {
2134                GSList *list;
2135                char buf[MAXMSGLEN];
2136                int at;
2137
2138                switch(ic->permdeny) {
2139                case 1:
2140                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, ic->acc->user);
2141                        break;
2142                case 2:
2143                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, ic->acc->user);
2144                        break;
2145                case 3:
2146                        list = ic->permit;
2147                        at = 0;
2148                        while (list) {
2149                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2150                                list = list->next;
2151                        }
2152                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2153                        break;
2154                case 4:
2155                        list = ic->deny;
2156                        at = 0;
2157                        while (list) {
2158                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2159                                list = list->next;
2160                        }
2161                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2162                        break;
2163                        default:
2164                        break;
2165                }
2166        } else {
2167                if (od->sess->ssi.received_data)
2168                        aim_ssi_setpermdeny(od->sess, od->conn, ic->permdeny, 0xffffffff);
2169        }
2170}
2171
2172static void oscar_add_permit(struct im_connection *ic, char *who) {
2173        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2174        if (od->icq) {
2175                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2176        } else {
2177                if (od->sess->ssi.received_data)
2178                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2179        }
2180}
2181
2182static void oscar_add_deny(struct im_connection *ic, char *who) {
2183        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2184        if (od->icq) {
2185                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2186        } else {
2187                if (od->sess->ssi.received_data)
2188                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2189        }
2190}
2191
2192static void oscar_rem_permit(struct im_connection *ic, char *who) {
2193        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2194        if (!od->icq) {
2195                if (od->sess->ssi.received_data)
2196                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2197        }
2198}
2199
2200static void oscar_rem_deny(struct im_connection *ic, char *who) {
2201        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2202        if (!od->icq) {
2203                if (od->sess->ssi.received_data)
2204                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2205        }
2206}
2207
2208static GList *oscar_away_states(struct im_connection *ic)
2209{
2210        struct oscar_data *od = ic->proto_data;
2211        GList *m = NULL;
2212
2213        if (!od->icq)
2214                return g_list_append(m, GAIM_AWAY_CUSTOM);
2215
2216        m = g_list_append(m, "Online");
2217        m = g_list_append(m, "Away");
2218        m = g_list_append(m, "Do Not Disturb");
2219        m = g_list_append(m, "Not Available");
2220        m = g_list_append(m, "Occupied");
2221        m = g_list_append(m, "Free For Chat");
2222        m = g_list_append(m, "Invisible");
2223
2224        return m;
2225}
2226
2227static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2228{
2229        struct im_connection *ic = sess->aux_data;
2230        gchar who[16];
2231        GString *str;
2232        va_list ap;
2233        struct aim_icq_info *info;
2234
2235        va_start(ap, fr);
2236        info = va_arg(ap, struct aim_icq_info *);
2237        va_end(ap);
2238
2239        if (!info->uin)
2240                return 0;
2241
2242        str = g_string_sized_new(100);
2243        g_snprintf(who, sizeof(who), "%u", info->uin);
2244
2245        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2246                                info->nick ? info->nick : "-");
2247        info_string_append(str, "\n", _("First Name"), info->first);
2248        info_string_append(str, "\n", _("Last Name"), info->last);
2249                info_string_append(str, "\n", _("Email Address"), info->email);
2250        if (info->numaddresses && info->email2) {
2251                int i;
2252                for (i = 0; i < info->numaddresses; i++) {
2253                                        info_string_append(str, "\n", _("Email Address"), info->email2[i]);
2254                }
2255        }
2256        info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
2257        info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : info->gender==2 ? _("Male") : _("Unknown"));
2258        if (info->birthyear || info->birthmonth || info->birthday) {
2259                char date[30];
2260                struct tm tm;
2261                tm.tm_mday = (int)info->birthday;
2262                tm.tm_mon = (int)info->birthmonth-1;
2263                tm.tm_year = (int)info->birthyear%100;
2264                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2265                info_string_append(str, "\n", _("Birthday"), date);
2266        }
2267        if (info->age) {
2268                char age[5];
2269                g_snprintf(age, sizeof(age), "%hhd", info->age);
2270                info_string_append(str, "\n", _("Age"), age);
2271        }
2272                info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
2273        if (info->info && info->info[0]) {
2274                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2275                                                info->info, _("End of Additional Information"));
2276        }
2277        g_string_sprintfa(str, "\n");
2278        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2279                g_string_sprintfa(str, "%s:", _("Home Address"));
2280                info_string_append(str, "\n", _("Address"), info->homeaddr);
2281                info_string_append(str, "\n", _("City"), info->homecity);
2282                info_string_append(str, "\n", _("State"), info->homestate); 
2283                                info_string_append(str, "\n", _("Zip Code"), info->homezip);
2284                g_string_sprintfa(str, "\n");
2285        }
2286        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2287                g_string_sprintfa(str, "%s:", _("Work Address"));
2288                info_string_append(str, "\n", _("Address"), info->workaddr);
2289                info_string_append(str, "\n", _("City"), info->workcity);
2290                info_string_append(str, "\n", _("State"), info->workstate);
2291                                info_string_append(str, "\n", _("Zip Code"), info->workzip);
2292                g_string_sprintfa(str, "\n");
2293        }
2294        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2295                g_string_sprintfa(str, "%s:", _("Work Information"));
2296                info_string_append(str, "\n", _("Company"), info->workcompany);
2297                info_string_append(str, "\n", _("Division"), info->workdivision);
2298                info_string_append(str, "\n", _("Position"), info->workposition);
2299                if (info->workwebpage && info->workwebpage[0]) {
2300                        info_string_append(str, "\n", _("Web Page"), info->workwebpage);
2301                }
2302                g_string_sprintfa(str, "\n");
2303        }
2304
2305                imcb_log(ic, "%s\n%s", _("User Info"), str->str);
2306        g_string_free(str, TRUE);
2307
2308        return 1;
2309
2310}
2311
2312static char *oscar_encoding_extract(const char *encoding)
2313{
2314        char *ret = NULL;
2315        char *begin, *end;
2316
2317        g_return_val_if_fail(encoding != NULL, NULL);
2318
2319        /* Make sure encoding begins with charset= */
2320        if (strncmp(encoding, "text/plain; charset=", 20) &&
2321                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2322                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2323        {
2324                return NULL;
2325        }
2326
2327        begin = strchr(encoding, '"');
2328        end = strrchr(encoding, '"');
2329
2330        if ((begin == NULL) || (end == NULL) || (begin >= end))
2331                return NULL;
2332
2333        ret = g_strndup(begin+1, (end-1) - begin);
2334
2335        return ret;
2336}
2337
2338static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2339{
2340        char *utf8 = g_new0(char, 8192);
2341
2342        if ((encoding == NULL) || encoding[0] == '\0') {
2343                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2344        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2345                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2346        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2347                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2348        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2349                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2350        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2351                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2352                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2353                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2354        }
2355
2356        /*
2357         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2358         * we have been unable to convert the text to utf-8 from the encoding
2359         * that was specified.  So we assume it's UTF-8 and hope for the best.
2360         */
2361        if (*utf8 == 0) {
2362            strncpy(utf8, text, textlen);
2363        }
2364
2365        return utf8;
2366}
2367
2368static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2369{
2370        struct im_connection *ic = sess->aux_data;
2371        va_list ap;
2372        aim_userinfo_t *userinfo;
2373        guint16 infotype;
2374        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2375        guint16 text_length;
2376        char *utf8 = NULL;
2377
2378        va_start(ap, fr);
2379        userinfo = va_arg(ap, aim_userinfo_t *);
2380        infotype = va_arg(ap, int);
2381        text_encoding = va_arg(ap, char*);
2382        text = va_arg(ap, char*);
2383        text_length = va_arg(ap, int);
2384        va_end(ap);
2385
2386        if(text_encoding)
2387                extracted_encoding = oscar_encoding_extract(text_encoding);
2388        if(infotype == AIM_GETINFO_GENERALINFO) {
2389                /*Display idle time*/
2390                char buff[256];
2391                struct tm idletime;
2392                if(userinfo->idletime) {
2393                        memset(&idletime, 0, sizeof(struct tm));
2394                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2395                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2396                        idletime.tm_min = userinfo->idletime % 60;
2397                        idletime.tm_sec = 0;
2398                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
2399                        imcb_log(ic, "%s: %s", _("Idle Time"), buff);
2400                }
2401               
2402                if(text) {
2403                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2404                        imcb_log(ic, "%s\n%s", _("User Info"), utf8);
2405                } else {
2406                        imcb_log(ic, _("No user info available."));
2407                }
2408        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2409                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2410                imcb_log(ic, "%s\n%s", _("Away Message"), utf8);
2411        }
2412
2413        g_free(utf8);
2414   
2415        return 1;
2416}
2417
2418int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)
2419{
2420        struct im_connection * ic = sess->aux_data;
2421        va_list ap;
2422        guint16 type1, type2;
2423        char * sn;
2424
2425        va_start(ap, fr);
2426        type1 = va_arg(ap, int);
2427        sn = va_arg(ap, char*);
2428        type2 = va_arg(ap, int);
2429        va_end(ap);
2430   
2431        if(type2 == 0x0002) {
2432                /* User is typing */
2433                serv_got_typing(ic, sn, 0, 1);
2434        } 
2435        else if (type2 == 0x0001) {
2436                /* User has typed something, but is not actively typing (stale) */
2437                serv_got_typing(ic, sn, 0, 2);
2438        }
2439        else {
2440                /* User has stopped typing */
2441                serv_got_typing(ic, sn, 0, 0);
2442        }       
2443       
2444        return 1;
2445}
2446
2447static char *oscar_get_status_string( struct im_connection *ic, int number )
2448{
2449        struct oscar_data *od = ic->proto_data;
2450       
2451        if( ! number & UC_UNAVAILABLE )
2452        {
2453                return( NULL );
2454        }
2455        else if( od->icq )
2456        {
2457                number >>= 7;
2458                if( number & AIM_ICQ_STATE_DND )
2459                        return( "Do Not Disturb" );
2460                else if( number & AIM_ICQ_STATE_OUT )
2461                        return( "Not Available" );
2462                else if( number & AIM_ICQ_STATE_BUSY )
2463                        return( "Occupied" );
2464                else if( number & AIM_ICQ_STATE_INVISIBLE )
2465                        return( "Invisible" );
2466                else
2467                        return( "Away" );
2468        }
2469        else
2470        {
2471                return( "Away" );
2472        }
2473}
2474
2475int oscar_send_typing(struct im_connection *ic, char * who, int typing)
2476{
2477        struct oscar_data *od = ic->proto_data;
2478        return( aim_im_sendmtn(od->sess, 1, who, typing ? 0x0002 : 0x0000) );
2479}
2480
2481void oscar_chat_send(struct groupchat *c, char *message, int msgflags)
2482{
2483        struct im_connection *ic = c->ic;
2484        struct oscar_data * od = (struct oscar_data*)ic->proto_data;
2485        struct chat_connection * ccon;
2486        int ret;
2487        guint8 len = strlen(message);
2488        guint16 flags;
2489        char *s;
2490       
2491        ccon = c->data;
2492               
2493        for (s = message; *s; s++)
2494                if (*s & 128)
2495                        break;
2496       
2497        flags = AIM_CHATFLAGS_NOREFLECT;
2498       
2499        /* Message contains high ASCII chars, time for some translation! */
2500        if (*s) {
2501                s = g_malloc(BUF_LONG);
2502                /* Try if we can put it in an ISO8859-1 string first.
2503                   If we can't, fall back to UTF16. */
2504                if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
2505                        flags |= AIM_CHATFLAGS_ISO_8859_1;
2506                        len = ret;
2507                } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
2508                        flags |= AIM_CHATFLAGS_UNICODE;
2509                        len = ret;
2510                } else {
2511                        /* OOF, translation failed... Oh well.. */
2512                        g_free( s );
2513                        s = message;
2514                }
2515        } else {
2516                s = message;
2517        }
2518               
2519        ret = aim_chat_send_im(od->sess, ccon->conn, flags, s, len);
2520               
2521        if (s != message) {     
2522                g_free(s);
2523  }
2524 
2525/*  return (ret >= 0); */
2526}
2527
2528void oscar_chat_invite(struct groupchat *c, char *message, char *who)
2529{
2530        struct im_connection *ic = c->ic;
2531        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2532        struct chat_connection *ccon = c->data;
2533       
2534        aim_chat_invite(od->sess, od->conn, who, message ? message : "",
2535                                        ccon->exchange, ccon->name, 0x0);
2536}
2537
2538void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc)
2539{
2540        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2541
2542        /* Notify the conversation window that we've left the chat */
2543        serv_got_chat_left(cc->cnv);
2544
2545        /* Destroy the chat_connection */
2546        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
2547        if (cc->inpa > 0)
2548                b_event_remove(cc->inpa);
2549        aim_conn_kill(od->sess, &cc->conn);
2550        g_free(cc->name);
2551        g_free(cc->show);
2552        g_free(cc);
2553}
2554
2555void oscar_chat_leave(struct groupchat *c)
2556{
2557        oscar_chat_kill(c->ic, c->data);
2558}
2559
2560int oscar_chat_join(struct im_connection * ic, char * name)
2561{
2562        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2563       
2564        aim_conn_t * cur;
2565
2566        if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
2567       
2568                return (aim_chatnav_createroom(od->sess, cur, name, 4) == 0);
2569       
2570        } else {
2571                struct create_room * cr = g_new0(struct create_room, 1);
2572                cr->exchange = 4;
2573                cr->name = g_strdup(name);
2574                od->create_rooms = g_slist_append(od->create_rooms, cr);
2575                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
2576                return 1;
2577        }
2578}
2579
2580struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)
2581{
2582        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2583        int ret;
2584        static int chat_id = 0;
2585        char * chatname;
2586       
2587        chatname = g_strdup_printf("%s%d", ic->acc->user, chat_id++);
2588 
2589        ret = oscar_chat_join(ic, chatname);
2590
2591        aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);
2592
2593        g_free(chatname);
2594       
2595        return NULL;
2596}
2597
2598void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
2599{
2600        oscar_chat_join(inv->ic, inv->name);
2601        g_free(inv->name);
2602        g_free(inv);
2603}
2604
2605void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
2606{
2607        g_free(inv->name);
2608        g_free(inv);
2609}
2610
2611void oscar_initmodule() 
2612{
2613        struct prpl *ret = g_new0(struct prpl, 1);
2614        ret->name = "oscar";
2615        ret->away_states = oscar_away_states;
2616        ret->init = oscar_init;
2617        ret->login = oscar_login;
2618        ret->keepalive = oscar_keepalive;
2619        ret->logout = oscar_logout;
2620        ret->send_im = oscar_send_im;
2621        ret->get_info = oscar_get_info;
2622        ret->set_away = oscar_set_away;
2623        ret->get_away = oscar_get_away;
2624        ret->add_buddy = oscar_add_buddy;
2625        ret->remove_buddy = oscar_remove_buddy;
2626        ret->chat_send = oscar_chat_send;
2627        ret->chat_invite = oscar_chat_invite;
2628        ret->chat_leave = oscar_chat_leave;
2629        ret->chat_with = oscar_chat_with;
2630        ret->add_permit = oscar_add_permit;
2631        ret->add_deny = oscar_add_deny;
2632        ret->rem_permit = oscar_rem_permit;
2633        ret->rem_deny = oscar_rem_deny;
2634        ret->set_permit_deny = oscar_set_permit_deny;
2635        ret->get_status_string = oscar_get_status_string;
2636        ret->send_typing = oscar_send_typing;
2637       
2638        ret->handle_cmp = aim_sncmp;
2639
2640        register_protocol(ret);
2641}
Note: See TracBrowser for help on using the repository browser.