source: protocols/oscar/oscar.c @ b5cfc2b

Last change on this file since b5cfc2b was 9143aeb, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-04-05T12:26:04Z

query.h now defines a callback function type, not using void* for it anymore.
Got rid of the bogus window handler pointer as the first argument to the
callback.

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