source: protocols/oscar/oscar.c @ e774815

Last change on this file since e774815 was ec86b22, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-15T23:28:16Z

Mainline merge. (Probably mostly irrelevant for this branch, oh well.)

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