source: protocols/oscar/oscar.c @ 17f9522

Last change on this file since 17f9522 was 17f9522, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-06T16:17:23Z

OSCAR cleanup.

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