source: protocols/oscar/oscar.c @ c0c43fb

Last change on this file since c0c43fb was c0c43fb, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-12-14T10:31:49Z

Fixed ic->away leaking memory. This var is only used by OSCAR and should
maybe be killed. Also fixed some completely broken indentation in those
functions.

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