source: protocols/oscar/oscar.c @ cf13671

Last change on this file since cf13671 was 2cdd8ce, checked in by Jelmer Vernooij <jelmer@…>, at 2005-11-19T15:17:03Z

Merge Wilmer

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