source: protocols/oscar/oscar.c @ 0fbd3a6d

Last change on this file since 0fbd3a6d was eded1f7, checked in by kenobi <kenobi@…>, at 2007-12-18T23:59:35Z

Merged in 280..288 from upstream (e.g. PING)

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