source: protocols/oscar/oscar.c @ fbc7844

Last change on this file since fbc7844 was b7d3cc34, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-11-06T18:23:18Z

Initial repository (0.99 release tree)

  • 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/*
1213int handle_cmp_aim(const char * a, const char * b) {
1214        return handle_cmp(a, b, PROTO_TOC);
1215}
1216*/
1217static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1218        int channel, ret = 0;
1219        aim_userinfo_t *userinfo;
1220        va_list ap;
1221
1222        va_start(ap, fr);
1223        channel = va_arg(ap, int);
1224        userinfo = va_arg(ap, aim_userinfo_t *);
1225
1226    if (set_getint(sess->aux_data, "debug")) {
1227        serv_got_crap(sess->aux_data, "channel %i called", channel);
1228    }
1229
1230        switch (channel) {
1231                case 1: { /* standard message */
1232                        struct aim_incomingim_ch1_args *args;
1233                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1234                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1235                } break;
1236
1237                case 2: { /* rendevous */
1238                        struct aim_incomingim_ch2_args *args;
1239                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1240                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1241                } break;
1242
1243                case 4: { /* ICQ */
1244                        struct aim_incomingim_ch4_args *args;
1245                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1246                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1247                } break;
1248
1249                default: {;
1250                } break;
1251        }
1252
1253        va_end(ap);
1254
1255        return ret;
1256}
1257
1258static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1259        va_list ap;
1260        guint16 chan, nummissed, reason;
1261        aim_userinfo_t *userinfo;
1262        char buf[1024];
1263
1264        va_start(ap, fr);
1265        chan = (guint16)va_arg(ap, unsigned int);
1266        userinfo = va_arg(ap, aim_userinfo_t *);
1267        nummissed = (guint16)va_arg(ap, unsigned int);
1268        reason = (guint16)va_arg(ap, unsigned int);
1269        va_end(ap);
1270
1271        switch(reason) {
1272                case 0:
1273                        /* Invalid (0) */
1274                        g_snprintf(buf,
1275                                   sizeof(buf),
1276                                   nummissed == 1 ? 
1277                                   _("You missed %d message from %s because it was invalid.") :
1278                                   _("You missed %d messages from %s because they were invalid."),
1279                                   nummissed,
1280                                   userinfo->sn);
1281                        break;
1282                case 1:
1283                        /* Message too large */
1284                        g_snprintf(buf,
1285                                   sizeof(buf),
1286                                   nummissed == 1 ?
1287                                   _("You missed %d message from %s because it was too large.") :
1288                                   _("You missed %d messages from %s because they were too large."),
1289                                   nummissed,
1290                                   userinfo->sn);
1291                        break;
1292                case 2:
1293                        /* Rate exceeded */
1294                        g_snprintf(buf,
1295                                   sizeof(buf),
1296                                   nummissed == 1 ? 
1297                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1298                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1299                                   nummissed,
1300                                   userinfo->sn);
1301                        break;
1302                case 3:
1303                        /* Evil Sender */
1304                        g_snprintf(buf,
1305                                   sizeof(buf),
1306                                   nummissed == 1 ?
1307                                   _("You missed %d message from %s because it was too evil.") : 
1308                                   _("You missed %d messages from %s because they are too evil."),
1309                                   nummissed,
1310                                   userinfo->sn);
1311                        break;
1312                case 4:
1313                        /* Evil Receiver */
1314                        g_snprintf(buf,
1315                                   sizeof(buf),
1316                                   nummissed == 1 ? 
1317                                   _("You missed %d message from %s because you are too evil.") :
1318                                   _("You missed %d messages from %s because you are too evil."),
1319                                   nummissed,
1320                                   userinfo->sn);
1321                        break;
1322                default:
1323                        g_snprintf(buf,
1324                                   sizeof(buf),
1325                                   nummissed == 1 ? 
1326                                   _("You missed %d message from %s for unknown reasons.") :
1327                                   _("You missed %d messages from %s for unknown reasons."),
1328                                   nummissed,
1329                                   userinfo->sn);
1330                        break;
1331        }
1332        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1333
1334        return 1;
1335}
1336
1337static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1338        va_list ap;
1339        guint16 reason;
1340        char *m;
1341
1342        va_start(ap, fr);
1343        reason = (guint16)va_arg(ap, unsigned int);
1344        va_end(ap);
1345
1346        m = g_strdup_printf(_("SNAC threw error: %s\n"),
1347                        reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1348        do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error"));
1349        g_free(m);
1350
1351        return 1;
1352}
1353
1354static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1355        va_list ap;
1356        char *destn;
1357        guint16 reason;
1358        char buf[1024];
1359
1360        va_start(ap, fr);
1361        reason = (guint16)va_arg(ap, unsigned int);
1362        destn = va_arg(ap, char *);
1363        va_end(ap);
1364
1365        sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
1366                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1367        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1368
1369        return 1;
1370}
1371
1372static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1373        va_list ap;
1374        char *destn;
1375        guint16 reason;
1376        char buf[1024];
1377
1378        va_start(ap, fr);
1379        reason = (guint16)va_arg(ap, unsigned int);
1380        destn = va_arg(ap, char *);
1381        va_end(ap);
1382
1383        sprintf(buf, _("User information for %s unavailable: %s"), destn,
1384                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1385        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1386
1387
1388        return 1;
1389}
1390
1391static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1392        char *msg;
1393        guint16 id;
1394        va_list ap;
1395
1396        va_start(ap, fr);
1397        id  = (guint16)va_arg(ap, unsigned int);
1398        msg = va_arg(ap, char *);
1399        va_end(ap);
1400
1401        if (id < 4)
1402                do_error_dialog(sess->aux_data, _("Your connection may be lost."),
1403                                _("AOL error"));
1404
1405        return 1;
1406}
1407
1408static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1409        va_list ap;
1410        guint16 type;
1411        struct gaim_connection *gc = sess->aux_data;
1412        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1413
1414        va_start(ap, fr);
1415        type = (guint16)va_arg(ap, unsigned int);
1416
1417        switch(type) {
1418                case 0x0002: {
1419                        guint8 maxrooms;
1420                        struct aim_chat_exchangeinfo *exchanges;
1421                        int exchangecount; // i;
1422
1423                        maxrooms = (guint8)va_arg(ap, unsigned int);
1424                        exchangecount = va_arg(ap, int);
1425                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1426                        va_end(ap);
1427
1428                        while (odata->create_rooms) {
1429                                struct create_room *cr = odata->create_rooms->data;
1430                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1431                                g_free(cr->name);
1432                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1433                                g_free(cr);
1434                        }
1435                        }
1436                        break;
1437                case 0x0008: {
1438                        char *fqcn, *name, *ck;
1439                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1440                        guint8 createperms;
1441                        guint32 createtime;
1442
1443                        fqcn = va_arg(ap, char *);
1444                        instance = (guint16)va_arg(ap, unsigned int);
1445                        exchange = (guint16)va_arg(ap, unsigned int);
1446                        flags = (guint16)va_arg(ap, unsigned int);
1447                        createtime = va_arg(ap, guint32);
1448                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1449                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1450                        createperms = (guint8)va_arg(ap, int);
1451                        unknown = (guint16)va_arg(ap, unsigned int);
1452                        name = va_arg(ap, char *);
1453                        ck = va_arg(ap, char *);
1454                        va_end(ap);
1455
1456                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1457                        }
1458                        break;
1459                default:
1460                        va_end(ap);
1461                        break;
1462        }
1463        return 1;
1464}
1465
1466static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1467        va_list ap;
1468        int count, i;
1469        aim_userinfo_t *info;
1470        struct gaim_connection *g = sess->aux_data;
1471
1472        struct chat_connection *c = NULL;
1473
1474        va_start(ap, fr);
1475        count = va_arg(ap, int);
1476        info  = va_arg(ap, aim_userinfo_t *);
1477        va_end(ap);
1478
1479        c = find_oscar_chat_by_conn(g, fr->conn);
1480        if (!c)
1481                return 1;
1482
1483        for (i = 0; i < count; i++)
1484                add_chat_buddy(c->cnv, info[i].sn);
1485
1486        return 1;
1487}
1488
1489static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1490        va_list ap;
1491        int count, i;
1492        aim_userinfo_t *info;
1493        struct gaim_connection *g = sess->aux_data;
1494
1495        struct chat_connection *c = NULL;
1496
1497        va_start(ap, fr);
1498        count = va_arg(ap, int);
1499        info  = va_arg(ap, aim_userinfo_t *);
1500        va_end(ap);
1501
1502        c = find_oscar_chat_by_conn(g, fr->conn);
1503        if (!c)
1504                return 1;
1505
1506        for (i = 0; i < count; i++)
1507                remove_chat_buddy(c->cnv, info[i].sn, NULL);
1508
1509        return 1;
1510}
1511
1512static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1513        va_list ap;
1514        aim_userinfo_t *userinfo;
1515        struct aim_chat_roominfo *roominfo;
1516        char *roomname;
1517        int usercount;
1518        char *roomdesc;
1519        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1520        guint32 creationtime;
1521        struct gaim_connection *gc = sess->aux_data;
1522        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1523
1524        va_start(ap, fr);
1525        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1526        roomname = va_arg(ap, char *);
1527        usercount= va_arg(ap, int);
1528        userinfo = va_arg(ap, aim_userinfo_t *);
1529        roomdesc = va_arg(ap, char *);
1530        unknown_c9 = (guint16)va_arg(ap, int);
1531        creationtime = (guint32)va_arg(ap, unsigned long);
1532        maxmsglen = (guint16)va_arg(ap, int);
1533        unknown_d2 = (guint16)va_arg(ap, int);
1534        unknown_d5 = (guint16)va_arg(ap, int);
1535        maxvisiblemsglen = (guint16)va_arg(ap, int);
1536        va_end(ap);
1537
1538        ccon->maxlen = maxmsglen;
1539        ccon->maxvis = maxvisiblemsglen;
1540
1541        return 1;
1542}
1543
1544static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1545        va_list ap;
1546        aim_userinfo_t *info;
1547        char *msg;
1548        struct gaim_connection *gc = sess->aux_data;
1549        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1550        char *tmp;
1551
1552        va_start(ap, fr);
1553        info = va_arg(ap, aim_userinfo_t *);
1554        msg  = va_arg(ap, char *);
1555
1556        tmp = g_malloc(BUF_LONG);
1557        g_snprintf(tmp, BUF_LONG, "%s", msg);
1558        serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL));
1559        g_free(tmp);
1560
1561        return 1;
1562}
1563
1564static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1565#if 0
1566        static const char *codes[5] = {
1567                "invalid",
1568                 "change",
1569                 "warning",
1570                 "limit",
1571                 "limit cleared",
1572        };
1573#endif
1574        va_list ap;
1575        guint16 code, rateclass;
1576        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1577
1578        va_start(ap, fr); 
1579        code = (guint16)va_arg(ap, unsigned int);
1580        rateclass= (guint16)va_arg(ap, unsigned int);
1581        windowsize = (guint32)va_arg(ap, unsigned long);
1582        clear = (guint32)va_arg(ap, unsigned long);
1583        alert = (guint32)va_arg(ap, unsigned long);
1584        limit = (guint32)va_arg(ap, unsigned long);
1585        disconnect = (guint32)va_arg(ap, unsigned long);
1586        currentavg = (guint32)va_arg(ap, unsigned long);
1587        maxavg = (guint32)va_arg(ap, unsigned long);
1588        va_end(ap);
1589
1590        /* XXX fix these values */
1591        if (code == AIM_RATE_CODE_CHANGE) {
1592                if (currentavg >= clear)
1593                        aim_conn_setlatency(fr->conn, 0);
1594        } else if (code == AIM_RATE_CODE_WARNING) {
1595                aim_conn_setlatency(fr->conn, windowsize/4);
1596        } else if (code == AIM_RATE_CODE_LIMIT) {
1597                do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1598                                  "Please wait 10 seconds and try again."), _("Gaim - Error"));
1599                aim_conn_setlatency(fr->conn, windowsize/2);
1600        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1601                aim_conn_setlatency(fr->conn, 0);
1602        }
1603
1604        return 1;
1605}
1606
1607static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1608        va_list ap;
1609        aim_userinfo_t *info;
1610        struct gaim_connection *gc = sess->aux_data;
1611
1612        va_start(ap, fr);
1613        info = va_arg(ap, aim_userinfo_t *);
1614        va_end(ap);
1615
1616        gc->evil = info->warnlevel/10;
1617        /* gc->correction_time = (info->onlinesince - gc->login_time); */
1618
1619        return 1;
1620}
1621
1622static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1623
1624        aim_reqpersonalinfo(sess, fr->conn);
1625        aim_bos_reqlocaterights(sess, fr->conn);
1626        aim_bos_reqbuddyrights(sess, fr->conn);
1627
1628        aim_reqicbmparams(sess);
1629
1630        aim_bos_reqrights(sess, fr->conn);
1631        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1632        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1633                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1634
1635        return 1;
1636}
1637
1638static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1639        struct gaim_connection *gc = sess->aux_data;
1640        struct oscar_data *od = gc->proto_data;
1641
1642        aim_clientready(sess, fr->conn);
1643
1644        if (od->chpass) {
1645                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1646                g_free(od->oldp);
1647                od->oldp = NULL;
1648                g_free(od->newp);
1649                od->newp = NULL;
1650                od->chpass = FALSE;
1651        }
1652        if (od->setnick) {
1653                aim_admin_setnick(sess, fr->conn, od->newsn);
1654                g_free(od->newsn);
1655                od->newsn = NULL;
1656                od->setnick = FALSE;
1657        }
1658        if (od->conf) {
1659                aim_admin_reqconfirm(sess, fr->conn);
1660                od->conf = FALSE;
1661        }
1662        if (od->reqemail) {
1663                aim_admin_getinfo(sess, fr->conn, 0x0011);
1664                od->reqemail = FALSE;
1665        }
1666        if (od->setemail) {
1667                aim_admin_setemail(sess, fr->conn, od->email);
1668                g_free(od->email);
1669                od->setemail = FALSE;
1670        }
1671
1672        return 1;
1673}
1674
1675static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1676        struct aim_icbmparameters *params;
1677        va_list ap;
1678
1679        va_start(ap, fr);
1680        params = va_arg(ap, struct aim_icbmparameters *);
1681        va_end(ap);
1682
1683        /* Maybe senderwarn and recverwarn should be user preferences... */
1684        params->maxmsglen = 8000;
1685        params->minmsginterval = 0;
1686
1687        aim_seticbmparam(sess, params);
1688
1689        return 1;
1690}
1691
1692static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1693{
1694        va_list ap;
1695        guint16 maxsiglen;
1696        struct gaim_connection *gc = sess->aux_data;
1697        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1698
1699        va_start(ap, fr);
1700        maxsiglen = va_arg(ap, int);
1701        va_end(ap);
1702
1703        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1704
1705        aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps);
1706
1707        return 1;
1708}
1709
1710static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1711        va_list ap;
1712        guint16 maxbuddies, maxwatchers;
1713        struct gaim_connection *gc = sess->aux_data;
1714        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1715
1716        va_start(ap, fr);
1717        maxbuddies = (guint16)va_arg(ap, unsigned int);
1718        maxwatchers = (guint16)va_arg(ap, unsigned int);
1719        va_end(ap);
1720
1721        odata->rights.maxbuddies = (guint)maxbuddies;
1722        odata->rights.maxwatchers = (guint)maxwatchers;
1723
1724        return 1;
1725}
1726
1727static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1728        guint16 maxpermits, maxdenies;
1729        va_list ap;
1730        struct gaim_connection *gc = sess->aux_data;
1731        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1732
1733        va_start(ap, fr);
1734        maxpermits = (guint16)va_arg(ap, unsigned int);
1735        maxdenies = (guint16)va_arg(ap, unsigned int);
1736        va_end(ap);
1737
1738        odata->rights.maxpermits = (guint)maxpermits;
1739        odata->rights.maxdenies = (guint)maxdenies;
1740
1741//      serv_finish_login(gc);
1742
1743        if (bud_list_cache_exists(gc))
1744                do_import(gc, NULL);
1745
1746        aim_clientready(sess, fr->conn);
1747
1748        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1749
1750        aim_ssi_reqrights(sess, fr->conn);
1751        aim_ssi_reqalldata(sess, fr->conn);
1752
1753        return 1;
1754}
1755
1756static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1757        va_list ap;
1758        struct aim_icq_offlinemsg *msg;
1759        struct gaim_connection *gc = sess->aux_data;
1760
1761        va_start(ap, fr);
1762        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1763        va_end(ap);
1764
1765        switch (msg->type) {
1766                case 0x0001: { /* Basic offline message */
1767                        char sender[32];
1768                        char *dialog_msg = g_strdup(msg->msg);
1769                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1770                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1771                        strip_linefeed(dialog_msg);
1772                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1773                        g_free(dialog_msg);
1774                } break;
1775
1776                case 0x0004: { /* Someone sent you a URL */
1777                        char sender[32];
1778                        char *dialog_msg;
1779                        char **m;
1780
1781                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1782                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1783
1784                        m = g_strsplit(msg->msg, "\376", 2);
1785
1786                        if ((strlen(m[0]) != 0)) {
1787                          dialog_msg = g_strjoinv(" -- ", m);
1788                        } else {
1789                          dialog_msg = m[1];
1790                        }
1791
1792                        strip_linefeed(dialog_msg);
1793                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1794                        g_free(dialog_msg);
1795                        g_free(m);
1796                } break;
1797               
1798                case 0x0006: { /* Authorization request */
1799                        gaim_icq_authask(gc, msg->sender, msg->msg);
1800                } break;
1801
1802                case 0x0007: { /* Someone has denied you authorization */
1803                        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.") );
1804                } break;
1805
1806                case 0x0008: { /* Someone has granted you authorization */
1807                        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.") );
1808                } break;
1809
1810                case 0x0012: {
1811                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1812                } break;
1813
1814                default: {;
1815                }
1816        }
1817
1818        return 1;
1819}
1820
1821static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1822{
1823        aim_icq_ackofflinemsgs(sess);
1824        return 1;
1825}
1826
1827static void oscar_keepalive(struct gaim_connection *gc) {
1828        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1829        aim_flap_nop(odata->sess, odata->conn);
1830}
1831
1832static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) {
1833        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1834        int ret = 0;
1835        if (imflags & IM_FLAG_AWAY) {
1836                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1837        } else {
1838                struct aim_sendimext_args args;
1839                char *s;
1840               
1841                args.flags = AIM_IMFLAGS_ACK;
1842                if (odata->icq)
1843                        args.flags |= AIM_IMFLAGS_OFFLINE;
1844                for (s = message; *s; s++)
1845                        if (*s & 128)
1846                                break;
1847               
1848                /* Message contains high ASCII chars, time for some translation! */
1849                if (*s) {
1850                        s = g_malloc(BUF_LONG);
1851                        /* Try if we can put it in an ISO8859-1 string first.
1852                           If we can't, fall back to UTF16. */
1853                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1854                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1855                                len = ret;
1856                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1857                                args.flags |= AIM_IMFLAGS_UNICODE;
1858                                len = ret;
1859                        } else {
1860                                /* OOF, translation failed... Oh well.. */
1861                                g_free( s );
1862                                s = message;
1863                        }
1864                } else {
1865                        s = message;
1866                }
1867               
1868                args.features = gaim_features;
1869                args.featureslen = sizeof(gaim_features);
1870               
1871                args.destsn = name;
1872                args.msg    = s;
1873                args.msglen = len;
1874               
1875                ret = aim_send_im_ext(odata->sess, &args);
1876               
1877                if (s != message) {
1878                        g_free(s);
1879                }
1880        }
1881        if (ret >= 0)
1882                return 1;
1883        return ret;
1884}
1885
1886static void oscar_get_info(struct gaim_connection *g, char *name) {
1887        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1888        if (odata->icq)
1889                aim_icq_getallinfo(odata->sess, name);
1890        else {
1891                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1892                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1893        }
1894}
1895
1896static void oscar_get_away(struct gaim_connection *g, char *who) {
1897        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1898        if (odata->icq) {
1899                struct buddy *budlight = find_buddy(g, who);
1900                if (budlight)
1901                        if ((budlight->uc & 0xff80) >> 7)
1902                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1903                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1904        } else
1905                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1906}
1907
1908static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1909{
1910
1911        if (!g_strcasecmp(state, _("Visible"))) {
1912                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1913                return;
1914        } else if (!g_strcasecmp(state, _("Invisible"))) {
1915                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1916                return;
1917        } /* else... */
1918
1919        if (od->rights.maxawaymsglen == 0)
1920                do_error_dialog(gc, "oscar_set_away_aim called before locate rights received", "Protocol Error");
1921
1922        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1923
1924        if (gc->away)
1925                g_free(gc->away);
1926        gc->away = NULL;
1927
1928        if (!message) {
1929                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1930                return;
1931        }
1932
1933        if (strlen(message) > od->rights.maxawaymsglen) {
1934                gchar *errstr;
1935
1936                errstr = g_strdup_printf("Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
1937
1938                do_error_dialog(gc, errstr, "Away Message Too Long");
1939
1940                g_free(errstr);
1941        }
1942
1943        gc->away = g_strndup(message, od->rights.maxawaymsglen);
1944        aim_bos_setprofile(od->sess, od->conn, NULL, gc->away, gaim_caps);
1945
1946        return;
1947}
1948
1949static void oscar_set_away_icq(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1950{
1951    const char *msg = NULL;
1952        gboolean no_message = FALSE;
1953
1954        /* clean old states */
1955    if (gc->away) {
1956                g_free(gc->away);
1957                gc->away = NULL;
1958    }
1959        od->sess->aim_icq_state = 0;
1960
1961        /* if no message, then use an empty message */
1962    if (message) {
1963        msg = message;
1964    } else {
1965        msg = "";
1966                no_message = TRUE;
1967    }
1968
1969        if (!g_strcasecmp(state, "Online")) {
1970                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1971        } else if (!g_strcasecmp(state, "Away")) {
1972                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1973        gc->away = g_strdup(msg);
1974                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1975        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1976                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
1977        gc->away = g_strdup(msg);
1978                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1979        } else if (!g_strcasecmp(state, "Not Available")) {
1980                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
1981        gc->away = g_strdup(msg);
1982                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1983        } else if (!g_strcasecmp(state, "Occupied")) {
1984                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
1985        gc->away = g_strdup(msg);
1986                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1987        } else if (!g_strcasecmp(state, "Free For Chat")) {
1988                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
1989        gc->away = g_strdup(msg);
1990                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1991        } else if (!g_strcasecmp(state, "Invisible")) {
1992                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1993        gc->away = g_strdup(msg);
1994        } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
1995                if (no_message) {
1996                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1997                } else {
1998                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1999            gc->away = g_strdup(msg);
2000                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
2001                }
2002        }
2003
2004        return;
2005}
2006
2007static void oscar_set_away(struct gaim_connection *gc, char *state, char *message)
2008{
2009        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2010
2011    oscar_set_away_aim(gc, od, state, message);
2012        if (od->icq)
2013                oscar_set_away_icq(gc, od, state, message);
2014
2015        return;
2016}
2017
2018static void oscar_add_buddy(struct gaim_connection *g, char *name) {
2019        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2020        aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
2021}
2022
2023static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) {
2024        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2025        struct aim_ssi_item *ssigroup;
2026        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
2027}
2028
2029static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
2030        return 1;
2031}
2032
2033static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
2034        struct gaim_connection *gc = sess->aux_data;
2035        struct aim_ssi_item *curitem;
2036        int tmp;
2037
2038        /* Add from server list to local list */
2039        tmp = 0;
2040        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
2041                switch (curitem->type) {
2042                        case 0x0000: /* Buddy */
2043                                if ((curitem->name) && (!find_buddy(gc, curitem->name))) {
2044                                        char *realname = NULL;
2045
2046                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
2047                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
2048                                               
2049                                        add_buddy(gc, NULL, curitem->name, realname);
2050                                       
2051                                        if (realname)
2052                                            g_free(realname);
2053                                }
2054                                break;
2055
2056                        case 0x0002: /* Permit buddy */
2057                                if (curitem->name) {
2058                                        GSList *list;
2059                                        for (list=gc->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2060                                        if (!list) {
2061                                                char *name;
2062                                                name = g_strdup(normalize(curitem->name));
2063                                                gc->permit = g_slist_append(gc->permit, name);
2064                                                build_allow_list();
2065                                                tmp++;
2066                                        }
2067                                }
2068                                break;
2069
2070                        case 0x0003: /* Deny buddy */
2071                                if (curitem->name) {
2072                                        GSList *list;
2073                                        for (list=gc->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2074                                        if (!list) {
2075                                                char *name;
2076                                                name = g_strdup(normalize(curitem->name));
2077                                                gc->deny = g_slist_append(gc->deny, name);
2078                                                build_block_list();
2079                                                tmp++;
2080                                        }
2081                                }
2082                                break;
2083
2084                        case 0x0004: /* Permit/deny setting */
2085                                if (curitem->data) {
2086                                        guint8 permdeny;
2087                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != gc->permdeny)) {
2088                                                gc->permdeny = permdeny;
2089                                                tmp++;
2090                                        }
2091                                }
2092                                break;
2093
2094                        case 0x0005: /* Presence setting */
2095                                /* We don't want to change Gaim's setting because it applies to all accounts */
2096                                break;
2097                } /* End of switch on curitem->type */
2098        } /* End of for loop */
2099
2100        if (tmp)
2101                do_export(gc);
2102        aim_ssi_enable(sess, fr->conn);
2103       
2104        /* Request offline messages, now that the buddy list is complete. */
2105        aim_icq_reqofflinemsgs(sess);
2106       
2107        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
2108        account_online(gc);
2109       
2110        return 1;
2111}
2112
2113static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2114{
2115        aim_snac_t *origsnac;
2116        va_list ap;
2117
2118        va_start( ap, fr );
2119        origsnac = va_arg( ap, aim_snac_t * );
2120        va_end( ap );
2121       
2122        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2123        {
2124                int i, st, count = aim_bstream_empty( &fr->data );
2125                char *list;
2126               
2127                if( count & 1 )
2128                {
2129                        /* Hmm, the length should be even... */
2130                        do_error_dialog( sess->aux_data, "Received SSI ACK package with non-even length", "Gaim - Error" );
2131                        return( 0 );
2132                }
2133                count >>= 1;
2134               
2135                list = (char *) origsnac->data;
2136                for( i = 0; i < count; i ++ )
2137                {
2138                        st = aimbs_get16( &fr->data );
2139                        if( st == 0x0E )
2140                        {
2141                                serv_got_crap( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
2142                               
2143                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2144                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2145                        }
2146                        list += strlen( list ) + 1;
2147                }
2148        }
2149       
2150        return( 1 );
2151}
2152
2153static void oscar_set_permit_deny(struct gaim_connection *gc) {
2154        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2155        if (od->icq) {
2156                GSList *list;
2157                char buf[MAXMSGLEN];
2158                int at;
2159
2160                switch(gc->permdeny) {
2161                case 1:
2162                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gc->username);
2163                        break;
2164                case 2:
2165                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gc->username);
2166                        break;
2167                case 3:
2168                        list = gc->permit;
2169                        at = 0;
2170                        while (list) {
2171                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2172                                list = list->next;
2173                        }
2174                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2175                        break;
2176                case 4:
2177                        list = gc->deny;
2178                        at = 0;
2179                        while (list) {
2180                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2181                                list = list->next;
2182                        }
2183                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2184                        break;
2185                        default:
2186                        break;
2187                }
2188                signoff_blocked(gc);
2189        } else {
2190                if (od->sess->ssi.received_data)
2191                        aim_ssi_setpermdeny(od->sess, od->conn, gc->permdeny, 0xffffffff);
2192        }
2193}
2194
2195static void oscar_add_permit(struct gaim_connection *gc, char *who) {
2196        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2197        if (od->icq) {
2198                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2199        } else {
2200                if (od->sess->ssi.received_data)
2201                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2202        }
2203}
2204
2205static void oscar_add_deny(struct gaim_connection *gc, char *who) {
2206        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2207        if (od->icq) {
2208                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2209        } else {
2210                if (od->sess->ssi.received_data)
2211                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2212        }
2213}
2214
2215static void oscar_rem_permit(struct gaim_connection *gc, char *who) {
2216        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2217        if (!od->icq) {
2218                if (od->sess->ssi.received_data)
2219                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2220        }
2221}
2222
2223static void oscar_rem_deny(struct gaim_connection *gc, char *who) {
2224        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2225        if (!od->icq) {
2226                if (od->sess->ssi.received_data)
2227                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2228        }
2229}
2230
2231static GList *oscar_away_states(struct gaim_connection *gc)
2232{
2233        struct oscar_data *od = gc->proto_data;
2234        GList *m = NULL;
2235
2236        if (!od->icq)
2237                return g_list_append(m, GAIM_AWAY_CUSTOM);
2238
2239        m = g_list_append(m, "Online");
2240        m = g_list_append(m, "Away");
2241        m = g_list_append(m, "Do Not Disturb");
2242        m = g_list_append(m, "Not Available");
2243        m = g_list_append(m, "Occupied");
2244        m = g_list_append(m, "Free For Chat");
2245        m = g_list_append(m, "Invisible");
2246
2247        return m;
2248}
2249
2250static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2251{
2252        struct gaim_connection *gc = sess->aux_data;
2253        gchar who[16];
2254        GString *str;
2255        va_list ap;
2256        struct aim_icq_info *info;
2257
2258        va_start(ap, fr);
2259        info = va_arg(ap, struct aim_icq_info *);
2260        va_end(ap);
2261
2262        if (!info->uin)
2263                return 0;
2264
2265        str = g_string_sized_new(100);
2266        g_snprintf(who, sizeof(who), "%u", info->uin);
2267
2268        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2269                                info->nick ? info->nick : "-");
2270        info_string_append(str, "\n", _("First Name"), info->first);
2271        info_string_append(str, "\n", _("Last Name"), info->last);
2272                info_string_append(str, "\n", _("Email Address"), info->email);
2273        if (info->numaddresses && info->email2) {
2274                int i;
2275                for (i = 0; i < info->numaddresses; i++) {
2276                                        info_string_append(str, "\n", _("Email Address"), info->email2[i]);
2277                }
2278        }
2279        info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
2280        info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
2281        if (info->birthyear || info->birthmonth || info->birthday) {
2282                char date[30];
2283                struct tm tm;
2284                tm.tm_mday = (int)info->birthday;
2285                tm.tm_mon = (int)info->birthmonth-1;
2286                tm.tm_year = (int)info->birthyear-1900;
2287                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2288                info_string_append(str, "\n", _("Birthday"), date);
2289        }
2290        if (info->age) {
2291                char age[5];
2292                g_snprintf(age, sizeof(age), "%hhd", info->age);
2293                info_string_append(str, "\n", _("Age"), age);
2294        }
2295                info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
2296        if (info->info && info->info[0]) {
2297                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2298                                                info->info, _("End of Additional Information"));
2299        }
2300        g_string_sprintfa(str, "\n");
2301        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2302                g_string_sprintfa(str, "%s:", _("Home Address"));
2303                info_string_append(str, "\n", _("Address"), info->homeaddr);
2304                info_string_append(str, "\n", _("City"), info->homecity);
2305                info_string_append(str, "\n", _("State"), info->homestate); 
2306                                info_string_append(str, "\n", _("Zip Code"), info->homezip);
2307                g_string_sprintfa(str, "\n");
2308        }
2309        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2310                g_string_sprintfa(str, "%s:", _("Work Address"));
2311                info_string_append(str, "\n", _("Address"), info->workaddr);
2312                info_string_append(str, "\n", _("City"), info->workcity);
2313                info_string_append(str, "\n", _("State"), info->workstate);
2314                                info_string_append(str, "\n", _("Zip Code"), info->workzip);
2315                g_string_sprintfa(str, "\n");
2316        }
2317        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2318                g_string_sprintfa(str, "%s:", _("Work Information"));
2319                info_string_append(str, "\n", _("Company"), info->workcompany);
2320                info_string_append(str, "\n", _("Division"), info->workdivision);
2321                info_string_append(str, "\n", _("Position"), info->workposition);
2322                if (info->workwebpage && info->workwebpage[0]) {
2323                        info_string_append(str, "\n", _("Web Page"), info->workwebpage);
2324                }
2325                g_string_sprintfa(str, "\n");
2326        }
2327
2328                serv_got_crap(gc, "%s\n%s", _("User Info"), str->str);
2329        g_string_free(str, TRUE);
2330
2331        return 1;
2332
2333}
2334
2335static char *oscar_encoding_extract(const char *encoding)
2336{
2337        char *ret = NULL;
2338        char *begin, *end;
2339
2340        g_return_val_if_fail(encoding != NULL, NULL);
2341
2342        /* Make sure encoding begins with charset= */
2343        if (strncmp(encoding, "text/plain; charset=", 20) &&
2344                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2345                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2346        {
2347                return NULL;
2348        }
2349
2350        begin = strchr(encoding, '"');
2351        end = strrchr(encoding, '"');
2352
2353        if ((begin == NULL) || (end == NULL) || (begin >= end))
2354                return NULL;
2355
2356        ret = g_strndup(begin+1, (end-1) - begin);
2357
2358        return ret;
2359}
2360
2361static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2362{
2363        char *utf8 = g_new0(char, 8192);
2364
2365        if ((encoding == NULL) || encoding[0] == '\0') {
2366                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2367        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2368                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2369        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2370                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2371        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2372                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2373        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2374                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2375                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2376                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2377        }
2378
2379        /*
2380         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2381         * we have been unable to convert the text to utf-8 from the encoding
2382         * that was specified.  So we assume it's UTF-8 and hope for the best.
2383         */
2384        if (*utf8 == 0) {
2385            strncpy(utf8, text, textlen);
2386        }
2387
2388        return utf8;
2389}
2390
2391static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2392{
2393        struct gaim_connection *gc = sess->aux_data;
2394        va_list ap;
2395        aim_userinfo_t *userinfo;
2396        guint16 infotype;
2397        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2398        guint16 text_length;
2399        char *utf8 = NULL;
2400
2401        va_start(ap, fr);
2402        userinfo = va_arg(ap, aim_userinfo_t *);
2403        infotype = va_arg(ap, int);
2404        text_encoding = va_arg(ap, char*);
2405        text = va_arg(ap, char*);
2406        text_length = va_arg(ap, int);
2407        va_end(ap);
2408
2409        if(text_encoding)
2410                extracted_encoding = oscar_encoding_extract(text_encoding);
2411        if(infotype == AIM_GETINFO_GENERALINFO) {
2412                /*Display idle time*/
2413                char buff[256];
2414                struct tm idletime;
2415                if(userinfo->idletime) {
2416                        memset(&idletime, 0, sizeof(struct tm));
2417                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2418                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2419                        idletime.tm_min = userinfo->idletime % 60;
2420                        idletime.tm_sec = 0;
2421                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
2422                        serv_got_crap(gc, "%s: %s", _("Idle Time"), buff);
2423                }
2424               
2425                if(text) {
2426                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2427                        serv_got_crap(gc, "%s\n%s", _("User Info"), utf8);
2428                } else {
2429                        serv_got_crap(gc, _("No user info available."));
2430                }
2431        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2432                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2433                serv_got_crap(gc, "%s\n%s", _("Away Message"), utf8);
2434        }
2435
2436        g_free(utf8);
2437   
2438        return 1;
2439}
2440
2441static char *oscar_get_status_string( struct gaim_connection *gc, int number )
2442{
2443        struct oscar_data *od = gc->proto_data;
2444       
2445        if( ! number & UC_UNAVAILABLE )
2446        {
2447                return( NULL );
2448        }
2449        else if( od->icq )
2450        {
2451                number >>= 7;
2452                if( number & AIM_ICQ_STATE_DND )
2453                        return( "Do Not Disturb" );
2454                else if( number & AIM_ICQ_STATE_OUT )
2455                        return( "Not Available" );
2456                else if( number & AIM_ICQ_STATE_BUSY )
2457                        return( "Occupied" );
2458                else if( number & AIM_ICQ_STATE_INVISIBLE )
2459                        return( "Invisible" );
2460                else
2461                        return( "Away" );
2462        }
2463        else
2464        {
2465                return( "Away" );
2466        }
2467}
2468
2469static struct prpl *my_protocol = NULL;
2470
2471void oscar_init(struct prpl *ret) {
2472        ret->protocol = PROTO_OSCAR;
2473        ret->away_states = oscar_away_states;
2474        ret->login = oscar_login;
2475        ret->close = oscar_close;
2476        ret->send_im = oscar_send_im;
2477        ret->get_info = oscar_get_info;
2478        ret->set_away = oscar_set_away;
2479        ret->get_away = oscar_get_away;
2480        ret->add_buddy = oscar_add_buddy;
2481        ret->remove_buddy = oscar_remove_buddy;
2482        ret->add_permit = oscar_add_permit;
2483        ret->add_deny = oscar_add_deny;
2484        ret->rem_permit = oscar_rem_permit;
2485        ret->rem_deny = oscar_rem_deny;
2486        ret->set_permit_deny = oscar_set_permit_deny;
2487        ret->keepalive = oscar_keepalive;
2488        ret->get_status_string = oscar_get_status_string;
2489
2490        my_protocol = ret;
2491}
Note: See TracBrowser for help on using the repository browser.