source: protocols/oscar/oscar.c @ 4eb4c0f

Last change on this file since 4eb4c0f was 4eb4c0f, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-16T17:15:31Z

merge in upstream changes

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