source: protocols/oscar/oscar.c @ 3b6eadc

Last change on this file since 3b6eadc was 05bcd20, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-04-28T05:20:56Z

s/Gender: Unknown in OSCAR profile info. If we don't know, just don't
mention it, like the other vars.

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