source: protocols/oscar/oscar.c @ 42bdeec

Last change on this file since 42bdeec was 9cb9868, checked in by Jelmer Vernooij <jelmer@…>, at 2005-11-15T14:47:17Z

Remove handle_cmp() replacing it by a protocol-specific function.

  • Property mode set to 100644
File size: 71.2 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 *, ...);
223
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->protocol = PROTO_ICQ;
367                gc->password[8] = 0;
368        } else {
369                gc->protocol = PROTO_TOC;
370                gc->flags |= OPT_CONN_HTML;
371        }
372
373        sess = g_new0(aim_session_t, 1);
374
375        aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
376
377        /* we need an immediate queue because we don't use a while-loop to
378         * see if things need to be sent. */
379        aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
380        odata->sess = sess;
381        sess->aux_data = gc;
382
383        conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
384        if (conn == NULL) {
385                hide_login_progress(gc, _("Unable to login to AIM"));
386                signoff(gc);
387                return;
388        }
389       
390        if (g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.icq.com") != 0 &&
391            g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.oscar.aol.com") != 0) {
392                serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.");
393        }
394       
395        g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username);
396        set_login_progress(gc, 2, buf);
397
398        aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
399        aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
400
401        conn->status |= AIM_CONN_STATUS_INPROGRESS;
402        conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ?
403                                        user->proto_opt[USEROPT_AUTH] : AIM_DEFAULT_LOGIN_SERVER,
404                                 user->proto_opt[USEROPT_AUTHPORT][0] ?
405                                        atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
406                                 oscar_login_connect, gc);
407        if (conn->fd < 0) {
408                hide_login_progress(gc, _("Couldn't connect to host"));
409                signoff(gc);
410                return;
411        }
412        aim_request_login(sess, conn, gc->username);
413}
414
415static void oscar_close(struct gaim_connection *gc) {
416        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
417       
418        while (odata->oscar_chats) {
419                struct chat_connection *n = odata->oscar_chats->data;
420                if (n->inpa > 0)
421                        gaim_input_remove(n->inpa);
422                g_free(n->name);
423                g_free(n->show);
424                odata->oscar_chats = g_slist_remove(odata->oscar_chats, n);
425                g_free(n);
426        }
427        while (odata->create_rooms) {
428                struct create_room *cr = odata->create_rooms->data;
429                g_free(cr->name);
430                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
431                g_free(cr);
432        }
433        if (odata->email)
434                g_free(odata->email);
435        if (odata->newp)
436                g_free(odata->newp);
437        if (odata->oldp)
438                g_free(odata->oldp);
439        if (gc->inpa > 0)
440                gaim_input_remove(gc->inpa);
441        if (odata->cnpa > 0)
442                gaim_input_remove(odata->cnpa);
443        if (odata->paspa > 0)
444                gaim_input_remove(odata->paspa);
445        aim_session_kill(odata->sess);
446        g_free(odata->sess);
447        odata->sess = NULL;
448        g_free(gc->proto_data);
449        gc->proto_data = NULL;
450}
451
452static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) {
453        struct gaim_connection *gc = data;
454        struct oscar_data *odata;
455        aim_session_t *sess;
456        aim_conn_t *bosconn;
457
458        if (!g_slist_find(get_connections(), gc)) {
459                closesocket(source);
460                return;
461        }
462
463        odata = gc->proto_data;
464        sess = odata->sess;
465        bosconn = odata->conn;
466
467        if (source < 0) {
468                hide_login_progress(gc, _("Could Not Connect"));
469                signoff(gc);
470                return;
471        }
472
473        aim_conn_completeconnect(sess, bosconn);
474        gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ,
475                        oscar_callback, bosconn);
476        set_login_progress(gc, 4, _("Connection established, cookie sent"));
477}
478
479static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
480        va_list ap;
481        struct aim_authresp_info *info;
482        int i; char *host; int port;
483        struct aim_user *user;
484        aim_conn_t *bosconn;
485
486        struct gaim_connection *gc = sess->aux_data;
487        struct oscar_data *od = gc->proto_data;
488        user = gc->user;
489        port = user->proto_opt[USEROPT_AUTHPORT][0] ?
490                atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
491
492        va_start(ap, fr);
493        info = va_arg(ap, struct aim_authresp_info *);
494        va_end(ap);
495
496        if (info->errorcode || !info->bosip || !info->cookie) {
497                switch (info->errorcode) {
498                case 0x05:
499                        /* Incorrect nick/password */
500                        hide_login_progress(gc, _("Incorrect nickname or password."));
501//                      plugin_event(event_error, (void *)980, 0, 0, 0);
502                        break;
503                case 0x11:
504                        /* Suspended account */
505                        hide_login_progress(gc, _("Your account is currently suspended."));
506                        break;
507                case 0x18:
508                        /* connecting too frequently */
509                        hide_login_progress(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
510                        break;
511                case 0x1c:
512                        /* client too old */
513                        hide_login_progress(gc, _("The client version you are using is too old. Please upgrade at " WEBSITE));
514                        break;
515                default:
516                        hide_login_progress(gc, _("Authentication Failed"));
517                        break;
518                }
519                od->killme = TRUE;
520                return 1;
521        }
522
523
524        aim_conn_kill(sess, &fr->conn);
525
526        bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL);
527        if (bosconn == NULL) {
528                hide_login_progress(gc, _("Internal Error"));
529                od->killme = TRUE;
530                return 0;
531        }
532
533        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0);
534        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_RIGHTS, gaim_bosrights, 0);
535        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
536        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
537        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0);
538        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
539        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
540        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
541        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
542        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
543        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
544        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
545        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
546        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
547        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_PARAMINFO, gaim_icbm_param_info, 0);
548        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_ERROR, gaim_parse_genericerr, 0);
549        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ERROR, gaim_parse_genericerr, 0);
550        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_ERROR, gaim_parse_genericerr, 0);
551        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x1f, gaim_memrequest, 0);
552        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SELFINFO, gaim_selfinfo, 0);
553        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
554        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
555        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0);
556        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
557        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
558        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);
559        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0);
560
561        ((struct oscar_data *)gc->proto_data)->conn = bosconn;
562        for (i = 0; i < (int)strlen(info->bosip); i++) {
563                if (info->bosip[i] == ':') {
564                        port = atoi(&(info->bosip[i+1]));
565                        break;
566                }
567        }
568        host = g_strndup(info->bosip, i);
569        bosconn->status |= AIM_CONN_STATUS_INPROGRESS;
570        bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc);
571        g_free(host);
572        if (bosconn->fd < 0) {
573                hide_login_progress(gc, _("Could Not Connect"));
574                od->killme = TRUE;
575                return 0;
576        }
577        aim_sendcookie(sess, bosconn, info->cookie);
578        gaim_input_remove(gc->inpa);
579
580        return 1;
581}
582
583struct pieceofcrap {
584        struct gaim_connection *gc;
585        unsigned long offset;
586        unsigned long len;
587        char *modname;
588        int fd;
589        aim_conn_t *conn;
590        unsigned int inpa;
591};
592
593static void damn_you(gpointer data, gint source, GaimInputCondition c)
594{
595        struct pieceofcrap *pos = data;
596        struct oscar_data *od = pos->gc->proto_data;
597        char in = '\0';
598        int x = 0;
599        unsigned char m[17];
600
601        while (read(pos->fd, &in, 1) == 1) {
602                if (in == '\n')
603                        x++;
604                else if (in != '\r')
605                        x = 0;
606                if (x == 2)
607                        break;
608                in = '\0';
609        }
610        if (in != '\n') {
611                do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
612                                " You may be disconnected shortly.", "Login Error");
613                gaim_input_remove(pos->inpa);
614                closesocket(pos->fd);
615                g_free(pos);
616                return;
617        }
618        read(pos->fd, m, 16);
619        m[16] = '\0';
620        gaim_input_remove(pos->inpa);
621        closesocket(pos->fd);
622        aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
623        g_free(pos);
624}
625
626static void straight_to_hell(gpointer data, gint source, GaimInputCondition cond) {
627        struct pieceofcrap *pos = data;
628        char buf[BUF_LONG];
629
630        if (source < 0) {
631                do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
632                                " You may be disconnected shortly.", "Login Error");
633                if (pos->modname)
634                        g_free(pos->modname);
635                g_free(pos);
636                return;
637        }
638
639        g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA
640                        "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
641                        pos->offset, pos->len, pos->modname ? pos->modname : "");
642        write(pos->fd, buf, strlen(buf));
643        if (pos->modname)
644                g_free(pos->modname);
645        pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos);
646        return;
647}
648
649/* size of icbmui.ocm, the largest module in AIM 3.5 */
650#define AIM_MAX_FILE_SIZE 98304
651
652int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
653        va_list ap;
654        struct pieceofcrap *pos;
655        guint32 offset, len;
656        char *modname;
657        int fd;
658
659        va_start(ap, fr);
660        offset = (guint32)va_arg(ap, unsigned long);
661        len = (guint32)va_arg(ap, unsigned long);
662        modname = va_arg(ap, char *);
663        va_end(ap);
664
665        if (len == 0) {
666                aim_sendmemblock(sess, fr->conn, offset, len, NULL,
667                                AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
668                return 1;
669        }
670        /* uncomment this when you're convinced it's right. remember, it's been wrong before.
671        if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
672                char *buf;
673                int i = 8;
674                if (modname)
675                        i += strlen(modname);
676                buf = g_malloc(i);
677                i = 0;
678                if (modname) {
679                        memcpy(buf, modname, strlen(modname));
680                        i += strlen(modname);
681                }
682                buf[i++] = offset & 0xff;
683                buf[i++] = (offset >> 8) & 0xff;
684                buf[i++] = (offset >> 16) & 0xff;
685                buf[i++] = (offset >> 24) & 0xff;
686                buf[i++] = len & 0xff;
687                buf[i++] = (len >> 8) & 0xff;
688                buf[i++] = (len >> 16) & 0xff;
689                buf[i++] = (len >> 24) & 0xff;
690                aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
691                g_free(buf);
692                return 1;
693        }
694        */
695
696        pos = g_new0(struct pieceofcrap, 1);
697        pos->gc = sess->aux_data;
698        pos->conn = fr->conn;
699
700        pos->offset = offset;
701        pos->len = len;
702        pos->modname = modname ? g_strdup(modname) : NULL;
703
704        fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos);
705        if (fd < 0) {
706                if (pos->modname)
707                        g_free(pos->modname);
708                g_free(pos);
709                do_error_dialog(sess->aux_data, "Gaim was unable to get a valid hash for logging into AIM."
710                                " You may be disconnected shortly.", "Login Error");
711        }
712        pos->fd = fd;
713
714        return 1;
715}
716
717static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {
718#if 0
719        struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b};
720#else
721        struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD;
722#endif
723        char *key;
724        va_list ap;
725        struct gaim_connection *gc = sess->aux_data;
726
727        va_start(ap, fr);
728        key = va_arg(ap, char *);
729        va_end(ap);
730
731        aim_send_login(sess, fr->conn, gc->username, gc->password, &info, key);
732
733        return 1;
734}
735
736static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {
737        struct gaim_connection *gc = sess->aux_data;
738        struct chat_connection *chatcon;
739        static int id = 1;
740
741        aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0);
742        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
743        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
744        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
745        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
746
747        aim_clientready(sess, fr->conn);
748
749        chatcon = find_oscar_chat_by_conn(gc, fr->conn);
750        chatcon->id = id;
751        chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
752
753        return 1;
754}
755
756static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
757
758        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_ERROR, gaim_parse_genericerr, 0);
759        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
760
761        aim_clientready(sess, fr->conn);
762
763        aim_chatnav_reqrights(sess, fr->conn);
764
765        return 1;
766}
767
768static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) {
769        struct gaim_connection *gc = data;
770        struct oscar_data *odata;
771        aim_session_t *sess;
772        aim_conn_t *tstconn;
773
774        if (!g_slist_find(get_connections(), gc)) {
775                closesocket(source);
776                return;
777        }
778
779        odata = gc->proto_data;
780        sess = odata->sess;
781        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
782
783        if (source < 0) {
784                aim_conn_kill(sess, &tstconn);
785                return;
786        }
787
788        aim_conn_completeconnect(sess, tstconn);
789        odata->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
790                                        oscar_callback, tstconn);
791}
792
793static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond)
794{
795        struct gaim_connection *gc = data;
796        struct oscar_data *odata;
797        aim_session_t *sess;
798        aim_conn_t *tstconn;
799
800        if (!g_slist_find(get_connections(), gc)) {
801                closesocket(source);
802                return;
803        }
804
805        odata = gc->proto_data;
806        sess = odata->sess;
807        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
808
809        if (source < 0) {
810                aim_conn_kill(sess, &tstconn);
811                return;
812        }
813
814        aim_conn_completeconnect(sess, tstconn);
815        odata->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
816                                oscar_callback, tstconn);
817}
818
819static void oscar_chat_connect(gpointer data, gint source, GaimInputCondition cond)
820{
821        struct chat_connection *ccon = data;
822        struct gaim_connection *gc = ccon->gc;
823        struct oscar_data *odata;
824        aim_session_t *sess;
825        aim_conn_t *tstconn;
826
827        if (!g_slist_find(get_connections(), gc)) {
828                closesocket(source);
829                g_free(ccon->show);
830                g_free(ccon->name);
831                g_free(ccon);
832                return;
833        }
834
835        odata = gc->proto_data;
836        sess = odata->sess;
837        tstconn = ccon->conn;
838
839        if (source < 0) {
840                aim_conn_kill(sess, &tstconn);
841                g_free(ccon->show);
842                g_free(ccon->name);
843                g_free(ccon);
844                return;
845        }
846
847        aim_conn_completeconnect(sess, ccon->conn);
848        ccon->inpa = gaim_input_add(tstconn->fd,
849                        GAIM_INPUT_READ,
850                        oscar_callback, tstconn);
851        odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon);
852}
853
854/* Hrmph. I don't know how to make this look better. --mid */
855static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
856        va_list ap;
857        struct aim_redirect_data *redir;
858        struct gaim_connection *gc = sess->aux_data;
859        struct aim_user *user = gc->user;
860        aim_conn_t *tstconn;
861        int i;
862        char *host;
863        int port;
864
865        port = user->proto_opt[USEROPT_AUTHPORT][0] ?
866                atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
867
868        va_start(ap, fr);
869        redir = va_arg(ap, struct aim_redirect_data *);
870        va_end(ap);
871
872        for (i = 0; i < (int)strlen(redir->ip); i++) {
873                if (redir->ip[i] == ':') {
874                        port = atoi(&(redir->ip[i+1]));
875                        break;
876                }
877        }
878        host = g_strndup(redir->ip, i);
879
880        switch(redir->group) {
881        case 0x7: /* Authorizer */
882                tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
883                if (tstconn == NULL) {
884                        g_free(host);
885                        return 1;
886                }
887                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0);
888//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0);
889//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0);
890//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0);
891
892                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
893                tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc);
894                if (tstconn->fd < 0) {
895                        aim_conn_kill(sess, &tstconn);
896                        g_free(host);
897                        return 1;
898                }
899                aim_sendcookie(sess, tstconn, redir->cookie);
900                break;
901        case 0xd: /* ChatNav */
902                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL);
903                if (tstconn == NULL) {
904                        g_free(host);
905                        return 1;
906                }
907                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0);
908
909                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
910                tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc);
911                if (tstconn->fd < 0) {
912                        aim_conn_kill(sess, &tstconn);
913                        g_free(host);
914                        return 1;
915                }
916                aim_sendcookie(sess, tstconn, redir->cookie);
917                break;
918        case 0xe: /* Chat */
919                {
920                struct chat_connection *ccon;
921
922                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
923                if (tstconn == NULL) {
924                        g_free(host);
925                        return 1;
926                }
927
928                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0);
929
930                ccon = g_new0(struct chat_connection, 1);
931                ccon->conn = tstconn;
932                ccon->gc = gc;
933                ccon->fd = -1;
934                ccon->name = g_strdup(redir->chat.room);
935                ccon->exchange = redir->chat.exchange;
936                ccon->instance = redir->chat.instance;
937                ccon->show = extract_name(redir->chat.room);
938               
939                ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
940                ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon);
941                if (ccon->conn->fd < 0) {
942                        aim_conn_kill(sess, &tstconn);
943                        g_free(host);
944                        g_free(ccon->show);
945                        g_free(ccon->name);
946                        g_free(ccon);
947                        return 1;
948                }
949                aim_sendcookie(sess, tstconn, redir->cookie);
950                }
951                break;
952        default: /* huh? */
953                break;
954        }
955
956        g_free(host);
957        return 1;
958}
959
960static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {
961        struct gaim_connection *gc = sess->aux_data;
962        struct oscar_data *od = gc->proto_data;
963        aim_userinfo_t *info;
964        time_t time_idle = 0, signon = 0;
965        int type = 0;
966        int caps = 0;
967        char *tmp;
968
969        va_list ap;
970        va_start(ap, fr);
971        info = va_arg(ap, aim_userinfo_t *);
972        va_end(ap);
973
974        if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
975                caps = info->capabilities;
976        if (info->flags & AIM_FLAG_ACTIVEBUDDY)
977                type |= UC_AB;
978
979        if ((!od->icq) && (info->present & AIM_USERINFO_PRESENT_FLAGS)) {
980                if (info->flags & AIM_FLAG_UNCONFIRMED)
981                        type |= UC_UNCONFIRMED;
982                if (info->flags & AIM_FLAG_ADMINISTRATOR)
983                        type |= UC_ADMIN;
984                if (info->flags & AIM_FLAG_AOL)
985                        type |= UC_AOL;
986                if (info->flags & AIM_FLAG_FREE)
987                        type |= UC_NORMAL;
988                if (info->flags & AIM_FLAG_AWAY)
989                        type |= UC_UNAVAILABLE;
990                if (info->flags & AIM_FLAG_WIRELESS)
991                        type |= UC_WIRELESS;
992        }
993        if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
994                type = (info->icqinfo.status << 7);
995                if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
996                      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
997                        type |= UC_UNAVAILABLE;
998                }
999        }
1000
1001        if (caps & AIM_CAPS_ICQ)
1002                caps ^= AIM_CAPS_ICQ;
1003
1004        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
1005                time(&time_idle);
1006                time_idle -= info->idletime*60;
1007        }
1008
1009        if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
1010                signon = time(NULL) - info->sessionlen;
1011
1012        tmp = g_strdup(normalize(gc->username));
1013        if (!strcmp(tmp, normalize(info->sn)))
1014                g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", info->sn);
1015        g_free(tmp);
1016
1017        serv_got_update(gc, info->sn, 1, info->warnlevel/10, signon,
1018                        time_idle, type, caps);
1019
1020        return 1;
1021}
1022
1023static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
1024        aim_userinfo_t *info;
1025        va_list ap;
1026        struct gaim_connection *gc = sess->aux_data;
1027
1028        va_start(ap, fr);
1029        info = va_arg(ap, aim_userinfo_t *);
1030        va_end(ap);
1031
1032        serv_got_update(gc, info->sn, 0, 0, 0, 0, 0, 0);
1033
1034        return 1;
1035}
1036
1037static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
1038        char *tmp = g_malloc(BUF_LONG + 1);
1039        struct gaim_connection *gc = sess->aux_data;
1040        int flags = 0;
1041       
1042        if (args->icbmflags & AIM_IMFLAGS_AWAY)
1043                flags |= IM_FLAG_AWAY;
1044       
1045        if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) {
1046                char *src;
1047               
1048                if (args->icbmflags & AIM_IMFLAGS_UNICODE)
1049                        src = "UNICODEBIG";
1050                else
1051                        src = "ISO8859-1";
1052               
1053                /* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */
1054                if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) {
1055                        // Successfully converted!
1056                } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
1057                        int i;
1058                       
1059                        for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
1060                                unsigned short uni;
1061                               
1062                                uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1063       
1064                                if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1065                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
1066                                } else { /* something else, do UNICODE entity */
1067                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
1068                                }
1069                        }
1070                } else {
1071                        g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1072                }
1073        } else
1074                g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1075       
1076        strip_linefeed(tmp);
1077        serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1);
1078        g_free(tmp);
1079       
1080        return 1;
1081}
1082
1083static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
1084#if 0
1085        struct gaim_connection *gc = sess->aux_data;
1086#endif
1087
1088        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1089                return 1;
1090#if 0
1091        if (args->reqclass & AIM_CAPS_CHAT) {
1092                char *name = extract_name(args->info.chat.roominfo.name);
1093                int *exch = g_new0(int, 1);
1094                GList *m = NULL;
1095                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1096                *exch = args->info.chat.roominfo.exchange;
1097                m = g_list_append(m, exch);
1098                serv_got_chat_invite(gc,
1099                                     name ? name : args->info.chat.roominfo.name,
1100                                     userinfo->sn,
1101                                     (char *)args->msg,
1102                                     m);
1103                if (name)
1104                        g_free(name);
1105        }
1106#endif
1107        return 1;
1108}
1109
1110static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
1111        char *uin, message;
1112        struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
1113       
1114        uin = g_strdup_printf("%u", data->uin);
1115        message = 0;
1116        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1117        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
1118        show_got_added(data->gc, NULL, uin, NULL, NULL);
1119       
1120        g_free(uin);
1121        g_free(data);
1122}
1123
1124static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
1125        char *uin, *message;
1126        struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
1127       
1128        uin = g_strdup_printf("%u", data->uin);
1129        message = g_strdup_printf("No reason given.");
1130        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1131        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1132        g_free(message);
1133       
1134        g_free(uin);
1135        g_free(data);
1136}
1137
1138/*
1139 * For when other people ask you for authorization
1140 */
1141static void gaim_icq_authask(struct gaim_connection *gc, guint32 uin, char *msg) {
1142        struct icq_auth *data = g_new(struct icq_auth, 1);
1143        char *reason = NULL;
1144        char *dialog_msg;
1145       
1146        if (strlen(msg) > 6)
1147                reason = msg + 6;
1148       
1149        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.");
1150        data->gc = gc;
1151        data->uin = uin;
1152        do_ask_dialog(gc, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
1153        g_free(dialog_msg);
1154}
1155
1156static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
1157        struct gaim_connection *gc = sess->aux_data;
1158
1159        switch (args->type) {
1160                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1161                        char *uin, *message;
1162                        uin = g_strdup_printf("%u", args->uin);
1163                        message = g_strdup(args->msg);
1164                        strip_linefeed(message);
1165                        serv_got_im(gc, uin, message, 0, time(NULL), -1);
1166                        g_free(uin);
1167                        g_free(message);
1168                } break;
1169
1170                case 0x0004: { /* Someone sent you a URL */
1171                        char *uin, *message;
1172                        char **m;
1173       
1174                        uin = g_strdup_printf("%u", args->uin);
1175                        m = g_strsplit(args->msg, "\376", 2);
1176
1177                        if ((strlen(m[0]) != 0)) {
1178                          message = g_strjoinv(" -- ", m);
1179                        } else {
1180                          message = m[1];
1181                        }
1182
1183                        strip_linefeed(message);
1184                        serv_got_im(gc, uin, message, 0, time(NULL), -1);
1185                        g_free(uin);
1186                        g_free(m);
1187                        g_free(message);
1188                } break;
1189               
1190                case 0x0006: { /* Someone requested authorization */
1191                        gaim_icq_authask(gc, args->uin, args->msg);
1192                } break;
1193
1194                case 0x0007: { /* Someone has denied you authorization */
1195                        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.") );
1196                } break;
1197
1198                case 0x0008: { /* Someone has granted you authorization */
1199                        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.") );
1200                } break;
1201
1202                case 0x0012: {
1203                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1204                } break;
1205
1206                default: {;
1207                } break;
1208        }
1209
1210        return 1;
1211}
1212
1213static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1214        int channel, ret = 0;
1215        aim_userinfo_t *userinfo;
1216        va_list ap;
1217
1218        va_start(ap, fr);
1219        channel = va_arg(ap, int);
1220        userinfo = va_arg(ap, aim_userinfo_t *);
1221
1222    if (set_getint(sess->aux_data, "debug")) {
1223        serv_got_crap(sess->aux_data, "channel %i called", channel);
1224    }
1225
1226        switch (channel) {
1227                case 1: { /* standard message */
1228                        struct aim_incomingim_ch1_args *args;
1229                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1230                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1231                } break;
1232
1233                case 2: { /* rendevous */
1234                        struct aim_incomingim_ch2_args *args;
1235                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1236                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1237                } break;
1238
1239                case 4: { /* ICQ */
1240                        struct aim_incomingim_ch4_args *args;
1241                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1242                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1243                } break;
1244
1245                default: {;
1246                } break;
1247        }
1248
1249        va_end(ap);
1250
1251        return ret;
1252}
1253
1254static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1255        va_list ap;
1256        guint16 chan, nummissed, reason;
1257        aim_userinfo_t *userinfo;
1258        char buf[1024];
1259
1260        va_start(ap, fr);
1261        chan = (guint16)va_arg(ap, unsigned int);
1262        userinfo = va_arg(ap, aim_userinfo_t *);
1263        nummissed = (guint16)va_arg(ap, unsigned int);
1264        reason = (guint16)va_arg(ap, unsigned int);
1265        va_end(ap);
1266
1267        switch(reason) {
1268                case 0:
1269                        /* Invalid (0) */
1270                        g_snprintf(buf,
1271                                   sizeof(buf),
1272                                   nummissed == 1 ? 
1273                                   _("You missed %d message from %s because it was invalid.") :
1274                                   _("You missed %d messages from %s because they were invalid."),
1275                                   nummissed,
1276                                   userinfo->sn);
1277                        break;
1278                case 1:
1279                        /* Message too large */
1280                        g_snprintf(buf,
1281                                   sizeof(buf),
1282                                   nummissed == 1 ?
1283                                   _("You missed %d message from %s because it was too large.") :
1284                                   _("You missed %d messages from %s because they were too large."),
1285                                   nummissed,
1286                                   userinfo->sn);
1287                        break;
1288                case 2:
1289                        /* Rate exceeded */
1290                        g_snprintf(buf,
1291                                   sizeof(buf),
1292                                   nummissed == 1 ? 
1293                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1294                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1295                                   nummissed,
1296                                   userinfo->sn);
1297                        break;
1298                case 3:
1299                        /* Evil Sender */
1300                        g_snprintf(buf,
1301                                   sizeof(buf),
1302                                   nummissed == 1 ?
1303                                   _("You missed %d message from %s because it was too evil.") : 
1304                                   _("You missed %d messages from %s because they are too evil."),
1305                                   nummissed,
1306                                   userinfo->sn);
1307                        break;
1308                case 4:
1309                        /* Evil Receiver */
1310                        g_snprintf(buf,
1311                                   sizeof(buf),
1312                                   nummissed == 1 ? 
1313                                   _("You missed %d message from %s because you are too evil.") :
1314                                   _("You missed %d messages from %s because you are too evil."),
1315                                   nummissed,
1316                                   userinfo->sn);
1317                        break;
1318                default:
1319                        g_snprintf(buf,
1320                                   sizeof(buf),
1321                                   nummissed == 1 ? 
1322                                   _("You missed %d message from %s for unknown reasons.") :
1323                                   _("You missed %d messages from %s for unknown reasons."),
1324                                   nummissed,
1325                                   userinfo->sn);
1326                        break;
1327        }
1328        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1329
1330        return 1;
1331}
1332
1333static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1334        va_list ap;
1335        guint16 reason;
1336        char *m;
1337
1338        va_start(ap, fr);
1339        reason = (guint16)va_arg(ap, unsigned int);
1340        va_end(ap);
1341
1342        m = g_strdup_printf(_("SNAC threw error: %s\n"),
1343                        reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1344        do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error"));
1345        g_free(m);
1346
1347        return 1;
1348}
1349
1350static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1351        va_list ap;
1352        char *destn;
1353        guint16 reason;
1354        char buf[1024];
1355
1356        va_start(ap, fr);
1357        reason = (guint16)va_arg(ap, unsigned int);
1358        destn = va_arg(ap, char *);
1359        va_end(ap);
1360
1361        sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
1362                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1363        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1364
1365        return 1;
1366}
1367
1368static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1369        va_list ap;
1370        char *destn;
1371        guint16 reason;
1372        char buf[1024];
1373
1374        va_start(ap, fr);
1375        reason = (guint16)va_arg(ap, unsigned int);
1376        destn = va_arg(ap, char *);
1377        va_end(ap);
1378
1379        sprintf(buf, _("User information for %s unavailable: %s"), destn,
1380                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1381        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1382
1383
1384        return 1;
1385}
1386
1387static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1388        char *msg;
1389        guint16 id;
1390        va_list ap;
1391
1392        va_start(ap, fr);
1393        id  = (guint16)va_arg(ap, unsigned int);
1394        msg = va_arg(ap, char *);
1395        va_end(ap);
1396
1397        if (id < 4)
1398                do_error_dialog(sess->aux_data, _("Your connection may be lost."),
1399                                _("AOL error"));
1400
1401        return 1;
1402}
1403
1404static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1405        va_list ap;
1406        guint16 type;
1407        struct gaim_connection *gc = sess->aux_data;
1408        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1409
1410        va_start(ap, fr);
1411        type = (guint16)va_arg(ap, unsigned int);
1412
1413        switch(type) {
1414                case 0x0002: {
1415                        guint8 maxrooms;
1416                        struct aim_chat_exchangeinfo *exchanges;
1417                        int exchangecount; // i;
1418
1419                        maxrooms = (guint8)va_arg(ap, unsigned int);
1420                        exchangecount = va_arg(ap, int);
1421                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1422                        va_end(ap);
1423
1424                        while (odata->create_rooms) {
1425                                struct create_room *cr = odata->create_rooms->data;
1426                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1427                                g_free(cr->name);
1428                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1429                                g_free(cr);
1430                        }
1431                        }
1432                        break;
1433                case 0x0008: {
1434                        char *fqcn, *name, *ck;
1435                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1436                        guint8 createperms;
1437                        guint32 createtime;
1438
1439                        fqcn = va_arg(ap, char *);
1440                        instance = (guint16)va_arg(ap, unsigned int);
1441                        exchange = (guint16)va_arg(ap, unsigned int);
1442                        flags = (guint16)va_arg(ap, unsigned int);
1443                        createtime = va_arg(ap, guint32);
1444                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1445                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1446                        createperms = (guint8)va_arg(ap, int);
1447                        unknown = (guint16)va_arg(ap, unsigned int);
1448                        name = va_arg(ap, char *);
1449                        ck = va_arg(ap, char *);
1450                        va_end(ap);
1451
1452                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1453                        }
1454                        break;
1455                default:
1456                        va_end(ap);
1457                        break;
1458        }
1459        return 1;
1460}
1461
1462static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1463        va_list ap;
1464        int count, i;
1465        aim_userinfo_t *info;
1466        struct gaim_connection *g = sess->aux_data;
1467
1468        struct chat_connection *c = NULL;
1469
1470        va_start(ap, fr);
1471        count = va_arg(ap, int);
1472        info  = va_arg(ap, aim_userinfo_t *);
1473        va_end(ap);
1474
1475        c = find_oscar_chat_by_conn(g, fr->conn);
1476        if (!c)
1477                return 1;
1478
1479        for (i = 0; i < count; i++)
1480                add_chat_buddy(c->cnv, info[i].sn);
1481
1482        return 1;
1483}
1484
1485static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1486        va_list ap;
1487        int count, i;
1488        aim_userinfo_t *info;
1489        struct gaim_connection *g = sess->aux_data;
1490
1491        struct chat_connection *c = NULL;
1492
1493        va_start(ap, fr);
1494        count = va_arg(ap, int);
1495        info  = va_arg(ap, aim_userinfo_t *);
1496        va_end(ap);
1497
1498        c = find_oscar_chat_by_conn(g, fr->conn);
1499        if (!c)
1500                return 1;
1501
1502        for (i = 0; i < count; i++)
1503                remove_chat_buddy(c->cnv, info[i].sn, NULL);
1504
1505        return 1;
1506}
1507
1508static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1509        va_list ap;
1510        aim_userinfo_t *userinfo;
1511        struct aim_chat_roominfo *roominfo;
1512        char *roomname;
1513        int usercount;
1514        char *roomdesc;
1515        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1516        guint32 creationtime;
1517        struct gaim_connection *gc = sess->aux_data;
1518        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1519
1520        va_start(ap, fr);
1521        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1522        roomname = va_arg(ap, char *);
1523        usercount= va_arg(ap, int);
1524        userinfo = va_arg(ap, aim_userinfo_t *);
1525        roomdesc = va_arg(ap, char *);
1526        unknown_c9 = (guint16)va_arg(ap, int);
1527        creationtime = (guint32)va_arg(ap, unsigned long);
1528        maxmsglen = (guint16)va_arg(ap, int);
1529        unknown_d2 = (guint16)va_arg(ap, int);
1530        unknown_d5 = (guint16)va_arg(ap, int);
1531        maxvisiblemsglen = (guint16)va_arg(ap, int);
1532        va_end(ap);
1533
1534        ccon->maxlen = maxmsglen;
1535        ccon->maxvis = maxvisiblemsglen;
1536
1537        return 1;
1538}
1539
1540static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1541        va_list ap;
1542        aim_userinfo_t *info;
1543        char *msg;
1544        struct gaim_connection *gc = sess->aux_data;
1545        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1546        char *tmp;
1547
1548        va_start(ap, fr);
1549        info = va_arg(ap, aim_userinfo_t *);
1550        msg  = va_arg(ap, char *);
1551
1552        tmp = g_malloc(BUF_LONG);
1553        g_snprintf(tmp, BUF_LONG, "%s", msg);
1554        serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL));
1555        g_free(tmp);
1556
1557        return 1;
1558}
1559
1560static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1561#if 0
1562        static const char *codes[5] = {
1563                "invalid",
1564                 "change",
1565                 "warning",
1566                 "limit",
1567                 "limit cleared",
1568        };
1569#endif
1570        va_list ap;
1571        guint16 code, rateclass;
1572        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1573
1574        va_start(ap, fr); 
1575        code = (guint16)va_arg(ap, unsigned int);
1576        rateclass= (guint16)va_arg(ap, unsigned int);
1577        windowsize = (guint32)va_arg(ap, unsigned long);
1578        clear = (guint32)va_arg(ap, unsigned long);
1579        alert = (guint32)va_arg(ap, unsigned long);
1580        limit = (guint32)va_arg(ap, unsigned long);
1581        disconnect = (guint32)va_arg(ap, unsigned long);
1582        currentavg = (guint32)va_arg(ap, unsigned long);
1583        maxavg = (guint32)va_arg(ap, unsigned long);
1584        va_end(ap);
1585
1586        /* XXX fix these values */
1587        if (code == AIM_RATE_CODE_CHANGE) {
1588                if (currentavg >= clear)
1589                        aim_conn_setlatency(fr->conn, 0);
1590        } else if (code == AIM_RATE_CODE_WARNING) {
1591                aim_conn_setlatency(fr->conn, windowsize/4);
1592        } else if (code == AIM_RATE_CODE_LIMIT) {
1593                do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1594                                  "Please wait 10 seconds and try again."), _("Gaim - Error"));
1595                aim_conn_setlatency(fr->conn, windowsize/2);
1596        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1597                aim_conn_setlatency(fr->conn, 0);
1598        }
1599
1600        return 1;
1601}
1602
1603static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1604        va_list ap;
1605        aim_userinfo_t *info;
1606        struct gaim_connection *gc = sess->aux_data;
1607
1608        va_start(ap, fr);
1609        info = va_arg(ap, aim_userinfo_t *);
1610        va_end(ap);
1611
1612        gc->evil = info->warnlevel/10;
1613        /* gc->correction_time = (info->onlinesince - gc->login_time); */
1614
1615        return 1;
1616}
1617
1618static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1619
1620        aim_reqpersonalinfo(sess, fr->conn);
1621        aim_bos_reqlocaterights(sess, fr->conn);
1622        aim_bos_reqbuddyrights(sess, fr->conn);
1623
1624        aim_reqicbmparams(sess);
1625
1626        aim_bos_reqrights(sess, fr->conn);
1627        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1628        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1629                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1630
1631        return 1;
1632}
1633
1634static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1635        struct gaim_connection *gc = sess->aux_data;
1636        struct oscar_data *od = gc->proto_data;
1637
1638        aim_clientready(sess, fr->conn);
1639
1640        if (od->chpass) {
1641                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1642                g_free(od->oldp);
1643                od->oldp = NULL;
1644                g_free(od->newp);
1645                od->newp = NULL;
1646                od->chpass = FALSE;
1647        }
1648        if (od->setnick) {
1649                aim_admin_setnick(sess, fr->conn, od->newsn);
1650                g_free(od->newsn);
1651                od->newsn = NULL;
1652                od->setnick = FALSE;
1653        }
1654        if (od->conf) {
1655                aim_admin_reqconfirm(sess, fr->conn);
1656                od->conf = FALSE;
1657        }
1658        if (od->reqemail) {
1659                aim_admin_getinfo(sess, fr->conn, 0x0011);
1660                od->reqemail = FALSE;
1661        }
1662        if (od->setemail) {
1663                aim_admin_setemail(sess, fr->conn, od->email);
1664                g_free(od->email);
1665                od->setemail = FALSE;
1666        }
1667
1668        return 1;
1669}
1670
1671static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1672        struct aim_icbmparameters *params;
1673        va_list ap;
1674
1675        va_start(ap, fr);
1676        params = va_arg(ap, struct aim_icbmparameters *);
1677        va_end(ap);
1678
1679        /* Maybe senderwarn and recverwarn should be user preferences... */
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
2437static char *oscar_get_status_string( struct gaim_connection *gc, int number )
2438{
2439        struct oscar_data *od = gc->proto_data;
2440       
2441        if( ! number & UC_UNAVAILABLE )
2442        {
2443                return( NULL );
2444        }
2445        else if( od->icq )
2446        {
2447                number >>= 7;
2448                if( number & AIM_ICQ_STATE_DND )
2449                        return( "Do Not Disturb" );
2450                else if( number & AIM_ICQ_STATE_OUT )
2451                        return( "Not Available" );
2452                else if( number & AIM_ICQ_STATE_BUSY )
2453                        return( "Occupied" );
2454                else if( number & AIM_ICQ_STATE_INVISIBLE )
2455                        return( "Invisible" );
2456                else
2457                        return( "Away" );
2458        }
2459        else
2460        {
2461                return( "Away" );
2462        }
2463}
2464
2465static struct prpl *my_protocol = NULL;
2466
2467void oscar_init(struct prpl *ret) {
2468        ret->protocol = PROTO_OSCAR;
2469        ret->away_states = oscar_away_states;
2470        ret->login = oscar_login;
2471        ret->close = oscar_close;
2472        ret->send_im = oscar_send_im;
2473        ret->get_info = oscar_get_info;
2474        ret->set_away = oscar_set_away;
2475        ret->get_away = oscar_get_away;
2476        ret->add_buddy = oscar_add_buddy;
2477        ret->remove_buddy = oscar_remove_buddy;
2478        ret->add_permit = oscar_add_permit;
2479        ret->add_deny = oscar_add_deny;
2480        ret->rem_permit = oscar_rem_permit;
2481        ret->rem_deny = oscar_rem_deny;
2482        ret->set_permit_deny = oscar_set_permit_deny;
2483        ret->keepalive = oscar_keepalive;
2484        ret->cmp_buddynames = aim_sncmp;
2485        ret->get_status_string = oscar_get_status_string;
2486
2487        my_protocol = ret;
2488}
Note: See TracBrowser for help on using the repository browser.