source: protocols/oscar/oscar.c @ 5100caa

Last change on this file since 5100caa was 0a3c243, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-06-30T23:18:56Z

Got rid of struct aim_user (now using account_t everywhere). Needs some more
testing though.

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