source: protocols/oscar/oscar.c @ 35529ae

Last change on this file since 35529ae was fab3d2d, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-06-22T13:02:15Z

Shut up a "mostly harmless" warning (this NULL would never actually be
dereferenced as far as I can see).

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