source: protocols/oscar/oscar.c @ 96863f6

Last change on this file since 96863f6 was 96863f6, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-07-02T09:49:31Z

Added protocol-specific settings, made the server setting specific to only
OSCAR and Jabber.

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