source: protocols/oscar/oscar.c @ ebb95b6

Last change on this file since ebb95b6 was ebb95b6, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-11-14T23:42:07Z

Merging from devel/Jelmer.

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