source: protocols/oscar/oscar.c @ cd4723c

Last change on this file since cd4723c was cd4723c, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-04-15T21:59:52Z

More format string problems.

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