source: protocols/oscar/oscar.c @ c998255

Last change on this file since c998255 was c998255, checked in by Jelmer Vernooij <jelmer@…>, at 2005-11-15T14:57:38Z

Merge from Jelmer

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