source: protocols/oscar/oscar.c @ 7b23afd

Last change on this file since 7b23afd was 7b23afd, checked in by Jelmer Vernooij <jelmer@…>, at 2005-11-07T16:16:18Z

Migrate my pluginable branch to use Wilmers' branch as parent

  • Property mode set to 100644
File size: 71.2 KB
Line 
1/*
2 * gaim
3 *
4 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 *
21 */
22
23#include "sock.h"
24#include <errno.h>
25#include <ctype.h>
26#include <string.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <time.h>
30#include <sys/stat.h>
31#include <glib.h>
32#include "nogaim.h"
33#include "bitlbee.h"
34#include "proxy.h"
35
36#include "aim.h"
37#include "icq.h"
38#include "bos.h"
39#include "ssi.h"
40#include "im.h"
41#include "info.h"
42#include "buddylist.h"
43#include "chat.h"
44#include "chatnav.h"
45
46/* constants to identify proto_opts */
47#define USEROPT_AUTH      0
48#define USEROPT_AUTHPORT  1
49
50#define UC_AOL          0x02
51#define UC_ADMIN        0x04
52#define UC_UNCONFIRMED  0x08
53#define UC_NORMAL       0x10
54#define UC_AB           0x20
55#define UC_WIRELESS     0x40
56
57#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
58
59#define OSCAR_GROUP "Friends"
60
61/* Don't know if support for UTF8 is really working. For now it's UTF16 here.
62   static int gaim_caps = AIM_CAPS_UTF8; */
63
64static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY;
65static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02};
66
67struct oscar_data {
68        aim_session_t *sess;
69        aim_conn_t *conn;
70
71        guint cnpa;
72        guint paspa;
73
74        GSList *create_rooms;
75
76        gboolean conf;
77        gboolean reqemail;
78        gboolean setemail;
79        char *email;
80        gboolean setnick;
81        char *newsn;
82        gboolean chpass;
83        char *oldp;
84        char *newp;
85
86        GSList *oscar_chats;
87
88        gboolean killme;
89        gboolean icq;
90        GSList *evilhack;
91
92        struct {
93                guint maxbuddies; /* max users you can watch */
94                guint maxwatchers; /* max users who can watch you */
95                guint maxpermits; /* max users on permit list */
96                guint maxdenies; /* max users on deny list */
97                guint maxsiglen; /* max size (bytes) of profile */
98                guint maxawaymsglen; /* max size (bytes) of posted away message */
99        } rights;
100};
101
102struct create_room {
103        char *name;
104        int exchange;
105};
106
107struct chat_connection {
108        char *name;
109        char *show; /* AOL did something funny to us */
110        guint16 exchange;
111        guint16 instance;
112        int fd; /* this is redundant since we have the conn below */
113        aim_conn_t *conn;
114        int inpa;
115        int id;
116        struct gaim_connection *gc; /* i hate this. */
117        struct conversation *cnv; /* bah. */
118        int maxlen;
119        int maxvis;
120};
121
122struct ask_direct {
123        struct gaim_connection *gc;
124        char *sn;
125        char ip[64];
126        guint8 cookie[8];
127};
128
129struct icq_auth {
130        struct gaim_connection *gc;
131        guint32 uin;
132};
133
134static char *extract_name(const char *name) {
135        char *tmp;
136        int i, j;
137        char *x = strchr(name, '-');
138        if (!x) return NULL;
139        x = strchr(++x, '-');
140        if (!x) return NULL;
141        tmp = g_strdup(++x);
142
143        for (i = 0, j = 0; x[i]; i++) {
144                char hex[3];
145                if (x[i] != '%') {
146                        tmp[j++] = x[i];
147                        continue;
148                }
149                strncpy(hex, x + ++i, 2); hex[2] = 0;
150                i++;
151                tmp[j++] = (char)strtol(hex, NULL, 16);
152        }
153
154        tmp[j] = 0;
155        return tmp;
156}
157
158#if 0
159static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) {
160        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
161        struct chat_connection *c = NULL;
162
163        while (g) {
164                c = (struct chat_connection *)g->data;
165                if (c->id == id)
166                        break;
167                g = g->next;
168                c = NULL;
169        }
170
171        return c;
172}
173#endif
174
175static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc,
176                                                        aim_conn_t *conn) {
177        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
178        struct chat_connection *c = NULL;
179
180        while (g) {
181                c = (struct chat_connection *)g->data;
182                if (c->conn == conn)
183                        break;
184                g = g->next;
185                c = NULL;
186        }
187
188        return c;
189}
190
191static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
192static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
193static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
194static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
195static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
196static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
197static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
198static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
199static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
200static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
201static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
202static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
203static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
204static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
205static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
206static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
207static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
208static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
209static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
210static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
211static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
212static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
213static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
214static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
215static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
216static int gaim_memrequest       (aim_session_t *, aim_frame_t *, ...);
217static int gaim_selfinfo         (aim_session_t *, aim_frame_t *, ...);
218static int gaim_offlinemsg       (aim_session_t *, aim_frame_t *, ...);
219static int gaim_offlinemsgdone   (aim_session_t *, aim_frame_t *, ...);
220static int gaim_ssi_parserights  (aim_session_t *, aim_frame_t *, ...);
221static int gaim_ssi_parselist    (aim_session_t *, aim_frame_t *, ...);
222static int gaim_ssi_parseack     (aim_session_t *, aim_frame_t *, ...);
223
224static int gaim_icqinfo          (aim_session_t *, aim_frame_t *, ...);
225static int gaim_parseaiminfo     (aim_session_t *, aim_frame_t *, ...);
226
227static char *msgerrreason[] = {
228        "Invalid error",
229        "Invalid SNAC",
230        "Rate to host",
231        "Rate to client",
232        "Not logged in",
233        "Service unavailable",
234        "Service not defined",
235        "Obsolete SNAC",
236        "Not supported by host",
237        "Not supported by client",
238        "Refused by client",
239        "Reply too big",
240        "Responses lost",
241        "Request denied",
242        "Busted SNAC payload",
243        "Insufficient rights",
244        "In local permit/deny",
245        "Too evil (sender)",
246        "Too evil (receiver)",
247        "User temporarily unavailable",
248        "No match",
249        "List overflow",
250        "Request ambiguous",
251        "Queue full",
252        "Not while on AOL"
253};
254static int msgerrreasonlen = 25;
255
256static void oscar_callback(gpointer data, gint source,
257                                GaimInputCondition condition) {
258        aim_conn_t *conn = (aim_conn_t *)data;
259        aim_session_t *sess = aim_conn_getsess(conn);
260        struct gaim_connection *gc = sess ? sess->aux_data : NULL;
261        struct oscar_data *odata;
262
263        if (!gc) {
264                /* gc is null. we return, else we seg SIGSEG on next line. */
265                return;
266        }
267     
268        if (!g_slist_find(get_connections(), gc)) {
269                /* oh boy. this is probably bad. i guess the only thing we
270                 * can really do is return? */
271                return;
272        }
273
274        odata = (struct oscar_data *)gc->proto_data;
275
276        if (condition & GAIM_INPUT_READ) {
277                if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
278                        if (aim_handlerendconnect(odata->sess, conn) < 0) {
279                                aim_conn_kill(odata->sess, &conn);
280                        }
281                } else {
282                        if (aim_get_command(odata->sess, conn) >= 0) {
283                                aim_rxdispatch(odata->sess);
284                                if (odata->killme)
285                                        signoff(gc);
286                        } else {
287                                if ((conn->type == AIM_CONN_TYPE_BOS) ||
288                                           !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) {
289                                        hide_login_progress_error(gc, _("Disconnected."));
290                                        signoff(gc);
291                                } else if (conn->type == AIM_CONN_TYPE_CHAT) {
292                                        struct chat_connection *c = find_oscar_chat_by_conn(gc, conn);
293                                        char buf[BUF_LONG];
294                                        c->conn = NULL;
295                                        if (c->inpa > 0)
296                                                gaim_input_remove(c->inpa);
297                                        c->inpa = 0;
298                                        c->fd = -1;
299                                        aim_conn_kill(odata->sess, &conn);
300                                        sprintf(buf, _("You have been disconnected from chat room %s."), c->name);
301                                        do_error_dialog(sess->aux_data, buf, _("Chat Error!"));
302                                } else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
303                                        if (odata->cnpa > 0)
304                                                gaim_input_remove(odata->cnpa);
305                                        odata->cnpa = 0;
306                                        while (odata->create_rooms) {
307                                                struct create_room *cr = odata->create_rooms->data;
308                                                g_free(cr->name);
309                                                odata->create_rooms =
310                                                        g_slist_remove(odata->create_rooms, cr);
311                                                g_free(cr);
312                                                do_error_dialog(sess->aux_data, _("Chat is currently unavailable"),
313                                                                _("Gaim - Chat"));
314                                        }
315                                        aim_conn_kill(odata->sess, &conn);
316                                } else if (conn->type == AIM_CONN_TYPE_AUTH) {
317                                        if (odata->paspa > 0)
318                                                gaim_input_remove(odata->paspa);
319                                        odata->paspa = 0;
320                                        aim_conn_kill(odata->sess, &conn);
321                                } else {
322                                        aim_conn_kill(odata->sess, &conn);
323                                }
324                        }
325                }
326        }
327}
328
329static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond)
330{
331        struct gaim_connection *gc = data;
332        struct oscar_data *odata;
333        aim_session_t *sess;
334        aim_conn_t *conn;
335
336        if (!g_slist_find(get_connections(), gc)) {
337                closesocket(source);
338                return;
339        }
340
341        odata = gc->proto_data;
342        sess = odata->sess;
343        conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
344
345        if (source < 0) {
346                hide_login_progress(gc, _("Couldn't connect to host"));
347                signoff(gc);
348                return;
349        }
350
351        aim_conn_completeconnect(sess, conn);
352        gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ,
353                        oscar_callback, conn);
354}
355
356static void oscar_login(struct aim_user *user) {
357        aim_session_t *sess;
358        aim_conn_t *conn;
359        char buf[256];
360        struct gaim_connection *gc = new_gaim_conn(user);
361        struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1);
362
363        if (isdigit(*user->username)) {
364                odata->icq = TRUE;
365                /* this is odd but it's necessary for a proper do_import and do_export */
366                gc->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/*
1211int handle_cmp_aim(const char * a, const char * b) {
1212        return handle_cmp(a, b, PROTO_TOC);
1213}
1214*/
1215static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1216        int channel, ret = 0;
1217        aim_userinfo_t *userinfo;
1218        va_list ap;
1219
1220        va_start(ap, fr);
1221        channel = va_arg(ap, int);
1222        userinfo = va_arg(ap, aim_userinfo_t *);
1223
1224    if (set_getint(sess->aux_data, "debug")) {
1225        serv_got_crap(sess->aux_data, "channel %i called", channel);
1226    }
1227
1228        switch (channel) {
1229                case 1: { /* standard message */
1230                        struct aim_incomingim_ch1_args *args;
1231                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1232                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1233                } break;
1234
1235                case 2: { /* rendevous */
1236                        struct aim_incomingim_ch2_args *args;
1237                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1238                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1239                } break;
1240
1241                case 4: { /* ICQ */
1242                        struct aim_incomingim_ch4_args *args;
1243                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1244                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1245                } break;
1246
1247                default: {;
1248                } break;
1249        }
1250
1251        va_end(ap);
1252
1253        return ret;
1254}
1255
1256static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1257        va_list ap;
1258        guint16 chan, nummissed, reason;
1259        aim_userinfo_t *userinfo;
1260        char buf[1024];
1261
1262        va_start(ap, fr);
1263        chan = (guint16)va_arg(ap, unsigned int);
1264        userinfo = va_arg(ap, aim_userinfo_t *);
1265        nummissed = (guint16)va_arg(ap, unsigned int);
1266        reason = (guint16)va_arg(ap, unsigned int);
1267        va_end(ap);
1268
1269        switch(reason) {
1270                case 0:
1271                        /* Invalid (0) */
1272                        g_snprintf(buf,
1273                                   sizeof(buf),
1274                                   nummissed == 1 ? 
1275                                   _("You missed %d message from %s because it was invalid.") :
1276                                   _("You missed %d messages from %s because they were invalid."),
1277                                   nummissed,
1278                                   userinfo->sn);
1279                        break;
1280                case 1:
1281                        /* Message too large */
1282                        g_snprintf(buf,
1283                                   sizeof(buf),
1284                                   nummissed == 1 ?
1285                                   _("You missed %d message from %s because it was too large.") :
1286                                   _("You missed %d messages from %s because they were too large."),
1287                                   nummissed,
1288                                   userinfo->sn);
1289                        break;
1290                case 2:
1291                        /* Rate exceeded */
1292                        g_snprintf(buf,
1293                                   sizeof(buf),
1294                                   nummissed == 1 ? 
1295                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1296                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1297                                   nummissed,
1298                                   userinfo->sn);
1299                        break;
1300                case 3:
1301                        /* Evil Sender */
1302                        g_snprintf(buf,
1303                                   sizeof(buf),
1304                                   nummissed == 1 ?
1305                                   _("You missed %d message from %s because it was too evil.") : 
1306                                   _("You missed %d messages from %s because they are too evil."),
1307                                   nummissed,
1308                                   userinfo->sn);
1309                        break;
1310                case 4:
1311                        /* Evil Receiver */
1312                        g_snprintf(buf,
1313                                   sizeof(buf),
1314                                   nummissed == 1 ? 
1315                                   _("You missed %d message from %s because you are too evil.") :
1316                                   _("You missed %d messages from %s because you are too evil."),
1317                                   nummissed,
1318                                   userinfo->sn);
1319                        break;
1320                default:
1321                        g_snprintf(buf,
1322                                   sizeof(buf),
1323                                   nummissed == 1 ? 
1324                                   _("You missed %d message from %s for unknown reasons.") :
1325                                   _("You missed %d messages from %s for unknown reasons."),
1326                                   nummissed,
1327                                   userinfo->sn);
1328                        break;
1329        }
1330        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1331
1332        return 1;
1333}
1334
1335static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1336        va_list ap;
1337        guint16 reason;
1338        char *m;
1339
1340        va_start(ap, fr);
1341        reason = (guint16)va_arg(ap, unsigned int);
1342        va_end(ap);
1343
1344        m = g_strdup_printf(_("SNAC threw error: %s\n"),
1345                        reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1346        do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error"));
1347        g_free(m);
1348
1349        return 1;
1350}
1351
1352static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1353        va_list ap;
1354        char *destn;
1355        guint16 reason;
1356        char buf[1024];
1357
1358        va_start(ap, fr);
1359        reason = (guint16)va_arg(ap, unsigned int);
1360        destn = va_arg(ap, char *);
1361        va_end(ap);
1362
1363        sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
1364                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1365        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1366
1367        return 1;
1368}
1369
1370static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1371        va_list ap;
1372        char *destn;
1373        guint16 reason;
1374        char buf[1024];
1375
1376        va_start(ap, fr);
1377        reason = (guint16)va_arg(ap, unsigned int);
1378        destn = va_arg(ap, char *);
1379        va_end(ap);
1380
1381        sprintf(buf, _("User information for %s unavailable: %s"), destn,
1382                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1383        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1384
1385
1386        return 1;
1387}
1388
1389static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1390        char *msg;
1391        guint16 id;
1392        va_list ap;
1393
1394        va_start(ap, fr);
1395        id  = (guint16)va_arg(ap, unsigned int);
1396        msg = va_arg(ap, char *);
1397        va_end(ap);
1398
1399        if (id < 4)
1400                do_error_dialog(sess->aux_data, _("Your connection may be lost."),
1401                                _("AOL error"));
1402
1403        return 1;
1404}
1405
1406static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1407        va_list ap;
1408        guint16 type;
1409        struct gaim_connection *gc = sess->aux_data;
1410        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1411
1412        va_start(ap, fr);
1413        type = (guint16)va_arg(ap, unsigned int);
1414
1415        switch(type) {
1416                case 0x0002: {
1417                        guint8 maxrooms;
1418                        struct aim_chat_exchangeinfo *exchanges;
1419                        int exchangecount; // i;
1420
1421                        maxrooms = (guint8)va_arg(ap, unsigned int);
1422                        exchangecount = va_arg(ap, int);
1423                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1424                        va_end(ap);
1425
1426                        while (odata->create_rooms) {
1427                                struct create_room *cr = odata->create_rooms->data;
1428                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1429                                g_free(cr->name);
1430                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1431                                g_free(cr);
1432                        }
1433                        }
1434                        break;
1435                case 0x0008: {
1436                        char *fqcn, *name, *ck;
1437                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1438                        guint8 createperms;
1439                        guint32 createtime;
1440
1441                        fqcn = va_arg(ap, char *);
1442                        instance = (guint16)va_arg(ap, unsigned int);
1443                        exchange = (guint16)va_arg(ap, unsigned int);
1444                        flags = (guint16)va_arg(ap, unsigned int);
1445                        createtime = va_arg(ap, guint32);
1446                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1447                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1448                        createperms = (guint8)va_arg(ap, int);
1449                        unknown = (guint16)va_arg(ap, unsigned int);
1450                        name = va_arg(ap, char *);
1451                        ck = va_arg(ap, char *);
1452                        va_end(ap);
1453
1454                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1455                        }
1456                        break;
1457                default:
1458                        va_end(ap);
1459                        break;
1460        }
1461        return 1;
1462}
1463
1464static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1465        va_list ap;
1466        int count, i;
1467        aim_userinfo_t *info;
1468        struct gaim_connection *g = sess->aux_data;
1469
1470        struct chat_connection *c = NULL;
1471
1472        va_start(ap, fr);
1473        count = va_arg(ap, int);
1474        info  = va_arg(ap, aim_userinfo_t *);
1475        va_end(ap);
1476
1477        c = find_oscar_chat_by_conn(g, fr->conn);
1478        if (!c)
1479                return 1;
1480
1481        for (i = 0; i < count; i++)
1482                add_chat_buddy(c->cnv, info[i].sn);
1483
1484        return 1;
1485}
1486
1487static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1488        va_list ap;
1489        int count, i;
1490        aim_userinfo_t *info;
1491        struct gaim_connection *g = sess->aux_data;
1492
1493        struct chat_connection *c = NULL;
1494
1495        va_start(ap, fr);
1496        count = va_arg(ap, int);
1497        info  = va_arg(ap, aim_userinfo_t *);
1498        va_end(ap);
1499
1500        c = find_oscar_chat_by_conn(g, fr->conn);
1501        if (!c)
1502                return 1;
1503
1504        for (i = 0; i < count; i++)
1505                remove_chat_buddy(c->cnv, info[i].sn, NULL);
1506
1507        return 1;
1508}
1509
1510static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1511        va_list ap;
1512        aim_userinfo_t *userinfo;
1513        struct aim_chat_roominfo *roominfo;
1514        char *roomname;
1515        int usercount;
1516        char *roomdesc;
1517        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1518        guint32 creationtime;
1519        struct gaim_connection *gc = sess->aux_data;
1520        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1521
1522        va_start(ap, fr);
1523        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1524        roomname = va_arg(ap, char *);
1525        usercount= va_arg(ap, int);
1526        userinfo = va_arg(ap, aim_userinfo_t *);
1527        roomdesc = va_arg(ap, char *);
1528        unknown_c9 = (guint16)va_arg(ap, int);
1529        creationtime = (guint32)va_arg(ap, unsigned long);
1530        maxmsglen = (guint16)va_arg(ap, int);
1531        unknown_d2 = (guint16)va_arg(ap, int);
1532        unknown_d5 = (guint16)va_arg(ap, int);
1533        maxvisiblemsglen = (guint16)va_arg(ap, int);
1534        va_end(ap);
1535
1536        ccon->maxlen = maxmsglen;
1537        ccon->maxvis = maxvisiblemsglen;
1538
1539        return 1;
1540}
1541
1542static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1543        va_list ap;
1544        aim_userinfo_t *info;
1545        char *msg;
1546        struct gaim_connection *gc = sess->aux_data;
1547        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1548        char *tmp;
1549
1550        va_start(ap, fr);
1551        info = va_arg(ap, aim_userinfo_t *);
1552        msg  = va_arg(ap, char *);
1553
1554        tmp = g_malloc(BUF_LONG);
1555        g_snprintf(tmp, BUF_LONG, "%s", msg);
1556        serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL));
1557        g_free(tmp);
1558
1559        return 1;
1560}
1561
1562static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1563#if 0
1564        static const char *codes[5] = {
1565                "invalid",
1566                 "change",
1567                 "warning",
1568                 "limit",
1569                 "limit cleared",
1570        };
1571#endif
1572        va_list ap;
1573        guint16 code, rateclass;
1574        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1575
1576        va_start(ap, fr); 
1577        code = (guint16)va_arg(ap, unsigned int);
1578        rateclass= (guint16)va_arg(ap, unsigned int);
1579        windowsize = (guint32)va_arg(ap, unsigned long);
1580        clear = (guint32)va_arg(ap, unsigned long);
1581        alert = (guint32)va_arg(ap, unsigned long);
1582        limit = (guint32)va_arg(ap, unsigned long);
1583        disconnect = (guint32)va_arg(ap, unsigned long);
1584        currentavg = (guint32)va_arg(ap, unsigned long);
1585        maxavg = (guint32)va_arg(ap, unsigned long);
1586        va_end(ap);
1587
1588        /* XXX fix these values */
1589        if (code == AIM_RATE_CODE_CHANGE) {
1590                if (currentavg >= clear)
1591                        aim_conn_setlatency(fr->conn, 0);
1592        } else if (code == AIM_RATE_CODE_WARNING) {
1593                aim_conn_setlatency(fr->conn, windowsize/4);
1594        } else if (code == AIM_RATE_CODE_LIMIT) {
1595                do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1596                                  "Please wait 10 seconds and try again."), _("Gaim - Error"));
1597                aim_conn_setlatency(fr->conn, windowsize/2);
1598        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1599                aim_conn_setlatency(fr->conn, 0);
1600        }
1601
1602        return 1;
1603}
1604
1605static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1606        va_list ap;
1607        aim_userinfo_t *info;
1608        struct gaim_connection *gc = sess->aux_data;
1609
1610        va_start(ap, fr);
1611        info = va_arg(ap, aim_userinfo_t *);
1612        va_end(ap);
1613
1614        gc->evil = info->warnlevel/10;
1615        /* gc->correction_time = (info->onlinesince - gc->login_time); */
1616
1617        return 1;
1618}
1619
1620static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1621
1622        aim_reqpersonalinfo(sess, fr->conn);
1623        aim_bos_reqlocaterights(sess, fr->conn);
1624        aim_bos_reqbuddyrights(sess, fr->conn);
1625
1626        aim_reqicbmparams(sess);
1627
1628        aim_bos_reqrights(sess, fr->conn);
1629        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1630        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1631                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1632
1633        return 1;
1634}
1635
1636static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1637        struct gaim_connection *gc = sess->aux_data;
1638        struct oscar_data *od = gc->proto_data;
1639
1640        aim_clientready(sess, fr->conn);
1641
1642        if (od->chpass) {
1643                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1644                g_free(od->oldp);
1645                od->oldp = NULL;
1646                g_free(od->newp);
1647                od->newp = NULL;
1648                od->chpass = FALSE;
1649        }
1650        if (od->setnick) {
1651                aim_admin_setnick(sess, fr->conn, od->newsn);
1652                g_free(od->newsn);
1653                od->newsn = NULL;
1654                od->setnick = FALSE;
1655        }
1656        if (od->conf) {
1657                aim_admin_reqconfirm(sess, fr->conn);
1658                od->conf = FALSE;
1659        }
1660        if (od->reqemail) {
1661                aim_admin_getinfo(sess, fr->conn, 0x0011);
1662                od->reqemail = FALSE;
1663        }
1664        if (od->setemail) {
1665                aim_admin_setemail(sess, fr->conn, od->email);
1666                g_free(od->email);
1667                od->setemail = FALSE;
1668        }
1669
1670        return 1;
1671}
1672
1673static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1674        struct aim_icbmparameters *params;
1675        va_list ap;
1676
1677        va_start(ap, fr);
1678        params = va_arg(ap, struct aim_icbmparameters *);
1679        va_end(ap);
1680
1681        /* Maybe senderwarn and recverwarn should be user preferences... */
1682        params->maxmsglen = 8000;
1683        params->minmsginterval = 0;
1684
1685        aim_seticbmparam(sess, params);
1686
1687        return 1;
1688}
1689
1690static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1691{
1692        va_list ap;
1693        guint16 maxsiglen;
1694        struct gaim_connection *gc = sess->aux_data;
1695        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1696
1697        va_start(ap, fr);
1698        maxsiglen = va_arg(ap, int);
1699        va_end(ap);
1700
1701        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1702
1703        aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps);
1704
1705        return 1;
1706}
1707
1708static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1709        va_list ap;
1710        guint16 maxbuddies, maxwatchers;
1711        struct gaim_connection *gc = sess->aux_data;
1712        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1713
1714        va_start(ap, fr);
1715        maxbuddies = (guint16)va_arg(ap, unsigned int);
1716        maxwatchers = (guint16)va_arg(ap, unsigned int);
1717        va_end(ap);
1718
1719        odata->rights.maxbuddies = (guint)maxbuddies;
1720        odata->rights.maxwatchers = (guint)maxwatchers;
1721
1722        return 1;
1723}
1724
1725static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1726        guint16 maxpermits, maxdenies;
1727        va_list ap;
1728        struct gaim_connection *gc = sess->aux_data;
1729        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1730
1731        va_start(ap, fr);
1732        maxpermits = (guint16)va_arg(ap, unsigned int);
1733        maxdenies = (guint16)va_arg(ap, unsigned int);
1734        va_end(ap);
1735
1736        odata->rights.maxpermits = (guint)maxpermits;
1737        odata->rights.maxdenies = (guint)maxdenies;
1738
1739//      serv_finish_login(gc);
1740
1741        if (bud_list_cache_exists(gc))
1742                do_import(gc, NULL);
1743
1744        aim_clientready(sess, fr->conn);
1745
1746        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1747
1748        aim_ssi_reqrights(sess, fr->conn);
1749        aim_ssi_reqalldata(sess, fr->conn);
1750
1751        return 1;
1752}
1753
1754static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1755        va_list ap;
1756        struct aim_icq_offlinemsg *msg;
1757        struct gaim_connection *gc = sess->aux_data;
1758
1759        va_start(ap, fr);
1760        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1761        va_end(ap);
1762
1763        switch (msg->type) {
1764                case 0x0001: { /* Basic offline message */
1765                        char sender[32];
1766                        char *dialog_msg = g_strdup(msg->msg);
1767                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1768                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1769                        strip_linefeed(dialog_msg);
1770                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1771                        g_free(dialog_msg);
1772                } break;
1773
1774                case 0x0004: { /* Someone sent you a URL */
1775                        char sender[32];
1776                        char *dialog_msg;
1777                        char **m;
1778
1779                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1780                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1781
1782                        m = g_strsplit(msg->msg, "\376", 2);
1783
1784                        if ((strlen(m[0]) != 0)) {
1785                          dialog_msg = g_strjoinv(" -- ", m);
1786                        } else {
1787                          dialog_msg = m[1];
1788                        }
1789
1790                        strip_linefeed(dialog_msg);
1791                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1792                        g_free(dialog_msg);
1793                        g_free(m);
1794                } break;
1795               
1796                case 0x0006: { /* Authorization request */
1797                        gaim_icq_authask(gc, msg->sender, msg->msg);
1798                } break;
1799
1800                case 0x0007: { /* Someone has denied you authorization */
1801                        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.") );
1802                } break;
1803
1804                case 0x0008: { /* Someone has granted you authorization */
1805                        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.") );
1806                } break;
1807
1808                case 0x0012: {
1809                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1810                } break;
1811
1812                default: {;
1813                }
1814        }
1815
1816        return 1;
1817}
1818
1819static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1820{
1821        aim_icq_ackofflinemsgs(sess);
1822        return 1;
1823}
1824
1825static void oscar_keepalive(struct gaim_connection *gc) {
1826        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1827        aim_flap_nop(odata->sess, odata->conn);
1828}
1829
1830static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) {
1831        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1832        int ret = 0;
1833        if (imflags & IM_FLAG_AWAY) {
1834                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1835        } else {
1836                struct aim_sendimext_args args;
1837                char *s;
1838               
1839                args.flags = AIM_IMFLAGS_ACK;
1840                if (odata->icq)
1841                        args.flags |= AIM_IMFLAGS_OFFLINE;
1842                for (s = message; *s; s++)
1843                        if (*s & 128)
1844                                break;
1845               
1846                /* Message contains high ASCII chars, time for some translation! */
1847                if (*s) {
1848                        s = g_malloc(BUF_LONG);
1849                        /* Try if we can put it in an ISO8859-1 string first.
1850                           If we can't, fall back to UTF16. */
1851                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1852                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1853                                len = ret;
1854                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1855                                args.flags |= AIM_IMFLAGS_UNICODE;
1856                                len = ret;
1857                        } else {
1858                                /* OOF, translation failed... Oh well.. */
1859                                g_free( s );
1860                                s = message;
1861                        }
1862                } else {
1863                        s = message;
1864                }
1865               
1866                args.features = gaim_features;
1867                args.featureslen = sizeof(gaim_features);
1868               
1869                args.destsn = name;
1870                args.msg    = s;
1871                args.msglen = len;
1872               
1873                ret = aim_send_im_ext(odata->sess, &args);
1874               
1875                if (s != message) {
1876                        g_free(s);
1877                }
1878        }
1879        if (ret >= 0)
1880                return 1;
1881        return ret;
1882}
1883
1884static void oscar_get_info(struct gaim_connection *g, char *name) {
1885        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1886        if (odata->icq)
1887                aim_icq_getallinfo(odata->sess, name);
1888        else {
1889                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1890                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1891        }
1892}
1893
1894static void oscar_get_away(struct gaim_connection *g, char *who) {
1895        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1896        if (odata->icq) {
1897                struct buddy *budlight = find_buddy(g, who);
1898                if (budlight)
1899                        if ((budlight->uc & 0xff80) >> 7)
1900                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1901                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1902        } else
1903                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1904}
1905
1906static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1907{
1908
1909        if (!g_strcasecmp(state, _("Visible"))) {
1910                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1911                return;
1912        } else if (!g_strcasecmp(state, _("Invisible"))) {
1913                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1914                return;
1915        } /* else... */
1916
1917        if (od->rights.maxawaymsglen == 0)
1918                do_error_dialog(gc, "oscar_set_away_aim called before locate rights received", "Protocol Error");
1919
1920        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1921
1922        if (gc->away)
1923                g_free(gc->away);
1924        gc->away = NULL;
1925
1926        if (!message) {
1927                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1928                return;
1929        }
1930
1931        if (strlen(message) > od->rights.maxawaymsglen) {
1932                gchar *errstr;
1933
1934                errstr = g_strdup_printf("Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
1935
1936                do_error_dialog(gc, errstr, "Away Message Too Long");
1937
1938                g_free(errstr);
1939        }
1940
1941        gc->away = g_strndup(message, od->rights.maxawaymsglen);
1942        aim_bos_setprofile(od->sess, od->conn, NULL, gc->away, gaim_caps);
1943
1944        return;
1945}
1946
1947static void oscar_set_away_icq(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1948{
1949    const char *msg = NULL;
1950        gboolean no_message = FALSE;
1951
1952        /* clean old states */
1953    if (gc->away) {
1954                g_free(gc->away);
1955                gc->away = NULL;
1956    }
1957        od->sess->aim_icq_state = 0;
1958
1959        /* if no message, then use an empty message */
1960    if (message) {
1961        msg = message;
1962    } else {
1963        msg = "";
1964                no_message = TRUE;
1965    }
1966
1967        if (!g_strcasecmp(state, "Online")) {
1968                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1969        } else if (!g_strcasecmp(state, "Away")) {
1970                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1971        gc->away = g_strdup(msg);
1972                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1973        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1974                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
1975        gc->away = g_strdup(msg);
1976                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1977        } else if (!g_strcasecmp(state, "Not Available")) {
1978                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
1979        gc->away = g_strdup(msg);
1980                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1981        } else if (!g_strcasecmp(state, "Occupied")) {
1982                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
1983        gc->away = g_strdup(msg);
1984                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1985        } else if (!g_strcasecmp(state, "Free For Chat")) {
1986                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
1987        gc->away = g_strdup(msg);
1988                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1989        } else if (!g_strcasecmp(state, "Invisible")) {
1990                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1991        gc->away = g_strdup(msg);
1992        } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
1993                if (no_message) {
1994                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1995                } else {
1996                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1997            gc->away = g_strdup(msg);
1998                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1999                }
2000        }
2001
2002        return;
2003}
2004
2005static void oscar_set_away(struct gaim_connection *gc, char *state, char *message)
2006{
2007        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2008
2009    oscar_set_away_aim(gc, od, state, message);
2010        if (od->icq)
2011                oscar_set_away_icq(gc, od, state, message);
2012
2013        return;
2014}
2015
2016static void oscar_add_buddy(struct gaim_connection *g, char *name) {
2017        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2018        aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
2019}
2020
2021static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) {
2022        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2023        struct aim_ssi_item *ssigroup;
2024        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
2025}
2026
2027static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
2028        return 1;
2029}
2030
2031static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
2032        struct gaim_connection *gc = sess->aux_data;
2033        struct aim_ssi_item *curitem;
2034        int tmp;
2035
2036        /* Add from server list to local list */
2037        tmp = 0;
2038        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
2039                switch (curitem->type) {
2040                        case 0x0000: /* Buddy */
2041                                if ((curitem->name) && (!find_buddy(gc, curitem->name))) {
2042                                        char *realname = NULL;
2043
2044                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
2045                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
2046                                               
2047                                        add_buddy(gc, NULL, curitem->name, realname);
2048                                       
2049                                        if (realname)
2050                                            g_free(realname);
2051                                }
2052                                break;
2053
2054                        case 0x0002: /* Permit buddy */
2055                                if (curitem->name) {
2056                                        GSList *list;
2057                                        for (list=gc->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2058                                        if (!list) {
2059                                                char *name;
2060                                                name = g_strdup(normalize(curitem->name));
2061                                                gc->permit = g_slist_append(gc->permit, name);
2062                                                build_allow_list();
2063                                                tmp++;
2064                                        }
2065                                }
2066                                break;
2067
2068                        case 0x0003: /* Deny buddy */
2069                                if (curitem->name) {
2070                                        GSList *list;
2071                                        for (list=gc->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2072                                        if (!list) {
2073                                                char *name;
2074                                                name = g_strdup(normalize(curitem->name));
2075                                                gc->deny = g_slist_append(gc->deny, name);
2076                                                build_block_list();
2077                                                tmp++;
2078                                        }
2079                                }
2080                                break;
2081
2082                        case 0x0004: /* Permit/deny setting */
2083                                if (curitem->data) {
2084                                        guint8 permdeny;
2085                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != gc->permdeny)) {
2086                                                gc->permdeny = permdeny;
2087                                                tmp++;
2088                                        }
2089                                }
2090                                break;
2091
2092                        case 0x0005: /* Presence setting */
2093                                /* We don't want to change Gaim's setting because it applies to all accounts */
2094                                break;
2095                } /* End of switch on curitem->type */
2096        } /* End of for loop */
2097
2098        if (tmp)
2099                do_export(gc);
2100        aim_ssi_enable(sess, fr->conn);
2101       
2102        /* Request offline messages, now that the buddy list is complete. */
2103        aim_icq_reqofflinemsgs(sess);
2104       
2105        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
2106        account_online(gc);
2107       
2108        return 1;
2109}
2110
2111static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2112{
2113        aim_snac_t *origsnac;
2114        va_list ap;
2115
2116        va_start( ap, fr );
2117        origsnac = va_arg( ap, aim_snac_t * );
2118        va_end( ap );
2119       
2120        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2121        {
2122                int i, st, count = aim_bstream_empty( &fr->data );
2123                char *list;
2124               
2125                if( count & 1 )
2126                {
2127                        /* Hmm, the length should be even... */
2128                        do_error_dialog( sess->aux_data, "Received SSI ACK package with non-even length", "Gaim - Error" );
2129                        return( 0 );
2130                }
2131                count >>= 1;
2132               
2133                list = (char *) origsnac->data;
2134                for( i = 0; i < count; i ++ )
2135                {
2136                        st = aimbs_get16( &fr->data );
2137                        if( st == 0x0E )
2138                        {
2139                                serv_got_crap( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
2140                               
2141                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2142                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2143                        }
2144                        list += strlen( list ) + 1;
2145                }
2146        }
2147       
2148        return( 1 );
2149}
2150
2151static void oscar_set_permit_deny(struct gaim_connection *gc) {
2152        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2153        if (od->icq) {
2154                GSList *list;
2155                char buf[MAXMSGLEN];
2156                int at;
2157
2158                switch(gc->permdeny) {
2159                case 1:
2160                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gc->username);
2161                        break;
2162                case 2:
2163                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gc->username);
2164                        break;
2165                case 3:
2166                        list = gc->permit;
2167                        at = 0;
2168                        while (list) {
2169                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2170                                list = list->next;
2171                        }
2172                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2173                        break;
2174                case 4:
2175                        list = gc->deny;
2176                        at = 0;
2177                        while (list) {
2178                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2179                                list = list->next;
2180                        }
2181                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2182                        break;
2183                        default:
2184                        break;
2185                }
2186                signoff_blocked(gc);
2187        } else {
2188                if (od->sess->ssi.received_data)
2189                        aim_ssi_setpermdeny(od->sess, od->conn, gc->permdeny, 0xffffffff);
2190        }
2191}
2192
2193static void oscar_add_permit(struct gaim_connection *gc, char *who) {
2194        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2195        if (od->icq) {
2196                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2197        } else {
2198                if (od->sess->ssi.received_data)
2199                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2200        }
2201}
2202
2203static void oscar_add_deny(struct gaim_connection *gc, char *who) {
2204        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2205        if (od->icq) {
2206                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2207        } else {
2208                if (od->sess->ssi.received_data)
2209                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2210        }
2211}
2212
2213static void oscar_rem_permit(struct gaim_connection *gc, char *who) {
2214        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2215        if (!od->icq) {
2216                if (od->sess->ssi.received_data)
2217                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2218        }
2219}
2220
2221static void oscar_rem_deny(struct gaim_connection *gc, char *who) {
2222        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2223        if (!od->icq) {
2224                if (od->sess->ssi.received_data)
2225                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2226        }
2227}
2228
2229static GList *oscar_away_states(struct gaim_connection *gc)
2230{
2231        struct oscar_data *od = gc->proto_data;
2232        GList *m = NULL;
2233
2234        if (!od->icq)
2235                return g_list_append(m, GAIM_AWAY_CUSTOM);
2236
2237        m = g_list_append(m, "Online");
2238        m = g_list_append(m, "Away");
2239        m = g_list_append(m, "Do Not Disturb");
2240        m = g_list_append(m, "Not Available");
2241        m = g_list_append(m, "Occupied");
2242        m = g_list_append(m, "Free For Chat");
2243        m = g_list_append(m, "Invisible");
2244
2245        return m;
2246}
2247
2248static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2249{
2250        struct gaim_connection *gc = sess->aux_data;
2251        gchar who[16];
2252        GString *str;
2253        va_list ap;
2254        struct aim_icq_info *info;
2255
2256        va_start(ap, fr);
2257        info = va_arg(ap, struct aim_icq_info *);
2258        va_end(ap);
2259
2260        if (!info->uin)
2261                return 0;
2262
2263        str = g_string_sized_new(100);
2264        g_snprintf(who, sizeof(who), "%u", info->uin);
2265
2266        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2267                                info->nick ? info->nick : "-");
2268        info_string_append(str, "\n", _("First Name"), info->first);
2269        info_string_append(str, "\n", _("Last Name"), info->last);
2270                info_string_append(str, "\n", _("Email Address"), info->email);
2271        if (info->numaddresses && info->email2) {
2272                int i;
2273                for (i = 0; i < info->numaddresses; i++) {
2274                                        info_string_append(str, "\n", _("Email Address"), info->email2[i]);
2275                }
2276        }
2277        info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
2278        info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
2279        if (info->birthyear || info->birthmonth || info->birthday) {
2280                char date[30];
2281                struct tm tm;
2282                tm.tm_mday = (int)info->birthday;
2283                tm.tm_mon = (int)info->birthmonth-1;
2284                tm.tm_year = (int)info->birthyear-1900;
2285                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2286                info_string_append(str, "\n", _("Birthday"), date);
2287        }
2288        if (info->age) {
2289                char age[5];
2290                g_snprintf(age, sizeof(age), "%hhd", info->age);
2291                info_string_append(str, "\n", _("Age"), age);
2292        }
2293                info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
2294        if (info->info && info->info[0]) {
2295                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2296                                                info->info, _("End of Additional Information"));
2297        }
2298        g_string_sprintfa(str, "\n");
2299        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2300                g_string_sprintfa(str, "%s:", _("Home Address"));
2301                info_string_append(str, "\n", _("Address"), info->homeaddr);
2302                info_string_append(str, "\n", _("City"), info->homecity);
2303                info_string_append(str, "\n", _("State"), info->homestate); 
2304                                info_string_append(str, "\n", _("Zip Code"), info->homezip);
2305                g_string_sprintfa(str, "\n");
2306        }
2307        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2308                g_string_sprintfa(str, "%s:", _("Work Address"));
2309                info_string_append(str, "\n", _("Address"), info->workaddr);
2310                info_string_append(str, "\n", _("City"), info->workcity);
2311                info_string_append(str, "\n", _("State"), info->workstate);
2312                                info_string_append(str, "\n", _("Zip Code"), info->workzip);
2313                g_string_sprintfa(str, "\n");
2314        }
2315        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2316                g_string_sprintfa(str, "%s:", _("Work Information"));
2317                info_string_append(str, "\n", _("Company"), info->workcompany);
2318                info_string_append(str, "\n", _("Division"), info->workdivision);
2319                info_string_append(str, "\n", _("Position"), info->workposition);
2320                if (info->workwebpage && info->workwebpage[0]) {
2321                        info_string_append(str, "\n", _("Web Page"), info->workwebpage);
2322                }
2323                g_string_sprintfa(str, "\n");
2324        }
2325
2326                serv_got_crap(gc, "%s\n%s", _("User Info"), str->str);
2327        g_string_free(str, TRUE);
2328
2329        return 1;
2330
2331}
2332
2333static char *oscar_encoding_extract(const char *encoding)
2334{
2335        char *ret = NULL;
2336        char *begin, *end;
2337
2338        g_return_val_if_fail(encoding != NULL, NULL);
2339
2340        /* Make sure encoding begins with charset= */
2341        if (strncmp(encoding, "text/plain; charset=", 20) &&
2342                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2343                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2344        {
2345                return NULL;
2346        }
2347
2348        begin = strchr(encoding, '"');
2349        end = strrchr(encoding, '"');
2350
2351        if ((begin == NULL) || (end == NULL) || (begin >= end))
2352                return NULL;
2353
2354        ret = g_strndup(begin+1, (end-1) - begin);
2355
2356        return ret;
2357}
2358
2359static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2360{
2361        char *utf8 = g_new0(char, 8192);
2362
2363        if ((encoding == NULL) || encoding[0] == '\0') {
2364                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2365        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2366                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2367        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2368                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2369        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2370                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2371        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2372                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2373                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2374                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2375        }
2376
2377        /*
2378         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2379         * we have been unable to convert the text to utf-8 from the encoding
2380         * that was specified.  So we assume it's UTF-8 and hope for the best.
2381         */
2382        if (*utf8 == 0) {
2383            strncpy(utf8, text, textlen);
2384        }
2385
2386        return utf8;
2387}
2388
2389static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2390{
2391        struct gaim_connection *gc = sess->aux_data;
2392        va_list ap;
2393        aim_userinfo_t *userinfo;
2394        guint16 infotype;
2395        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2396        guint16 text_length;
2397        char *utf8 = NULL;
2398
2399        va_start(ap, fr);
2400        userinfo = va_arg(ap, aim_userinfo_t *);
2401        infotype = va_arg(ap, int);
2402        text_encoding = va_arg(ap, char*);
2403        text = va_arg(ap, char*);
2404        text_length = va_arg(ap, int);
2405        va_end(ap);
2406
2407        if(text_encoding)
2408                extracted_encoding = oscar_encoding_extract(text_encoding);
2409        if(infotype == AIM_GETINFO_GENERALINFO) {
2410                /*Display idle time*/
2411                char buff[256];
2412                struct tm idletime;
2413                if(userinfo->idletime) {
2414                        memset(&idletime, 0, sizeof(struct tm));
2415                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2416                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2417                        idletime.tm_min = userinfo->idletime % 60;
2418                        idletime.tm_sec = 0;
2419                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
2420                        serv_got_crap(gc, "%s: %s", _("Idle Time"), buff);
2421                }
2422               
2423                if(text) {
2424                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2425                        serv_got_crap(gc, "%s\n%s", _("User Info"), utf8);
2426                } else {
2427                        serv_got_crap(gc, _("No user info available."));
2428                }
2429        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2430                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2431                serv_got_crap(gc, "%s\n%s", _("Away Message"), utf8);
2432        }
2433
2434        g_free(utf8);
2435   
2436        return 1;
2437}
2438
2439static char *oscar_get_status_string( struct gaim_connection *gc, int number )
2440{
2441        struct oscar_data *od = gc->proto_data;
2442       
2443        if( ! number & UC_UNAVAILABLE )
2444        {
2445                return( NULL );
2446        }
2447        else if( od->icq )
2448        {
2449                number >>= 7;
2450                if( number & AIM_ICQ_STATE_DND )
2451                        return( "Do Not Disturb" );
2452                else if( number & AIM_ICQ_STATE_OUT )
2453                        return( "Not Available" );
2454                else if( number & AIM_ICQ_STATE_BUSY )
2455                        return( "Occupied" );
2456                else if( number & AIM_ICQ_STATE_INVISIBLE )
2457                        return( "Invisible" );
2458                else
2459                        return( "Away" );
2460        }
2461        else
2462        {
2463                return( "Away" );
2464        }
2465}
2466
2467void oscar_init() 
2468{
2469        struct prpl *ret = g_new0(struct prpl, 1);
2470        ret->name = "oscar";
2471        ret->away_states = oscar_away_states;
2472        ret->login = oscar_login;
2473        ret->close = oscar_close;
2474        ret->send_im = oscar_send_im;
2475        ret->get_info = oscar_get_info;
2476        ret->set_away = oscar_set_away;
2477        ret->get_away = oscar_get_away;
2478        ret->add_buddy = oscar_add_buddy;
2479        ret->remove_buddy = oscar_remove_buddy;
2480        ret->add_permit = oscar_add_permit;
2481        ret->add_deny = oscar_add_deny;
2482        ret->rem_permit = oscar_rem_permit;
2483        ret->rem_deny = oscar_rem_deny;
2484        ret->set_permit_deny = oscar_set_permit_deny;
2485        ret->keepalive = oscar_keepalive;
2486        ret->get_status_string = oscar_get_status_string;
2487
2488        register_protocol(ret);
2489}
Note: See TracBrowser for help on using the repository browser.