source: protocols/oscar/oscar.c @ 704dd38

Last change on this file since 704dd38 was 704dd38, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-01T21:51:27Z

Merging head.

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