source: protocols/oscar/oscar.c @ 92ad3d4

Last change on this file since 92ad3d4 was e4d6271, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-27T14:39:32Z

IPv6 socket improvements. Daemon mode can now also listen on IPv6 sockets.
Also, when reverse lookup fails, BitlBee now correctly falls back to an
ASCII-formatted IP instead of "localhost.".

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