source: protocols/oscar/oscar.c @ 22bf64e

Last change on this file since 22bf64e was 22bf64e, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-13T23:27:58Z

Improved typing notification support. (from f0rked)

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