source: protocols/oscar/oscar.c @ 5a673f3

Last change on this file since 5a673f3 was 5a673f3, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-09T13:04:45Z

Pick up buddy group information from OSCAR server-side contact list.

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