source: protocols/oscar/oscar.c @ fa29d093

Last change on this file since fa29d093 was fa29d093, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-03-28T05:53:11Z

Preparing for Jabber conference room support.

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