source: protocols/oscar/oscar.c @ 8961950

Last change on this file since 8961950 was 5a71d9c, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-09T17:58:13Z
  • add support for setting ops/voice according to OTR msgstate
  • add 'otr trust' user command
  • support non-otr messages during keygen
  • run otr messages through strip_html
  • interpret <b> and <i> tags in html messages
  • record max message size in prpl
  • add 'encrypted' flag to user_t
  • cosmetics
  • 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                int i;
1069               
1070                *tmp = 0;
1071                for (i = 0; i < args->mpmsg.numparts; i ++) {
1072                        g_strlcat(tmp, (char*) args->mpmsg.parts[i].data, BUF_LONG);
1073                        g_strlcat(tmp, "\n", BUF_LONG);
1074                }
1075        }
1076       
1077        strip_linefeed(tmp);
1078        imcb_buddy_msg(ic, userinfo->sn, tmp, flags, 0);
1079        g_free(tmp);
1080       
1081        return 1;
1082}
1083
1084void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv);
1085void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv);
1086       
1087static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
1088        struct im_connection *ic = sess->aux_data;
1089
1090        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1091                return 1;
1092
1093        if (args->reqclass & AIM_CAPS_CHAT) {
1094                char *name = extract_name(args->info.chat.roominfo.name);
1095                int *exch = g_new0(int, 1);
1096                GList *m = NULL;
1097                char txt[1024];
1098                struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1);
1099
1100                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1101                *exch = args->info.chat.roominfo.exchange;
1102                m = g_list_append(m, exch);
1103
1104                g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg );
1105
1106                inv->ic = ic;
1107                inv->exchange = *exch;
1108                inv->name = g_strdup(name);
1109               
1110                imcb_ask( ic, txt, inv, oscar_accept_chat, oscar_reject_chat);
1111       
1112                if (name)
1113                        g_free(name);
1114        }
1115
1116        return 1;
1117}
1118
1119static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
1120        char *uin, message;
1121        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
1122       
1123        uin = g_strdup_printf("%u", data->uin);
1124        message = 0;
1125        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1126        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
1127        if(imcb_find_buddy(data->ic, uin) == NULL)
1128                imcb_ask_add(data->ic, uin, NULL);
1129       
1130        g_free(uin);
1131        g_free(data);
1132}
1133
1134static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
1135        char *uin, *message;
1136        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
1137       
1138        uin = g_strdup_printf("%u", data->uin);
1139        message = g_strdup_printf("No reason given.");
1140        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1141        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1142        g_free(message);
1143       
1144        g_free(uin);
1145        g_free(data);
1146}
1147
1148/*
1149 * For when other people ask you for authorization
1150 */
1151static void gaim_icq_authask(struct im_connection *ic, guint32 uin, char *msg) {
1152        struct icq_auth *data = g_new(struct icq_auth, 1);
1153        char *reason = NULL;
1154        char *dialog_msg;
1155       
1156        if (strlen(msg) > 6)
1157                reason = msg + 6;
1158       
1159        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.");
1160        data->ic = ic;
1161        data->uin = uin;
1162        imcb_ask(ic, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
1163        g_free(dialog_msg);
1164}
1165
1166static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
1167        struct im_connection *ic = sess->aux_data;
1168
1169        switch (args->type) {
1170                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1171                        char *uin, *message;
1172                        uin = g_strdup_printf("%u", args->uin);
1173                        message = g_strdup(args->msg);
1174                        strip_linefeed(message);
1175                        imcb_buddy_msg(ic, uin, message, 0, 0);
1176                        g_free(uin);
1177                        g_free(message);
1178                } break;
1179
1180                case 0x0004: { /* Someone sent you a URL */
1181                        char *uin, *message;
1182                        char **m;
1183       
1184                        uin = g_strdup_printf("%u", args->uin);
1185                        m = g_strsplit(args->msg, "\376", 2);
1186
1187                        if ((strlen(m[0]) != 0)) {
1188                          message = g_strjoinv(" -- ", m);
1189                        } else {
1190                          message = m[1];
1191                        }
1192
1193                        strip_linefeed(message);
1194                        imcb_buddy_msg(ic, uin, message, 0, 0);
1195                        g_free(uin);
1196                        g_free(m);
1197                        g_free(message);
1198                } break;
1199               
1200                case 0x0006: { /* Someone requested authorization */
1201                        gaim_icq_authask(ic, args->uin, args->msg);
1202                } break;
1203
1204                case 0x0007: { /* Someone has denied you authorization */
1205                        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.") );
1206                } break;
1207
1208                case 0x0008: { /* Someone has granted you authorization */
1209                        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.") );
1210                } break;
1211
1212                case 0x0012: {
1213                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1214                } break;
1215
1216                default: {;
1217                } break;
1218        }
1219
1220        return 1;
1221}
1222
1223static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1224        int channel, ret = 0;
1225        aim_userinfo_t *userinfo;
1226        va_list ap;
1227
1228        va_start(ap, fr);
1229        channel = va_arg(ap, int);
1230        userinfo = va_arg(ap, aim_userinfo_t *);
1231
1232        switch (channel) {
1233                case 1: { /* standard message */
1234                        struct aim_incomingim_ch1_args *args;
1235                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1236                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1237                } break;
1238
1239                case 2: { /* rendevous */
1240                        struct aim_incomingim_ch2_args *args;
1241                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1242                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1243                } break;
1244
1245                case 4: { /* ICQ */
1246                        struct aim_incomingim_ch4_args *args;
1247                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1248                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1249                } break;
1250
1251                default: {;
1252                } break;
1253        }
1254
1255        va_end(ap);
1256
1257        return ret;
1258}
1259
1260static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1261        va_list ap;
1262        guint16 chan, nummissed, reason;
1263        aim_userinfo_t *userinfo;
1264
1265        va_start(ap, fr);
1266        chan = (guint16)va_arg(ap, unsigned int);
1267        userinfo = va_arg(ap, aim_userinfo_t *);
1268        nummissed = (guint16)va_arg(ap, unsigned int);
1269        reason = (guint16)va_arg(ap, unsigned int);
1270        va_end(ap);
1271
1272        switch(reason) {
1273                case 0:
1274                        /* Invalid (0) */
1275                        imcb_error(sess->aux_data,
1276                                   nummissed == 1 ? 
1277                                   _("You missed %d message from %s because it was invalid.") :
1278                                   _("You missed %d messages from %s because they were invalid."),
1279                                   nummissed,
1280                                   userinfo->sn);
1281                        break;
1282                case 1:
1283                        /* Message too large */
1284                        imcb_error(sess->aux_data,
1285                                   nummissed == 1 ?
1286                                   _("You missed %d message from %s because it was too large.") :
1287                                   _("You missed %d messages from %s because they were too large."),
1288                                   nummissed,
1289                                   userinfo->sn);
1290                        break;
1291                case 2:
1292                        /* Rate exceeded */
1293                        imcb_error(sess->aux_data,
1294                                   nummissed == 1 ? 
1295                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1296                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1297                                   nummissed,
1298                                   userinfo->sn);
1299                        break;
1300                case 3:
1301                        /* Evil Sender */
1302                        imcb_error(sess->aux_data,
1303                                   nummissed == 1 ?
1304                                   _("You missed %d message from %s because it was too evil.") : 
1305                                   _("You missed %d messages from %s because they are too evil."),
1306                                   nummissed,
1307                                   userinfo->sn);
1308                        break;
1309                case 4:
1310                        /* Evil Receiver */
1311                        imcb_error(sess->aux_data,
1312                                   nummissed == 1 ? 
1313                                   _("You missed %d message from %s because you are too evil.") :
1314                                   _("You missed %d messages from %s because you are too evil."),
1315                                   nummissed,
1316                                   userinfo->sn);
1317                        break;
1318                default:
1319                        imcb_error(sess->aux_data,
1320                                   nummissed == 1 ? 
1321                                   _("You missed %d message from %s for unknown reasons.") :
1322                                   _("You missed %d messages from %s for unknown reasons."),
1323                                   nummissed,
1324                                   userinfo->sn);
1325                        break;
1326        }
1327
1328        return 1;
1329}
1330
1331static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1332        va_list ap;
1333        guint16 reason;
1334
1335        va_start(ap, fr);
1336        reason = (guint16)va_arg(ap, unsigned int);
1337        va_end(ap);
1338
1339        imcb_error(sess->aux_data, _("SNAC threw error: %s"),
1340                  reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1341
1342        return 1;
1343}
1344
1345static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1346        va_list ap;
1347        char *destn;
1348        guint16 reason;
1349
1350        va_start(ap, fr);
1351        reason = (guint16)va_arg(ap, unsigned int);
1352        destn = va_arg(ap, char *);
1353        va_end(ap);
1354
1355        imcb_error(sess->aux_data, _("Your message to %s did not get sent: %s"), destn,
1356                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1357
1358        return 1;
1359}
1360
1361static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1362        va_list ap;
1363        char *destn;
1364        guint16 reason;
1365
1366        va_start(ap, fr);
1367        reason = (guint16)va_arg(ap, unsigned int);
1368        destn = va_arg(ap, char *);
1369        va_end(ap);
1370
1371        imcb_error(sess->aux_data, _("User information for %s unavailable: %s"), destn,
1372                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1373
1374        return 1;
1375}
1376
1377static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1378        char *msg;
1379        guint16 id;
1380        va_list ap;
1381
1382        va_start(ap, fr);
1383        id  = (guint16)va_arg(ap, unsigned int);
1384        msg = va_arg(ap, char *);
1385        va_end(ap);
1386
1387        if (id < 4)
1388                imcb_error(sess->aux_data, _("Your connection may be lost."));
1389
1390        return 1;
1391}
1392
1393static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1394        va_list ap;
1395        guint16 type;
1396        struct im_connection *ic = sess->aux_data;
1397        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1398
1399        va_start(ap, fr);
1400        type = (guint16)va_arg(ap, unsigned int);
1401
1402        switch(type) {
1403                case 0x0002: {
1404                        guint8 maxrooms;
1405                        struct aim_chat_exchangeinfo *exchanges;
1406                        int exchangecount; // i;
1407
1408                        maxrooms = (guint8)va_arg(ap, unsigned int);
1409                        exchangecount = va_arg(ap, int);
1410                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1411                        va_end(ap);
1412
1413                        while (odata->create_rooms) {
1414                                struct create_room *cr = odata->create_rooms->data;
1415                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1416                                g_free(cr->name);
1417                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1418                                g_free(cr);
1419                        }
1420                        }
1421                        break;
1422                case 0x0008: {
1423                        char *fqcn, *name, *ck;
1424                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1425                        guint8 createperms;
1426                        guint32 createtime;
1427
1428                        fqcn = va_arg(ap, char *);
1429                        instance = (guint16)va_arg(ap, unsigned int);
1430                        exchange = (guint16)va_arg(ap, unsigned int);
1431                        flags = (guint16)va_arg(ap, unsigned int);
1432                        createtime = va_arg(ap, guint32);
1433                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1434                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1435                        createperms = (guint8)va_arg(ap, int);
1436                        unknown = (guint16)va_arg(ap, unsigned int);
1437                        name = va_arg(ap, char *);
1438                        ck = va_arg(ap, char *);
1439                        va_end(ap);
1440
1441                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1442                        }
1443                        break;
1444                default:
1445                        va_end(ap);
1446                        break;
1447        }
1448        return 1;
1449}
1450
1451static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1452        va_list ap;
1453        int count, i;
1454        aim_userinfo_t *info;
1455        struct im_connection *g = sess->aux_data;
1456
1457        struct chat_connection *c = NULL;
1458
1459        va_start(ap, fr);
1460        count = va_arg(ap, int);
1461        info  = va_arg(ap, aim_userinfo_t *);
1462        va_end(ap);
1463
1464        c = find_oscar_chat_by_conn(g, fr->conn);
1465        if (!c)
1466                return 1;
1467
1468        for (i = 0; i < count; i++)
1469                imcb_chat_add_buddy(c->cnv, info[i].sn);
1470
1471        return 1;
1472}
1473
1474static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1475        va_list ap;
1476        int count, i;
1477        aim_userinfo_t *info;
1478        struct im_connection *g = sess->aux_data;
1479
1480        struct chat_connection *c = NULL;
1481
1482        va_start(ap, fr);
1483        count = va_arg(ap, int);
1484        info  = va_arg(ap, aim_userinfo_t *);
1485        va_end(ap);
1486
1487        c = find_oscar_chat_by_conn(g, fr->conn);
1488        if (!c)
1489                return 1;
1490
1491        for (i = 0; i < count; i++)
1492                imcb_chat_remove_buddy(c->cnv, info[i].sn, NULL);
1493
1494        return 1;
1495}
1496
1497static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1498        va_list ap;
1499        aim_userinfo_t *userinfo;
1500        struct aim_chat_roominfo *roominfo;
1501        char *roomname;
1502        int usercount;
1503        char *roomdesc;
1504        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1505        guint32 creationtime;
1506        struct im_connection *ic = sess->aux_data;
1507        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
1508
1509        va_start(ap, fr);
1510        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1511        roomname = va_arg(ap, char *);
1512        usercount= va_arg(ap, int);
1513        userinfo = va_arg(ap, aim_userinfo_t *);
1514        roomdesc = va_arg(ap, char *);
1515        unknown_c9 = (guint16)va_arg(ap, int);
1516        creationtime = (guint32)va_arg(ap, unsigned long);
1517        maxmsglen = (guint16)va_arg(ap, int);
1518        unknown_d2 = (guint16)va_arg(ap, int);
1519        unknown_d5 = (guint16)va_arg(ap, int);
1520        maxvisiblemsglen = (guint16)va_arg(ap, int);
1521        va_end(ap);
1522
1523        ccon->maxlen = maxmsglen;
1524        ccon->maxvis = maxvisiblemsglen;
1525
1526        return 1;
1527}
1528
1529static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1530        va_list ap;
1531        aim_userinfo_t *info;
1532        char *msg;
1533        struct im_connection *ic = sess->aux_data;
1534        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
1535        char *tmp;
1536
1537        va_start(ap, fr);
1538        info = va_arg(ap, aim_userinfo_t *);
1539        msg  = va_arg(ap, char *);
1540
1541        tmp = g_malloc(BUF_LONG);
1542        g_snprintf(tmp, BUF_LONG, "%s", msg);
1543        imcb_chat_msg(ccon->cnv, info->sn, tmp, 0, 0);
1544        g_free(tmp);
1545
1546        return 1;
1547}
1548
1549static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1550#if 0
1551        static const char *codes[5] = {
1552                "invalid",
1553                 "change",
1554                 "warning",
1555                 "limit",
1556                 "limit cleared",
1557        };
1558#endif
1559        va_list ap;
1560        guint16 code, rateclass;
1561        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1562
1563        va_start(ap, fr); 
1564        code = (guint16)va_arg(ap, unsigned int);
1565        rateclass= (guint16)va_arg(ap, unsigned int);
1566        windowsize = (guint32)va_arg(ap, unsigned long);
1567        clear = (guint32)va_arg(ap, unsigned long);
1568        alert = (guint32)va_arg(ap, unsigned long);
1569        limit = (guint32)va_arg(ap, unsigned long);
1570        disconnect = (guint32)va_arg(ap, unsigned long);
1571        currentavg = (guint32)va_arg(ap, unsigned long);
1572        maxavg = (guint32)va_arg(ap, unsigned long);
1573        va_end(ap);
1574
1575        /* XXX fix these values */
1576        if (code == AIM_RATE_CODE_CHANGE) {
1577                if (currentavg >= clear)
1578                        aim_conn_setlatency(fr->conn, 0);
1579        } else if (code == AIM_RATE_CODE_WARNING) {
1580                aim_conn_setlatency(fr->conn, windowsize/4);
1581        } else if (code == AIM_RATE_CODE_LIMIT) {
1582                imcb_error(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1583                          "Please wait 10 seconds and try again."));
1584                aim_conn_setlatency(fr->conn, windowsize/2);
1585        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1586                aim_conn_setlatency(fr->conn, 0);
1587        }
1588
1589        return 1;
1590}
1591
1592static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1593        va_list ap;
1594        aim_userinfo_t *info;
1595        struct im_connection *ic = sess->aux_data;
1596
1597        va_start(ap, fr);
1598        info = va_arg(ap, aim_userinfo_t *);
1599        va_end(ap);
1600
1601        ic->evil = info->warnlevel/10;
1602        /* ic->correction_time = (info->onlinesince - ic->login_time); */
1603
1604        return 1;
1605}
1606
1607static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1608
1609        aim_reqpersonalinfo(sess, fr->conn);
1610        aim_bos_reqlocaterights(sess, fr->conn);
1611        aim_bos_reqbuddyrights(sess, fr->conn);
1612
1613        aim_reqicbmparams(sess);
1614
1615        aim_bos_reqrights(sess, fr->conn);
1616        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1617        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1618                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1619
1620        return 1;
1621}
1622
1623static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1624        struct im_connection *ic = sess->aux_data;
1625        struct oscar_data *od = ic->proto_data;
1626
1627        aim_clientready(sess, fr->conn);
1628
1629        if (od->chpass) {
1630                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1631                g_free(od->oldp);
1632                od->oldp = NULL;
1633                g_free(od->newp);
1634                od->newp = NULL;
1635                od->chpass = FALSE;
1636        }
1637        if (od->setnick) {
1638                aim_admin_setnick(sess, fr->conn, od->newsn);
1639                g_free(od->newsn);
1640                od->newsn = NULL;
1641                od->setnick = FALSE;
1642        }
1643        if (od->conf) {
1644                aim_admin_reqconfirm(sess, fr->conn);
1645                od->conf = FALSE;
1646        }
1647        if (od->reqemail) {
1648                aim_admin_getinfo(sess, fr->conn, 0x0011);
1649                od->reqemail = FALSE;
1650        }
1651        if (od->setemail) {
1652                aim_admin_setemail(sess, fr->conn, od->email);
1653                g_free(od->email);
1654                od->setemail = FALSE;
1655        }
1656
1657        return 1;
1658}
1659
1660static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1661        struct aim_icbmparameters *params;
1662        va_list ap;
1663
1664        va_start(ap, fr);
1665        params = va_arg(ap, struct aim_icbmparameters *);
1666        va_end(ap);
1667
1668        /* Maybe senderwarn and recverwarn should be user preferences... */
1669        params->flags = 0x0000000b;
1670        params->maxmsglen = 8000;
1671        params->minmsginterval = 0;
1672
1673        aim_seticbmparam(sess, params);
1674
1675        return 1;
1676}
1677
1678static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1679{
1680        va_list ap;
1681        guint16 maxsiglen;
1682        struct im_connection *ic = sess->aux_data;
1683        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1684
1685        va_start(ap, fr);
1686        maxsiglen = va_arg(ap, int);
1687        va_end(ap);
1688
1689        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1690
1691        /* FIXME: It seems we're not really using this, and it broke now that
1692           struct aim_user is dead.
1693        aim_bos_setprofile(sess, fr->conn, ic->user->user_info, NULL, gaim_caps);
1694        */
1695       
1696        return 1;
1697}
1698
1699static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1700        va_list ap;
1701        guint16 maxbuddies, maxwatchers;
1702        struct im_connection *ic = sess->aux_data;
1703        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1704
1705        va_start(ap, fr);
1706        maxbuddies = (guint16)va_arg(ap, unsigned int);
1707        maxwatchers = (guint16)va_arg(ap, unsigned int);
1708        va_end(ap);
1709
1710        odata->rights.maxbuddies = (guint)maxbuddies;
1711        odata->rights.maxwatchers = (guint)maxwatchers;
1712
1713        return 1;
1714}
1715
1716static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1717        guint16 maxpermits, maxdenies;
1718        va_list ap;
1719        struct im_connection *ic = sess->aux_data;
1720        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1721
1722        va_start(ap, fr);
1723        maxpermits = (guint16)va_arg(ap, unsigned int);
1724        maxdenies = (guint16)va_arg(ap, unsigned int);
1725        va_end(ap);
1726
1727        odata->rights.maxpermits = (guint)maxpermits;
1728        odata->rights.maxdenies = (guint)maxdenies;
1729
1730        aim_clientready(sess, fr->conn);
1731
1732        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1733
1734        aim_ssi_reqrights(sess, fr->conn);
1735        aim_ssi_reqalldata(sess, fr->conn);
1736
1737        return 1;
1738}
1739
1740static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1741        va_list ap;
1742        struct aim_icq_offlinemsg *msg;
1743        struct im_connection *ic = sess->aux_data;
1744
1745        va_start(ap, fr);
1746        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1747        va_end(ap);
1748
1749        switch (msg->type) {
1750                case 0x0001: { /* Basic offline message */
1751                        char sender[32];
1752                        char *dialog_msg = g_strdup(msg->msg);
1753                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1754                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1755                        strip_linefeed(dialog_msg);
1756                        imcb_buddy_msg(ic, sender, dialog_msg, 0, t);
1757                        g_free(dialog_msg);
1758                } break;
1759
1760                case 0x0004: { /* Someone sent you a URL */
1761                        char sender[32];
1762                        char *dialog_msg;
1763                        char **m;
1764
1765                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1766                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1767
1768                        m = g_strsplit(msg->msg, "\376", 2);
1769
1770                        if ((strlen(m[0]) != 0)) {
1771                          dialog_msg = g_strjoinv(" -- ", m);
1772                        } else {
1773                          dialog_msg = m[1];
1774                        }
1775
1776                        strip_linefeed(dialog_msg);
1777                        imcb_buddy_msg(ic, sender, dialog_msg, 0, t);
1778                        g_free(dialog_msg);
1779                        g_free(m);
1780                } break;
1781               
1782                case 0x0006: { /* Authorization request */
1783                        gaim_icq_authask(ic, msg->sender, msg->msg);
1784                } break;
1785
1786                case 0x0007: { /* Someone has denied you authorization */
1787                        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.") );
1788                } break;
1789
1790                case 0x0008: { /* Someone has granted you authorization */
1791                        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.") );
1792                } break;
1793
1794                case 0x0012: {
1795                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1796                } break;
1797
1798                default: {;
1799                }
1800        }
1801
1802        return 1;
1803}
1804
1805static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1806{
1807        aim_icq_ackofflinemsgs(sess);
1808        return 1;
1809}
1810
1811static void oscar_keepalive(struct im_connection *ic) {
1812        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1813        aim_flap_nop(odata->sess, odata->conn);
1814}
1815
1816static int oscar_buddy_msg(struct im_connection *ic, char *name, char *message, int imflags) {
1817        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1818        int ret = 0, len = strlen(message);
1819        if (imflags & OPT_AWAY) {
1820                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1821        } else {
1822                struct aim_sendimext_args args;
1823                char *s;
1824               
1825                args.flags = AIM_IMFLAGS_ACK;
1826                if (odata->icq)
1827                        args.flags |= AIM_IMFLAGS_OFFLINE;
1828                for (s = message; *s; s++)
1829                        if (*s & 128)
1830                                break;
1831               
1832                /* Message contains high ASCII chars, time for some translation! */
1833                if (*s) {
1834                        s = g_malloc(BUF_LONG);
1835                        /* Try if we can put it in an ISO8859-1 string first.
1836                           If we can't, fall back to UTF16. */
1837                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1838                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1839                                len = ret;
1840                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1841                                args.flags |= AIM_IMFLAGS_UNICODE;
1842                                len = ret;
1843                        } else {
1844                                /* OOF, translation failed... Oh well.. */
1845                                g_free( s );
1846                                s = message;
1847                        }
1848                } else {
1849                        s = message;
1850                }
1851               
1852                args.features = gaim_features;
1853                args.featureslen = sizeof(gaim_features);
1854               
1855                args.destsn = name;
1856                args.msg    = s;
1857                args.msglen = len;
1858               
1859                ret = aim_send_im_ext(odata->sess, &args);
1860               
1861                if (s != message) {
1862                        g_free(s);
1863                }
1864        }
1865        if (ret >= 0)
1866                return 1;
1867        return ret;
1868}
1869
1870static void oscar_get_info(struct im_connection *g, char *name) {
1871        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1872        if (odata->icq)
1873                aim_icq_getallinfo(odata->sess, name);
1874        else {
1875                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1876                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1877        }
1878}
1879
1880static void oscar_get_away(struct im_connection *g, char *who) {
1881        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1882        if (odata->icq) {
1883                struct buddy *budlight = imcb_find_buddy(g, who);
1884                if (budlight)
1885                        if ((budlight->uc & 0xff80) >> 7)
1886                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1887                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1888        } else
1889                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1890}
1891
1892static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
1893{
1894
1895        if (!g_strcasecmp(state, _("Visible"))) {
1896                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1897                return;
1898        } else if (!g_strcasecmp(state, _("Invisible"))) {
1899                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1900                return;
1901        } /* else... */
1902
1903        if (od->rights.maxawaymsglen == 0)
1904                imcb_error(ic, "oscar_set_away_aim called before locate rights received");
1905
1906        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1907
1908        if (ic->away)
1909                g_free(ic->away);
1910        ic->away = NULL;
1911
1912        if (!message) {
1913                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1914                return;
1915        }
1916
1917        if (strlen(message) > od->rights.maxawaymsglen) {
1918                imcb_error(ic, "Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
1919        }
1920
1921        ic->away = g_strndup(message, od->rights.maxawaymsglen);
1922        aim_bos_setprofile(od->sess, od->conn, NULL, ic->away, gaim_caps);
1923
1924        return;
1925}
1926
1927static void oscar_set_away_icq(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
1928{
1929    const char *msg = NULL;
1930        gboolean no_message = FALSE;
1931
1932        /* clean old states */
1933    if (ic->away) {
1934                g_free(ic->away);
1935                ic->away = NULL;
1936    }
1937        od->sess->aim_icq_state = 0;
1938
1939        /* if no message, then use an empty message */
1940    if (message) {
1941        msg = message;
1942    } else {
1943        msg = "";
1944                no_message = TRUE;
1945    }
1946
1947        if (!g_strcasecmp(state, "Online")) {
1948                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1949        } else if (!g_strcasecmp(state, "Away")) {
1950                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1951        ic->away = g_strdup(msg);
1952                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1953        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1954                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
1955        ic->away = g_strdup(msg);
1956                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1957        } else if (!g_strcasecmp(state, "Not Available")) {
1958                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
1959        ic->away = g_strdup(msg);
1960                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1961        } else if (!g_strcasecmp(state, "Occupied")) {
1962                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
1963        ic->away = g_strdup(msg);
1964                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1965        } else if (!g_strcasecmp(state, "Free For Chat")) {
1966                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
1967        ic->away = g_strdup(msg);
1968                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1969        } else if (!g_strcasecmp(state, "Invisible")) {
1970                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1971        ic->away = g_strdup(msg);
1972        } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
1973                if (no_message) {
1974                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1975                } else {
1976                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1977            ic->away = g_strdup(msg);
1978                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1979                }
1980        }
1981
1982        return;
1983}
1984
1985static void oscar_set_away(struct im_connection *ic, char *state, char *message)
1986{
1987        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
1988
1989    oscar_set_away_aim(ic, od, state, message);
1990        if (od->icq)
1991                oscar_set_away_icq(ic, od, state, message);
1992
1993        return;
1994}
1995
1996static void oscar_add_buddy(struct im_connection *g, char *name, char *group) {
1997        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1998        aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
1999}
2000
2001static void oscar_remove_buddy(struct im_connection *g, char *name, char *group) {
2002        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2003        struct aim_ssi_item *ssigroup;
2004        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
2005}
2006
2007static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
2008        return 1;
2009}
2010
2011static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
2012        struct im_connection *ic = sess->aux_data;
2013        struct aim_ssi_item *curitem;
2014        int tmp;
2015
2016        /* Add from server list to local list */
2017        tmp = 0;
2018        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
2019                switch (curitem->type) {
2020                        case 0x0000: /* Buddy */
2021                                if ((curitem->name) && (!imcb_find_buddy(ic, curitem->name))) {
2022                                        char *realname = NULL;
2023
2024                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
2025                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
2026                                               
2027                                        imcb_add_buddy(ic, curitem->name, NULL);
2028                                       
2029                                        if (realname) {
2030                                                imcb_buddy_nick_hint(ic, curitem->name, realname);
2031                                                imcb_rename_buddy(ic, curitem->name, realname);
2032                                                g_free(realname);
2033                                        }
2034                                }
2035                                break;
2036
2037                        case 0x0002: /* Permit buddy */
2038                                if (curitem->name) {
2039                                        GSList *list;
2040                                        for (list=ic->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2041                                        if (!list) {
2042                                                char *name;
2043                                                name = g_strdup(normalize(curitem->name));
2044                                                ic->permit = g_slist_append(ic->permit, name);
2045                                                tmp++;
2046                                        }
2047                                }
2048                                break;
2049
2050                        case 0x0003: /* Deny buddy */
2051                                if (curitem->name) {
2052                                        GSList *list;
2053                                        for (list=ic->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2054                                        if (!list) {
2055                                                char *name;
2056                                                name = g_strdup(normalize(curitem->name));
2057                                                ic->deny = g_slist_append(ic->deny, name);
2058                                                tmp++;
2059                                        }
2060                                }
2061                                break;
2062
2063                        case 0x0004: /* Permit/deny setting */
2064                                if (curitem->data) {
2065                                        guint8 permdeny;
2066                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != ic->permdeny)) {
2067                                                ic->permdeny = permdeny;
2068                                                tmp++;
2069                                        }
2070                                }
2071                                break;
2072
2073                        case 0x0005: /* Presence setting */
2074                                /* We don't want to change Gaim's setting because it applies to all accounts */
2075                                break;
2076                } /* End of switch on curitem->type */
2077        } /* End of for loop */
2078
2079        aim_ssi_enable(sess, fr->conn);
2080       
2081        /* Request offline messages, now that the buddy list is complete. */
2082        aim_icq_reqofflinemsgs(sess);
2083       
2084        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
2085        imcb_connected(ic);
2086       
2087        return 1;
2088}
2089
2090static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2091{
2092        aim_snac_t *origsnac;
2093        va_list ap;
2094
2095        va_start( ap, fr );
2096        origsnac = va_arg( ap, aim_snac_t * );
2097        va_end( ap );
2098       
2099        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2100        {
2101                int i, st, count = aim_bstream_empty( &fr->data );
2102                char *list;
2103               
2104                if( count & 1 )
2105                {
2106                        /* Hmm, the length should be even... */
2107                        imcb_error( sess->aux_data, "Received SSI ACK package with non-even length");
2108                        return( 0 );
2109                }
2110                count >>= 1;
2111               
2112                list = (char *) origsnac->data;
2113                for( i = 0; i < count; i ++ )
2114                {
2115                        st = aimbs_get16( &fr->data );
2116                        if( st == 0x00 )
2117                        {
2118                                imcb_add_buddy( sess->aux_data, list, NULL );
2119                        }
2120                        else if( st == 0x0E )
2121                        {
2122                                imcb_log( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
2123                               
2124                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2125                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2126                        }
2127                        else
2128                        {
2129                                imcb_error( sess->aux_data, "Error while adding buddy: 0x%04x", st );
2130                        }
2131                        list += strlen( list ) + 1;
2132                }
2133        }
2134       
2135        return( 1 );
2136}
2137
2138static void oscar_set_permit_deny(struct im_connection *ic) {
2139        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2140        if (od->icq) {
2141                GSList *list;
2142                char buf[MAXMSGLEN];
2143                int at;
2144
2145                switch(ic->permdeny) {
2146                case 1:
2147                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, ic->acc->user);
2148                        break;
2149                case 2:
2150                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, ic->acc->user);
2151                        break;
2152                case 3:
2153                        list = ic->permit;
2154                        at = 0;
2155                        while (list) {
2156                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2157                                list = list->next;
2158                        }
2159                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2160                        break;
2161                case 4:
2162                        list = ic->deny;
2163                        at = 0;
2164                        while (list) {
2165                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2166                                list = list->next;
2167                        }
2168                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2169                        break;
2170                        default:
2171                        break;
2172                }
2173        } else {
2174                if (od->sess->ssi.received_data)
2175                        aim_ssi_setpermdeny(od->sess, od->conn, ic->permdeny, 0xffffffff);
2176        }
2177}
2178
2179static void oscar_add_permit(struct im_connection *ic, char *who) {
2180        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2181        if (od->icq) {
2182                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2183        } else {
2184                if (od->sess->ssi.received_data)
2185                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2186        }
2187}
2188
2189static void oscar_add_deny(struct im_connection *ic, char *who) {
2190        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2191        if (od->icq) {
2192                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2193        } else {
2194                if (od->sess->ssi.received_data)
2195                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2196        }
2197}
2198
2199static void oscar_rem_permit(struct im_connection *ic, char *who) {
2200        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2201        if (!od->icq) {
2202                if (od->sess->ssi.received_data)
2203                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2204        }
2205}
2206
2207static void oscar_rem_deny(struct im_connection *ic, char *who) {
2208        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2209        if (!od->icq) {
2210                if (od->sess->ssi.received_data)
2211                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2212        }
2213}
2214
2215static GList *oscar_away_states(struct im_connection *ic)
2216{
2217        struct oscar_data *od = ic->proto_data;
2218        GList *m = NULL;
2219
2220        if (!od->icq)
2221                return g_list_append(m, GAIM_AWAY_CUSTOM);
2222
2223        m = g_list_append(m, "Online");
2224        m = g_list_append(m, "Away");
2225        m = g_list_append(m, "Do Not Disturb");
2226        m = g_list_append(m, "Not Available");
2227        m = g_list_append(m, "Occupied");
2228        m = g_list_append(m, "Free For Chat");
2229        m = g_list_append(m, "Invisible");
2230
2231        return m;
2232}
2233
2234static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2235{
2236        struct im_connection *ic = sess->aux_data;
2237        struct oscar_data *od = ic->proto_data;
2238        gchar who[16];
2239        GString *str;
2240        va_list ap;
2241        struct aim_icq_info *info;
2242        uint32_t ip;
2243
2244        va_start(ap, fr);
2245        info = va_arg(ap, struct aim_icq_info *);
2246        va_end(ap);
2247
2248        if (!info->uin)
2249                return 0;
2250
2251        str = g_string_sized_new(512);
2252        g_snprintf(who, sizeof(who), "%u", info->uin);
2253
2254        g_string_printf(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2255        info->nick ? info->nick : "-");
2256        g_string_append_printf(str, "\n%s: %s", _("First Name"), info->first);
2257        g_string_append_printf(str, "\n%s: %s", _("Last Name"), info->last);
2258        g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email);
2259        if (info->numaddresses && info->email2) {
2260                int i;
2261                for (i = 0; i < info->numaddresses; i++) {
2262                        g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email2[i]);
2263                }
2264        }
2265        if ((ip = (long) g_hash_table_lookup(od->ips, &info->uin)) != 0) {
2266                g_string_append_printf(str, "\n%s: %d.%d.%d.%d", _("Last used IP address"),
2267                                       (ip >> 24), (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
2268        }
2269        g_string_append_printf(str, "\n%s: %s", _("Mobile Phone"), info->mobile);
2270        if (info->gender != 0)
2271                g_string_append_printf(str, "\n%s: %s", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
2272        if (info->birthyear || info->birthmonth || info->birthday) {
2273                char date[30];
2274                struct tm tm;
2275                memset(&tm, 0, sizeof(struct tm));
2276                tm.tm_mday = (int)info->birthday;
2277                tm.tm_mon = (int)info->birthmonth-1;
2278                tm.tm_year = (int)info->birthyear%100;
2279                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2280                g_string_append_printf(str, "\n%s: %s", _("Birthday"), date);
2281        }
2282        if (info->age) {
2283                char age[5];
2284                g_snprintf(age, sizeof(age), "%hhd", info->age);
2285                g_string_append_printf(str, "\n%s: %s", _("Age"), age);
2286        }
2287        g_string_append_printf(str, "\n%s: %s", _("Personal Web Page"), info->personalwebpage);
2288        if (info->info && info->info[0]) {
2289                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2290                info->info, _("End of Additional Information"));
2291        }
2292        g_string_append_c(str, '\n');
2293        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2294                g_string_append_printf(str, "%s:", _("Home Address"));
2295                g_string_append_printf(str, "\n%s: %s", _("Address"), info->homeaddr);
2296                g_string_append_printf(str, "\n%s: %s", _("City"), info->homecity);
2297                g_string_append_printf(str, "\n%s: %s", _("State"), info->homestate); 
2298                g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->homezip);
2299                g_string_append_c(str, '\n');
2300        }
2301        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2302                g_string_append_printf(str, "%s:", _("Work Address"));
2303                g_string_append_printf(str, "\n%s: %s", _("Address"), info->workaddr);
2304                g_string_append_printf(str, "\n%s: %s", _("City"), info->workcity);
2305                g_string_append_printf(str, "\n%s: %s", _("State"), info->workstate);
2306                g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->workzip);
2307                g_string_append_c(str, '\n');
2308        }
2309        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2310                g_string_append_printf(str, "%s:", _("Work Information"));
2311                g_string_append_printf(str, "\n%s: %s", _("Company"), info->workcompany);
2312                g_string_append_printf(str, "\n%s: %s", _("Division"), info->workdivision);
2313                g_string_append_printf(str, "\n%s: %s", _("Position"), info->workposition);
2314                if (info->workwebpage && info->workwebpage[0]) {
2315                        g_string_append_printf(str, "\n%s: %s", _("Web Page"), info->workwebpage);
2316                }
2317                g_string_append_c(str, '\n');
2318        }
2319
2320        imcb_log(ic, "%s\n%s", _("User Info"), str->str);
2321        g_string_free(str, TRUE);
2322
2323        return 1;
2324
2325}
2326
2327static char *oscar_encoding_extract(const char *encoding)
2328{
2329        char *ret = NULL;
2330        char *begin, *end;
2331
2332        g_return_val_if_fail(encoding != NULL, NULL);
2333
2334        /* Make sure encoding begins with charset= */
2335        if (strncmp(encoding, "text/plain; charset=", 20) &&
2336                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2337                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2338        {
2339                return NULL;
2340        }
2341
2342        begin = strchr(encoding, '"');
2343        end = strrchr(encoding, '"');
2344
2345        if ((begin == NULL) || (end == NULL) || (begin >= end))
2346                return NULL;
2347
2348        ret = g_strndup(begin+1, (end-1) - begin);
2349
2350        return ret;
2351}
2352
2353static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2354{
2355        char *utf8 = g_new0(char, 8192);
2356
2357        if ((encoding == NULL) || encoding[0] == '\0') {
2358                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2359        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2360                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2361        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2362                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2363        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2364                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2365        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2366                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2367                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2368                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2369        }
2370
2371        /*
2372         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2373         * we have been unable to convert the text to utf-8 from the encoding
2374         * that was specified.  So we assume it's UTF-8 and hope for the best.
2375         */
2376        if (*utf8 == 0) {
2377            strncpy(utf8, text, textlen);
2378        }
2379
2380        return utf8;
2381}
2382
2383static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2384{
2385        struct im_connection *ic = sess->aux_data;
2386        va_list ap;
2387        aim_userinfo_t *userinfo;
2388        guint16 infotype;
2389        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2390        guint16 text_length;
2391        char *utf8 = NULL;
2392
2393        va_start(ap, fr);
2394        userinfo = va_arg(ap, aim_userinfo_t *);
2395        infotype = va_arg(ap, int);
2396        text_encoding = va_arg(ap, char*);
2397        text = va_arg(ap, char*);
2398        text_length = va_arg(ap, int);
2399        va_end(ap);
2400
2401        if(text_encoding)
2402                extracted_encoding = oscar_encoding_extract(text_encoding);
2403        if(infotype == AIM_GETINFO_GENERALINFO) {
2404                /*Display idle time*/
2405                char buff[256];
2406                struct tm idletime;
2407                if(userinfo->idletime) {
2408                        memset(&idletime, 0, sizeof(struct tm));
2409                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2410                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2411                        idletime.tm_min = userinfo->idletime % 60;
2412                        idletime.tm_sec = 0;
2413                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
2414                        imcb_log(ic, "%s: %s", _("Idle Time"), buff);
2415                }
2416               
2417                if(text) {
2418                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2419                        imcb_log(ic, "%s\n%s", _("User Info"), utf8);
2420                } else {
2421                        imcb_log(ic, _("No user info available."));
2422                }
2423        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2424                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2425                imcb_log(ic, "%s\n%s", _("Away Message"), utf8);
2426        }
2427
2428        g_free(utf8);
2429   
2430        return 1;
2431}
2432
2433int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)
2434{
2435        struct im_connection * ic = sess->aux_data;
2436        va_list ap;
2437        guint16 type1, type2;
2438        char * sn;
2439
2440        va_start(ap, fr);
2441        type1 = va_arg(ap, int);
2442        sn = va_arg(ap, char*);
2443        type2 = va_arg(ap, int);
2444        va_end(ap);
2445   
2446        if(type2 == 0x0002) {
2447                /* User is typing */
2448                imcb_buddy_typing(ic, sn, OPT_TYPING);
2449        } 
2450        else if (type2 == 0x0001) {
2451                /* User has typed something, but is not actively typing (stale) */
2452                imcb_buddy_typing(ic, sn, OPT_THINKING);
2453        }
2454        else {
2455                /* User has stopped typing */
2456                imcb_buddy_typing(ic, sn, 0);
2457        }
2458       
2459        return 1;
2460}
2461
2462int oscar_send_typing(struct im_connection *ic, char * who, int typing)
2463{
2464        struct oscar_data *od = ic->proto_data;
2465        return( aim_im_sendmtn(od->sess, 1, who, (typing & OPT_TYPING) ? 0x0002 : 0x0000) );
2466}
2467
2468void oscar_chat_msg(struct groupchat *c, char *message, int msgflags)
2469{
2470        struct im_connection *ic = c->ic;
2471        struct oscar_data * od = (struct oscar_data*)ic->proto_data;
2472        struct chat_connection * ccon;
2473        int ret;
2474        guint8 len = strlen(message);
2475        guint16 flags;
2476        char *s;
2477       
2478        ccon = c->data;
2479               
2480        for (s = message; *s; s++)
2481                if (*s & 128)
2482                        break;
2483       
2484        flags = AIM_CHATFLAGS_NOREFLECT;
2485       
2486        /* Message contains high ASCII chars, time for some translation! */
2487        if (*s) {
2488                s = g_malloc(BUF_LONG);
2489                /* Try if we can put it in an ISO8859-1 string first.
2490                   If we can't, fall back to UTF16. */
2491                if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
2492                        flags |= AIM_CHATFLAGS_ISO_8859_1;
2493                        len = ret;
2494                } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
2495                        flags |= AIM_CHATFLAGS_UNICODE;
2496                        len = ret;
2497                } else {
2498                        /* OOF, translation failed... Oh well.. */
2499                        g_free( s );
2500                        s = message;
2501                }
2502        } else {
2503                s = message;
2504        }
2505               
2506        ret = aim_chat_send_im(od->sess, ccon->conn, flags, s, len);
2507               
2508        if (s != message) {     
2509                g_free(s);
2510  }
2511 
2512/*  return (ret >= 0); */
2513}
2514
2515void oscar_chat_invite(struct groupchat *c, char *who, char *message)
2516{
2517        struct im_connection *ic = c->ic;
2518        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2519        struct chat_connection *ccon = c->data;
2520       
2521        aim_chat_invite(od->sess, od->conn, who, message ? message : "",
2522                                        ccon->exchange, ccon->name, 0x0);
2523}
2524
2525void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc)
2526{
2527        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2528
2529        /* Notify the conversation window that we've left the chat */
2530        imcb_chat_free(cc->cnv);
2531
2532        /* Destroy the chat_connection */
2533        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
2534        if (cc->inpa > 0)
2535                b_event_remove(cc->inpa);
2536        aim_conn_kill(od->sess, &cc->conn);
2537        g_free(cc->name);
2538        g_free(cc->show);
2539        g_free(cc);
2540}
2541
2542void oscar_chat_leave(struct groupchat *c)
2543{
2544        oscar_chat_kill(c->ic, c->data);
2545}
2546
2547struct groupchat *oscar_chat_join(struct im_connection * ic, char * room, char * nick, char * password )
2548{
2549        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2550        aim_conn_t * cur;
2551
2552        if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
2553                int st;
2554               
2555                st = aim_chatnav_createroom(od->sess, cur, room, 4);
2556               
2557                return NULL;
2558        } else {
2559                struct create_room * cr = g_new0(struct create_room, 1);
2560               
2561                cr->exchange = 4;
2562                cr->name = g_strdup(room);
2563                od->create_rooms = g_slist_append(od->create_rooms, cr);
2564                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
2565               
2566                return NULL;
2567        }
2568}
2569
2570struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)
2571{
2572        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2573        struct groupchat *ret;
2574        static int chat_id = 0;
2575        char * chatname;
2576       
2577        chatname = g_strdup_printf("%s%d", ic->acc->user, chat_id++);
2578 
2579        ret = oscar_chat_join(ic, chatname, NULL, NULL);
2580
2581        aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);
2582
2583        g_free(chatname);
2584       
2585        return NULL;
2586}
2587
2588void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
2589{
2590        oscar_chat_join(inv->ic, inv->name, NULL, NULL);
2591        g_free(inv->name);
2592        g_free(inv);
2593}
2594
2595void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
2596{
2597        g_free(inv->name);
2598        g_free(inv);
2599}
2600
2601void oscar_initmodule() 
2602{
2603        struct prpl *ret = g_new0(struct prpl, 1);
2604        ret->name = "oscar";
2605    ret->mms = 2343;       /* this guess taken from libotr UPGRADING file */
2606        ret->away_states = oscar_away_states;
2607        ret->init = oscar_init;
2608        ret->login = oscar_login;
2609        ret->keepalive = oscar_keepalive;
2610        ret->logout = oscar_logout;
2611        ret->buddy_msg = oscar_buddy_msg;
2612        ret->get_info = oscar_get_info;
2613        ret->set_away = oscar_set_away;
2614        ret->get_away = oscar_get_away;
2615        ret->add_buddy = oscar_add_buddy;
2616        ret->remove_buddy = oscar_remove_buddy;
2617        ret->chat_msg = oscar_chat_msg;
2618        ret->chat_invite = oscar_chat_invite;
2619        ret->chat_leave = oscar_chat_leave;
2620        ret->chat_with = oscar_chat_with;
2621        ret->chat_join = oscar_chat_join;
2622        ret->add_permit = oscar_add_permit;
2623        ret->add_deny = oscar_add_deny;
2624        ret->rem_permit = oscar_rem_permit;
2625        ret->rem_deny = oscar_rem_deny;
2626        ret->set_permit_deny = oscar_set_permit_deny;
2627        ret->send_typing = oscar_send_typing;
2628       
2629        ret->handle_cmp = aim_sncmp;
2630
2631        register_protocol(ret);
2632}
Note: See TracBrowser for help on using the repository browser.