source: protocols/oscar/oscar.c @ 73cf7fd

Last change on this file since 73cf7fd was 73cf7fd, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-05-22T09:11:49Z

Trying to fix charset issues with outgoing AIM chat messages.

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