source: protocols/oscar/oscar.c @ cfc8d58

Last change on this file since cfc8d58 was 6bbb939, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-04-16T04:01:13Z

Split serv_got_update() into imcb_buddy_(status|times). (Well, the second
one isn't implemented yet, but I'll do that later.) At last I got rid of
the hack called get_status_string(). And now Yahoo seems to mess up away
messages...

  • Property mode set to 100644
File size: 74.0 KB
Line 
1/*
2 * gaim
3 *
4 * Some code copyright (C) 2002-2006, Jelmer Vernooij <jelmer@samba.org>
5 *                                    and the BitlBee team.
6 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
7 * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 *
23 */
24
25#include <errno.h>
26#include <ctype.h>
27#include <string.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <time.h>
31#include <sys/stat.h>
32#include <glib.h>
33#include "nogaim.h"
34#include "bitlbee.h"
35#include "proxy.h"
36#include "sock.h"
37
38#include "aim.h"
39#include "icq.h"
40#include "bos.h"
41#include "ssi.h"
42#include "im.h"
43#include "info.h"
44#include "buddylist.h"
45#include "chat.h"
46#include "chatnav.h"
47
48/* constants to identify proto_opts */
49#define USEROPT_AUTH      0
50#define USEROPT_AUTHPORT  1
51
52#define UC_AOL          0x02
53#define UC_ADMIN        0x04
54#define UC_UNCONFIRMED  0x08
55#define UC_NORMAL       0x10
56#define UC_AB           0x20
57#define UC_WIRELESS     0x40
58
59#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
60
61#define OSCAR_GROUP "Friends"
62
63/* Don't know if support for UTF8 is really working. For now it's UTF16 here.
64   static int gaim_caps = AIM_CAPS_UTF8; */
65
66static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY | AIM_CAPS_CHAT;
67static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02};
68
69struct oscar_data {
70        aim_session_t *sess;
71        aim_conn_t *conn;
72
73        guint cnpa;
74        guint paspa;
75
76        GSList *create_rooms;
77
78        gboolean conf;
79        gboolean reqemail;
80        gboolean setemail;
81        char *email;
82        gboolean setnick;
83        char *newsn;
84        gboolean chpass;
85        char *oldp;
86        char *newp;
87
88        GSList *oscar_chats;
89
90        gboolean killme;
91        gboolean icq;
92        GSList *evilhack;
93
94        struct {
95                guint maxbuddies; /* max users you can watch */
96                guint maxwatchers; /* max users who can watch you */
97                guint maxpermits; /* max users on permit list */
98                guint maxdenies; /* max users on deny list */
99                guint maxsiglen; /* max size (bytes) of profile */
100                guint maxawaymsglen; /* max size (bytes) of posted away message */
101        } rights;
102};
103
104struct create_room {
105        char *name;
106        int exchange;
107};
108
109struct chat_connection {
110        char *name;
111        char *show; /* AOL did something funny to us */
112        guint16 exchange;
113        guint16 instance;
114        int fd; /* this is redundant since we have the conn below */
115        aim_conn_t *conn;
116        int inpa;
117        int id;
118        struct im_connection *ic; /* i hate this. */
119        struct groupchat *cnv; /* bah. */
120        int maxlen;
121        int maxvis;
122};
123
124struct ask_direct {
125        struct im_connection *ic;
126        char *sn;
127        char ip[64];
128        guint8 cookie[8];
129};
130
131struct icq_auth {
132        struct im_connection *ic;
133        guint32 uin;
134};
135
136static char *extract_name(const char *name) {
137        char *tmp;
138        int i, j;
139        char *x = strchr(name, '-');
140        if (!x) return g_strdup(name);
141        x = strchr(++x, '-');
142        if (!x) return g_strdup(name);
143        tmp = g_strdup(++x);
144
145        for (i = 0, j = 0; x[i]; i++) {
146                char hex[3];
147                if (x[i] != '%') {
148                        tmp[j++] = x[i];
149                        continue;
150                }
151                strncpy(hex, x + ++i, 2); hex[2] = 0;
152                i++;
153                tmp[j++] = (char)strtol(hex, NULL, 16);
154        }
155
156        tmp[j] = 0;
157        return tmp;
158}
159
160static struct chat_connection *find_oscar_chat_by_conn(struct im_connection *ic,
161                                                        aim_conn_t *conn) {
162        GSList *g = ((struct oscar_data *)ic->proto_data)->oscar_chats;
163        struct chat_connection *c = NULL;
164
165        while (g) {
166                c = (struct chat_connection *)g->data;
167                if (c->conn == conn)
168                        break;
169                g = g->next;
170                c = NULL;
171        }
172
173        return c;
174}
175
176static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
177static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
178static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
179static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
180static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
181static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
182static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
183static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
184static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
185static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
186static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
187static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
188static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
189static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
190static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
191static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
192static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
193static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
194static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
195static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
196static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
197static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
198static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
199static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
200static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
201static int gaim_memrequest       (aim_session_t *, aim_frame_t *, ...);
202static int gaim_selfinfo         (aim_session_t *, aim_frame_t *, ...);
203static int gaim_offlinemsg       (aim_session_t *, aim_frame_t *, ...);
204static int gaim_offlinemsgdone   (aim_session_t *, aim_frame_t *, ...);
205static int gaim_ssi_parserights  (aim_session_t *, aim_frame_t *, ...);
206static int gaim_ssi_parselist    (aim_session_t *, aim_frame_t *, ...);
207static int gaim_ssi_parseack     (aim_session_t *, aim_frame_t *, ...);
208static int gaim_parsemtn         (aim_session_t *, aim_frame_t *, ...);
209static int gaim_icqinfo          (aim_session_t *, aim_frame_t *, ...);
210static int gaim_parseaiminfo     (aim_session_t *, aim_frame_t *, ...);
211
212static char *msgerrreason[] = {
213        "Invalid error",
214        "Invalid SNAC",
215        "Rate to host",
216        "Rate to client",
217        "Not logged in",
218        "Service unavailable",
219        "Service not defined",
220        "Obsolete SNAC",
221        "Not supported by host",
222        "Not supported by client",
223        "Refused by client",
224        "Reply too big",
225        "Responses lost",
226        "Request denied",
227        "Busted SNAC payload",
228        "Insufficient rights",
229        "In local permit/deny",
230        "Too evil (sender)",
231        "Too evil (receiver)",
232        "User temporarily unavailable",
233        "No match",
234        "List overflow",
235        "Request ambiguous",
236        "Queue full",
237        "Not while on AOL"
238};
239static int msgerrreasonlen = 25;
240
241static gboolean oscar_callback(gpointer data, gint source,
242                                b_input_condition condition) {
243        aim_conn_t *conn = (aim_conn_t *)data;
244        aim_session_t *sess = aim_conn_getsess(conn);
245        struct im_connection *ic = sess ? sess->aux_data : NULL;
246        struct oscar_data *odata;
247
248        if (!ic) {
249                /* ic is null. we return, else we seg SIGSEG on next line. */
250                return FALSE;
251        }
252     
253        if (!g_slist_find(get_connections(), ic)) {
254                /* oh boy. this is probably bad. i guess the only thing we
255                 * can really do is return? */
256                return FALSE;
257        }
258
259        odata = (struct oscar_data *)ic->proto_data;
260
261        if (condition & GAIM_INPUT_READ) {
262                if (aim_get_command(odata->sess, conn) >= 0) {
263                        aim_rxdispatch(odata->sess);
264                               if (odata->killme)
265                                       imc_logout(ic, TRUE);
266                } else {
267                        if ((conn->type == AIM_CONN_TYPE_BOS) ||
268                                   !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) {
269                                imcb_error(ic, _("Disconnected."));
270                                imc_logout(ic, TRUE);
271                        } else if (conn->type == AIM_CONN_TYPE_CHAT) {
272                                struct chat_connection *c = find_oscar_chat_by_conn(ic, conn);
273                                c->conn = NULL;
274                                if (c->inpa > 0)
275                                        b_event_remove(c->inpa);
276                                c->inpa = 0;
277                                c->fd = -1;
278                                aim_conn_kill(odata->sess, &conn);
279                                imcb_error(sess->aux_data, _("You have been disconnected from chat room %s."), c->name);
280                        } else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
281                                if (odata->cnpa > 0)
282                                        b_event_remove(odata->cnpa);
283                                odata->cnpa = 0;
284                                while (odata->create_rooms) {
285                                        struct create_room *cr = odata->create_rooms->data;
286                                        g_free(cr->name);
287                                        odata->create_rooms =
288                                                g_slist_remove(odata->create_rooms, cr);
289                                        g_free(cr);
290                                        imcb_error(sess->aux_data, _("Chat is currently unavailable"));
291                                }
292                                aim_conn_kill(odata->sess, &conn);
293                        } else if (conn->type == AIM_CONN_TYPE_AUTH) {
294                                if (odata->paspa > 0)
295                                        b_event_remove(odata->paspa);
296                                odata->paspa = 0;
297                                aim_conn_kill(odata->sess, &conn);
298                        } else {
299                                aim_conn_kill(odata->sess, &conn);
300                        }
301                }
302        } else {
303                /* WTF??? */
304                return FALSE;
305        }
306               
307        return TRUE;
308}
309
310static gboolean oscar_login_connect(gpointer data, gint source, b_input_condition cond)
311{
312        struct im_connection *ic = data;
313        struct oscar_data *odata;
314        aim_session_t *sess;
315        aim_conn_t *conn;
316
317        if (!g_slist_find(get_connections(), ic)) {
318                closesocket(source);
319                return FALSE;
320        }
321
322        odata = ic->proto_data;
323        sess = odata->sess;
324        conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
325
326        if (source < 0) {
327                imcb_error(ic, _("Couldn't connect to host"));
328                imc_logout(ic, TRUE);
329                return FALSE;
330        }
331
332        aim_conn_completeconnect(sess, conn);
333        ic->inpa = b_input_add(conn->fd, GAIM_INPUT_READ,
334                        oscar_callback, conn);
335       
336        return FALSE;
337}
338
339static void oscar_init(account_t *acc)
340{
341        set_t *s;
342       
343        s = set_add( &acc->set, "server", 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 = serv_got_joined_chat(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        serv_got_im(ic, userinfo->sn, tmp, flags, time(NULL), -1);
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(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                        serv_got_im(ic, uin, message, 0, time(NULL), -1);
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                        serv_got_im(ic, uin, message, 0, time(NULL), -1);
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                add_chat_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                remove_chat_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        serv_got_chat_in(ccon->cnv, info->sn, 0, tmp, time((time_t)NULL));
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                        serv_got_im(ic, sender, dialog_msg, 0, t, -1);
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                        serv_got_im(ic, sender, dialog_msg, 0, t, -1);
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_send_im(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 = 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) && (!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                                        add_buddy(ic, NULL, curitem->name, realname);
2022                                       
2023                                        if (realname)
2024                                            g_free(realname);
2025                                }
2026                                break;
2027
2028                        case 0x0002: /* Permit buddy */
2029                                if (curitem->name) {
2030                                        GSList *list;
2031                                        for (list=ic->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2032                                        if (!list) {
2033                                                char *name;
2034                                                name = g_strdup(normalize(curitem->name));
2035                                                ic->permit = g_slist_append(ic->permit, name);
2036                                                tmp++;
2037                                        }
2038                                }
2039                                break;
2040
2041                        case 0x0003: /* Deny buddy */
2042                                if (curitem->name) {
2043                                        GSList *list;
2044                                        for (list=ic->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2045                                        if (!list) {
2046                                                char *name;
2047                                                name = g_strdup(normalize(curitem->name));
2048                                                ic->deny = g_slist_append(ic->deny, name);
2049                                                tmp++;
2050                                        }
2051                                }
2052                                break;
2053
2054                        case 0x0004: /* Permit/deny setting */
2055                                if (curitem->data) {
2056                                        guint8 permdeny;
2057                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != ic->permdeny)) {
2058                                                ic->permdeny = permdeny;
2059                                                tmp++;
2060                                        }
2061                                }
2062                                break;
2063
2064                        case 0x0005: /* Presence setting */
2065                                /* We don't want to change Gaim's setting because it applies to all accounts */
2066                                break;
2067                } /* End of switch on curitem->type */
2068        } /* End of for loop */
2069
2070        aim_ssi_enable(sess, fr->conn);
2071       
2072        /* Request offline messages, now that the buddy list is complete. */
2073        aim_icq_reqofflinemsgs(sess);
2074       
2075        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
2076        imcb_connected(ic);
2077       
2078        return 1;
2079}
2080
2081static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2082{
2083        aim_snac_t *origsnac;
2084        va_list ap;
2085
2086        va_start( ap, fr );
2087        origsnac = va_arg( ap, aim_snac_t * );
2088        va_end( ap );
2089       
2090        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2091        {
2092                int i, st, count = aim_bstream_empty( &fr->data );
2093                char *list;
2094               
2095                if( count & 1 )
2096                {
2097                        /* Hmm, the length should be even... */
2098                        imcb_error( sess->aux_data, "Received SSI ACK package with non-even length");
2099                        return( 0 );
2100                }
2101                count >>= 1;
2102               
2103                list = (char *) origsnac->data;
2104                for( i = 0; i < count; i ++ )
2105                {
2106                        st = aimbs_get16( &fr->data );
2107                        if( st == 0x0E )
2108                        {
2109                                imcb_log( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
2110                               
2111                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2112                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2113                        }
2114                        list += strlen( list ) + 1;
2115                }
2116        }
2117       
2118        return( 1 );
2119}
2120
2121static void oscar_set_permit_deny(struct im_connection *ic) {
2122        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2123        if (od->icq) {
2124                GSList *list;
2125                char buf[MAXMSGLEN];
2126                int at;
2127
2128                switch(ic->permdeny) {
2129                case 1:
2130                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, ic->acc->user);
2131                        break;
2132                case 2:
2133                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, ic->acc->user);
2134                        break;
2135                case 3:
2136                        list = ic->permit;
2137                        at = 0;
2138                        while (list) {
2139                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2140                                list = list->next;
2141                        }
2142                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2143                        break;
2144                case 4:
2145                        list = ic->deny;
2146                        at = 0;
2147                        while (list) {
2148                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2149                                list = list->next;
2150                        }
2151                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2152                        break;
2153                        default:
2154                        break;
2155                }
2156        } else {
2157                if (od->sess->ssi.received_data)
2158                        aim_ssi_setpermdeny(od->sess, od->conn, ic->permdeny, 0xffffffff);
2159        }
2160}
2161
2162static void oscar_add_permit(struct im_connection *ic, char *who) {
2163        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2164        if (od->icq) {
2165                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2166        } else {
2167                if (od->sess->ssi.received_data)
2168                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2169        }
2170}
2171
2172static void oscar_add_deny(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, 0, "");
2176        } else {
2177                if (od->sess->ssi.received_data)
2178                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2179        }
2180}
2181
2182static void oscar_rem_permit(struct im_connection *ic, char *who) {
2183        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2184        if (!od->icq) {
2185                if (od->sess->ssi.received_data)
2186                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2187        }
2188}
2189
2190static void oscar_rem_deny(struct im_connection *ic, char *who) {
2191        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2192        if (!od->icq) {
2193                if (od->sess->ssi.received_data)
2194                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2195        }
2196}
2197
2198static GList *oscar_away_states(struct im_connection *ic)
2199{
2200        struct oscar_data *od = ic->proto_data;
2201        GList *m = NULL;
2202
2203        if (!od->icq)
2204                return g_list_append(m, GAIM_AWAY_CUSTOM);
2205
2206        m = g_list_append(m, "Online");
2207        m = g_list_append(m, "Away");
2208        m = g_list_append(m, "Do Not Disturb");
2209        m = g_list_append(m, "Not Available");
2210        m = g_list_append(m, "Occupied");
2211        m = g_list_append(m, "Free For Chat");
2212        m = g_list_append(m, "Invisible");
2213
2214        return m;
2215}
2216
2217static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2218{
2219        struct im_connection *ic = sess->aux_data;
2220        gchar who[16];
2221        GString *str;
2222        va_list ap;
2223        struct aim_icq_info *info;
2224
2225        va_start(ap, fr);
2226        info = va_arg(ap, struct aim_icq_info *);
2227        va_end(ap);
2228
2229        if (!info->uin)
2230                return 0;
2231
2232        str = g_string_sized_new(100);
2233        g_snprintf(who, sizeof(who), "%u", info->uin);
2234
2235        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2236                                info->nick ? info->nick : "-");
2237        info_string_append(str, "\n", _("First Name"), info->first);
2238        info_string_append(str, "\n", _("Last Name"), info->last);
2239                info_string_append(str, "\n", _("Email Address"), info->email);
2240        if (info->numaddresses && info->email2) {
2241                int i;
2242                for (i = 0; i < info->numaddresses; i++) {
2243                                        info_string_append(str, "\n", _("Email Address"), info->email2[i]);
2244                }
2245        }
2246        info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
2247        info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : info->gender==2 ? _("Male") : _("Unknown"));
2248        if (info->birthyear || info->birthmonth || info->birthday) {
2249                char date[30];
2250                struct tm tm;
2251                tm.tm_mday = (int)info->birthday;
2252                tm.tm_mon = (int)info->birthmonth-1;
2253                tm.tm_year = (int)info->birthyear%100;
2254                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2255                info_string_append(str, "\n", _("Birthday"), date);
2256        }
2257        if (info->age) {
2258                char age[5];
2259                g_snprintf(age, sizeof(age), "%hhd", info->age);
2260                info_string_append(str, "\n", _("Age"), age);
2261        }
2262                info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
2263        if (info->info && info->info[0]) {
2264                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2265                                                info->info, _("End of Additional Information"));
2266        }
2267        g_string_sprintfa(str, "\n");
2268        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2269                g_string_sprintfa(str, "%s:", _("Home Address"));
2270                info_string_append(str, "\n", _("Address"), info->homeaddr);
2271                info_string_append(str, "\n", _("City"), info->homecity);
2272                info_string_append(str, "\n", _("State"), info->homestate); 
2273                                info_string_append(str, "\n", _("Zip Code"), info->homezip);
2274                g_string_sprintfa(str, "\n");
2275        }
2276        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2277                g_string_sprintfa(str, "%s:", _("Work Address"));
2278                info_string_append(str, "\n", _("Address"), info->workaddr);
2279                info_string_append(str, "\n", _("City"), info->workcity);
2280                info_string_append(str, "\n", _("State"), info->workstate);
2281                                info_string_append(str, "\n", _("Zip Code"), info->workzip);
2282                g_string_sprintfa(str, "\n");
2283        }
2284        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2285                g_string_sprintfa(str, "%s:", _("Work Information"));
2286                info_string_append(str, "\n", _("Company"), info->workcompany);
2287                info_string_append(str, "\n", _("Division"), info->workdivision);
2288                info_string_append(str, "\n", _("Position"), info->workposition);
2289                if (info->workwebpage && info->workwebpage[0]) {
2290                        info_string_append(str, "\n", _("Web Page"), info->workwebpage);
2291                }
2292                g_string_sprintfa(str, "\n");
2293        }
2294
2295                imcb_log(ic, "%s\n%s", _("User Info"), str->str);
2296        g_string_free(str, TRUE);
2297
2298        return 1;
2299
2300}
2301
2302static char *oscar_encoding_extract(const char *encoding)
2303{
2304        char *ret = NULL;
2305        char *begin, *end;
2306
2307        g_return_val_if_fail(encoding != NULL, NULL);
2308
2309        /* Make sure encoding begins with charset= */
2310        if (strncmp(encoding, "text/plain; charset=", 20) &&
2311                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2312                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2313        {
2314                return NULL;
2315        }
2316
2317        begin = strchr(encoding, '"');
2318        end = strrchr(encoding, '"');
2319
2320        if ((begin == NULL) || (end == NULL) || (begin >= end))
2321                return NULL;
2322
2323        ret = g_strndup(begin+1, (end-1) - begin);
2324
2325        return ret;
2326}
2327
2328static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2329{
2330        char *utf8 = g_new0(char, 8192);
2331
2332        if ((encoding == NULL) || encoding[0] == '\0') {
2333                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2334        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2335                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2336        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2337                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2338        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2339                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2340        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2341                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2342                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2343                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2344        }
2345
2346        /*
2347         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2348         * we have been unable to convert the text to utf-8 from the encoding
2349         * that was specified.  So we assume it's UTF-8 and hope for the best.
2350         */
2351        if (*utf8 == 0) {
2352            strncpy(utf8, text, textlen);
2353        }
2354
2355        return utf8;
2356}
2357
2358static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2359{
2360        struct im_connection *ic = sess->aux_data;
2361        va_list ap;
2362        aim_userinfo_t *userinfo;
2363        guint16 infotype;
2364        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2365        guint16 text_length;
2366        char *utf8 = NULL;
2367
2368        va_start(ap, fr);
2369        userinfo = va_arg(ap, aim_userinfo_t *);
2370        infotype = va_arg(ap, int);
2371        text_encoding = va_arg(ap, char*);
2372        text = va_arg(ap, char*);
2373        text_length = va_arg(ap, int);
2374        va_end(ap);
2375
2376        if(text_encoding)
2377                extracted_encoding = oscar_encoding_extract(text_encoding);
2378        if(infotype == AIM_GETINFO_GENERALINFO) {
2379                /*Display idle time*/
2380                char buff[256];
2381                struct tm idletime;
2382                if(userinfo->idletime) {
2383                        memset(&idletime, 0, sizeof(struct tm));
2384                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2385                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2386                        idletime.tm_min = userinfo->idletime % 60;
2387                        idletime.tm_sec = 0;
2388                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
2389                        imcb_log(ic, "%s: %s", _("Idle Time"), buff);
2390                }
2391               
2392                if(text) {
2393                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2394                        imcb_log(ic, "%s\n%s", _("User Info"), utf8);
2395                } else {
2396                        imcb_log(ic, _("No user info available."));
2397                }
2398        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2399                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2400                imcb_log(ic, "%s\n%s", _("Away Message"), utf8);
2401        }
2402
2403        g_free(utf8);
2404   
2405        return 1;
2406}
2407
2408int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)
2409{
2410        struct im_connection * ic = sess->aux_data;
2411        va_list ap;
2412        guint16 type1, type2;
2413        char * sn;
2414
2415        va_start(ap, fr);
2416        type1 = va_arg(ap, int);
2417        sn = va_arg(ap, char*);
2418        type2 = va_arg(ap, int);
2419        va_end(ap);
2420   
2421        if(type2 == 0x0002) {
2422                /* User is typing */
2423                serv_got_typing(ic, sn, 0, 1);
2424        } 
2425        else if (type2 == 0x0001) {
2426                /* User has typed something, but is not actively typing (stale) */
2427                serv_got_typing(ic, sn, 0, 2);
2428        }
2429        else {
2430                /* User has stopped typing */
2431                serv_got_typing(ic, sn, 0, 0);
2432        }       
2433       
2434        return 1;
2435}
2436
2437int oscar_send_typing(struct im_connection *ic, char * who, int typing)
2438{
2439        struct oscar_data *od = ic->proto_data;
2440        return( aim_im_sendmtn(od->sess, 1, who, typing ? 0x0002 : 0x0000) );
2441}
2442
2443void oscar_chat_send(struct groupchat *c, char *message, int msgflags)
2444{
2445        struct im_connection *ic = c->ic;
2446        struct oscar_data * od = (struct oscar_data*)ic->proto_data;
2447        struct chat_connection * ccon;
2448        int ret;
2449        guint8 len = strlen(message);
2450        guint16 flags;
2451        char *s;
2452       
2453        ccon = c->data;
2454               
2455        for (s = message; *s; s++)
2456                if (*s & 128)
2457                        break;
2458       
2459        flags = AIM_CHATFLAGS_NOREFLECT;
2460       
2461        /* Message contains high ASCII chars, time for some translation! */
2462        if (*s) {
2463                s = g_malloc(BUF_LONG);
2464                /* Try if we can put it in an ISO8859-1 string first.
2465                   If we can't, fall back to UTF16. */
2466                if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
2467                        flags |= AIM_CHATFLAGS_ISO_8859_1;
2468                        len = ret;
2469                } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
2470                        flags |= AIM_CHATFLAGS_UNICODE;
2471                        len = ret;
2472                } else {
2473                        /* OOF, translation failed... Oh well.. */
2474                        g_free( s );
2475                        s = message;
2476                }
2477        } else {
2478                s = message;
2479        }
2480               
2481        ret = aim_chat_send_im(od->sess, ccon->conn, flags, s, len);
2482               
2483        if (s != message) {     
2484                g_free(s);
2485  }
2486 
2487/*  return (ret >= 0); */
2488}
2489
2490void oscar_chat_invite(struct groupchat *c, char *message, char *who)
2491{
2492        struct im_connection *ic = c->ic;
2493        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2494        struct chat_connection *ccon = c->data;
2495       
2496        aim_chat_invite(od->sess, od->conn, who, message ? message : "",
2497                                        ccon->exchange, ccon->name, 0x0);
2498}
2499
2500void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc)
2501{
2502        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
2503
2504        /* Notify the conversation window that we've left the chat */
2505        serv_got_chat_left(cc->cnv);
2506
2507        /* Destroy the chat_connection */
2508        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
2509        if (cc->inpa > 0)
2510                b_event_remove(cc->inpa);
2511        aim_conn_kill(od->sess, &cc->conn);
2512        g_free(cc->name);
2513        g_free(cc->show);
2514        g_free(cc);
2515}
2516
2517void oscar_chat_leave(struct groupchat *c)
2518{
2519        oscar_chat_kill(c->ic, c->data);
2520}
2521
2522int oscar_chat_join(struct im_connection * ic, char * name)
2523{
2524        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2525       
2526        aim_conn_t * cur;
2527
2528        if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
2529       
2530                return (aim_chatnav_createroom(od->sess, cur, name, 4) == 0);
2531       
2532        } else {
2533                struct create_room * cr = g_new0(struct create_room, 1);
2534                cr->exchange = 4;
2535                cr->name = g_strdup(name);
2536                od->create_rooms = g_slist_append(od->create_rooms, cr);
2537                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
2538                return 1;
2539        }
2540}
2541
2542struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)
2543{
2544        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
2545        int ret;
2546        static int chat_id = 0;
2547        char * chatname;
2548       
2549        chatname = g_strdup_printf("%s%d", ic->acc->user, chat_id++);
2550 
2551        ret = oscar_chat_join(ic, chatname);
2552
2553        aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);
2554
2555        g_free(chatname);
2556       
2557        return NULL;
2558}
2559
2560void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
2561{
2562        oscar_chat_join(inv->ic, inv->name);
2563        g_free(inv->name);
2564        g_free(inv);
2565}
2566
2567void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
2568{
2569        g_free(inv->name);
2570        g_free(inv);
2571}
2572
2573void oscar_initmodule() 
2574{
2575        struct prpl *ret = g_new0(struct prpl, 1);
2576        ret->name = "oscar";
2577        ret->away_states = oscar_away_states;
2578        ret->init = oscar_init;
2579        ret->login = oscar_login;
2580        ret->keepalive = oscar_keepalive;
2581        ret->logout = oscar_logout;
2582        ret->send_im = oscar_send_im;
2583        ret->get_info = oscar_get_info;
2584        ret->set_away = oscar_set_away;
2585        ret->get_away = oscar_get_away;
2586        ret->add_buddy = oscar_add_buddy;
2587        ret->remove_buddy = oscar_remove_buddy;
2588        ret->chat_send = oscar_chat_send;
2589        ret->chat_invite = oscar_chat_invite;
2590        ret->chat_leave = oscar_chat_leave;
2591        ret->chat_with = oscar_chat_with;
2592        ret->add_permit = oscar_add_permit;
2593        ret->add_deny = oscar_add_deny;
2594        ret->rem_permit = oscar_rem_permit;
2595        ret->rem_deny = oscar_rem_deny;
2596        ret->set_permit_deny = oscar_set_permit_deny;
2597        ret->send_typing = oscar_send_typing;
2598       
2599        ret->handle_cmp = aim_sncmp;
2600
2601        register_protocol(ret);
2602}
Note: See TracBrowser for help on using the repository browser.