source: protocols/oscar/oscar.c @ 83ba3e5

Last change on this file since 83ba3e5 was e64de00, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-01-12T00:24:46Z

Killed info_string_append() and now showing the IP address of ICQ users
in the "info" command response.

  • 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 {
1068                int i;
1069               
1070                *tmp = 0;
1071                for (i = 0; i < args->mpmsg.numparts; i ++) {
1072                        g_strlcat(tmp, (char*) args->mpmsg.parts[i].data, BUF_LONG);
1073                        g_strlcat(tmp, "\n", BUF_LONG);
1074                }
1075        }
[b7d3cc34]1076       
1077        strip_linefeed(tmp);
[9624fdf]1078        imcb_buddy_msg(ic, userinfo->sn, tmp, flags, 0);
[b7d3cc34]1079        g_free(tmp);
1080       
1081        return 1;
1082}
1083
[b8ef1b1]1084void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv);
1085void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv);
1086       
[b7d3cc34]1087static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
[0da65d5]1088        struct im_connection *ic = sess->aux_data;
[b7d3cc34]1089
1090        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1091                return 1;
[b8ef1b1]1092
[b7d3cc34]1093        if (args->reqclass & AIM_CAPS_CHAT) {
1094                char *name = extract_name(args->info.chat.roominfo.name);
1095                int *exch = g_new0(int, 1);
1096                GList *m = NULL;
[11e090b]1097                char txt[1024];
1098                struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1);
1099
[b7d3cc34]1100                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1101                *exch = args->info.chat.roominfo.exchange;
1102                m = g_list_append(m, exch);
[b8ef1b1]1103
1104                g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg );
1105
[0da65d5]1106                inv->ic = ic;
[b8ef1b1]1107                inv->exchange = *exch;
1108                inv->name = g_strdup(name);
1109               
[84b045d]1110                imcb_ask( ic, txt, inv, oscar_accept_chat, oscar_reject_chat);
[b8ef1b1]1111       
[b7d3cc34]1112                if (name)
1113                        g_free(name);
1114        }
[b8ef1b1]1115
[b7d3cc34]1116        return 1;
1117}
1118
1119static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
1120        char *uin, message;
[0da65d5]1121        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
[b7d3cc34]1122       
1123        uin = g_strdup_printf("%u", data->uin);
1124        message = 0;
1125        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1126        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
[f0cb961]1127        if(imcb_find_buddy(data->ic, uin) == NULL)
[84b045d]1128                imcb_ask_add(data->ic, uin, NULL);
[b7d3cc34]1129       
1130        g_free(uin);
1131        g_free(data);
1132}
1133
1134static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
1135        char *uin, *message;
[0da65d5]1136        struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
[b7d3cc34]1137       
1138        uin = g_strdup_printf("%u", data->uin);
1139        message = g_strdup_printf("No reason given.");
1140        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1141        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1142        g_free(message);
1143       
1144        g_free(uin);
1145        g_free(data);
1146}
1147
1148/*
1149 * For when other people ask you for authorization
1150 */
[0da65d5]1151static void gaim_icq_authask(struct im_connection *ic, guint32 uin, char *msg) {
[b7d3cc34]1152        struct icq_auth *data = g_new(struct icq_auth, 1);
1153        char *reason = NULL;
1154        char *dialog_msg;
1155       
1156        if (strlen(msg) > 6)
1157                reason = msg + 6;
1158       
[aefa533e]1159        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]1160        data->ic = ic;
[b7d3cc34]1161        data->uin = uin;
[84b045d]1162        imcb_ask(ic, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
[b7d3cc34]1163        g_free(dialog_msg);
1164}
1165
1166static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
[0da65d5]1167        struct im_connection *ic = sess->aux_data;
[b7d3cc34]1168
1169        switch (args->type) {
1170                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1171                        char *uin, *message;
1172                        uin = g_strdup_printf("%u", args->uin);
1173                        message = g_strdup(args->msg);
1174                        strip_linefeed(message);
[9624fdf]1175                        imcb_buddy_msg(ic, uin, message, 0, 0);
[b7d3cc34]1176                        g_free(uin);
1177                        g_free(message);
1178                } break;
1179
1180                case 0x0004: { /* Someone sent you a URL */
1181                        char *uin, *message;
1182                        char **m;
1183       
1184                        uin = g_strdup_printf("%u", args->uin);
1185                        m = g_strsplit(args->msg, "\376", 2);
1186
1187                        if ((strlen(m[0]) != 0)) {
1188                          message = g_strjoinv(" -- ", m);
1189                        } else {
1190                          message = m[1];
1191                        }
1192
1193                        strip_linefeed(message);
[9624fdf]1194                        imcb_buddy_msg(ic, uin, message, 0, 0);
[b7d3cc34]1195                        g_free(uin);
1196                        g_free(m);
1197                        g_free(message);
1198                } break;
1199               
1200                case 0x0006: { /* Someone requested authorization */
[0da65d5]1201                        gaim_icq_authask(ic, args->uin, args->msg);
[b7d3cc34]1202                } break;
1203
1204                case 0x0007: { /* Someone has denied you authorization */
[84b045d]1205                        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]1206                } break;
1207
1208                case 0x0008: { /* Someone has granted you authorization */
[84b045d]1209                        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]1210                } break;
1211
1212                case 0x0012: {
1213                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1214                } break;
1215
1216                default: {;
1217                } break;
1218        }
1219
1220        return 1;
1221}
[9cb9868]1222
[b7d3cc34]1223static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1224        int channel, ret = 0;
1225        aim_userinfo_t *userinfo;
1226        va_list ap;
1227
1228        va_start(ap, fr);
1229        channel = va_arg(ap, int);
1230        userinfo = va_arg(ap, aim_userinfo_t *);
1231
1232        switch (channel) {
1233                case 1: { /* standard message */
1234                        struct aim_incomingim_ch1_args *args;
1235                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1236                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1237                } break;
1238
1239                case 2: { /* rendevous */
1240                        struct aim_incomingim_ch2_args *args;
1241                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1242                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1243                } break;
1244
1245                case 4: { /* ICQ */
1246                        struct aim_incomingim_ch4_args *args;
1247                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1248                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1249                } break;
1250
1251                default: {;
1252                } break;
1253        }
1254
1255        va_end(ap);
1256
1257        return ret;
1258}
1259
1260static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1261        va_list ap;
1262        guint16 chan, nummissed, reason;
1263        aim_userinfo_t *userinfo;
1264
1265        va_start(ap, fr);
1266        chan = (guint16)va_arg(ap, unsigned int);
1267        userinfo = va_arg(ap, aim_userinfo_t *);
1268        nummissed = (guint16)va_arg(ap, unsigned int);
1269        reason = (guint16)va_arg(ap, unsigned int);
1270        va_end(ap);
1271
1272        switch(reason) {
1273                case 0:
1274                        /* Invalid (0) */
[84b045d]1275                        imcb_error(sess->aux_data,
[b7d3cc34]1276                                   nummissed == 1 ? 
1277                                   _("You missed %d message from %s because it was invalid.") :
1278                                   _("You missed %d messages from %s because they were invalid."),
1279                                   nummissed,
1280                                   userinfo->sn);
1281                        break;
1282                case 1:
1283                        /* Message too large */
[84b045d]1284                        imcb_error(sess->aux_data,
[b7d3cc34]1285                                   nummissed == 1 ?
1286                                   _("You missed %d message from %s because it was too large.") :
1287                                   _("You missed %d messages from %s because they were too large."),
1288                                   nummissed,
1289                                   userinfo->sn);
1290                        break;
1291                case 2:
1292                        /* Rate exceeded */
[84b045d]1293                        imcb_error(sess->aux_data,
[b7d3cc34]1294                                   nummissed == 1 ? 
1295                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1296                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1297                                   nummissed,
1298                                   userinfo->sn);
1299                        break;
1300                case 3:
1301                        /* Evil Sender */
[84b045d]1302                        imcb_error(sess->aux_data,
[b7d3cc34]1303                                   nummissed == 1 ?
1304                                   _("You missed %d message from %s because it was too evil.") : 
1305                                   _("You missed %d messages from %s because they are too evil."),
1306                                   nummissed,
1307                                   userinfo->sn);
1308                        break;
1309                case 4:
1310                        /* Evil Receiver */
[84b045d]1311                        imcb_error(sess->aux_data,
[b7d3cc34]1312                                   nummissed == 1 ? 
1313                                   _("You missed %d message from %s because you are too evil.") :
1314                                   _("You missed %d messages from %s because you are too evil."),
1315                                   nummissed,
1316                                   userinfo->sn);
1317                        break;
1318                default:
[84b045d]1319                        imcb_error(sess->aux_data,
[b7d3cc34]1320                                   nummissed == 1 ? 
1321                                   _("You missed %d message from %s for unknown reasons.") :
1322                                   _("You missed %d messages from %s for unknown reasons."),
1323                                   nummissed,
1324                                   userinfo->sn);
1325                        break;
1326        }
1327
1328        return 1;
1329}
1330
1331static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1332        va_list ap;
1333        guint16 reason;
1334
1335        va_start(ap, fr);
1336        reason = (guint16)va_arg(ap, unsigned int);
1337        va_end(ap);
1338
[84b045d]1339        imcb_error(sess->aux_data, _("SNAC threw error: %s"),
[aef4828]1340                  reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
[b7d3cc34]1341
1342        return 1;
1343}
1344
1345static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1346        va_list ap;
1347        char *destn;
1348        guint16 reason;
1349
1350        va_start(ap, fr);
1351        reason = (guint16)va_arg(ap, unsigned int);
1352        destn = va_arg(ap, char *);
1353        va_end(ap);
1354
[84b045d]1355        imcb_error(sess->aux_data, _("Your message to %s did not get sent: %s"), destn,
[b7d3cc34]1356                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1357
1358        return 1;
1359}
1360
1361static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1362        va_list ap;
1363        char *destn;
1364        guint16 reason;
1365
1366        va_start(ap, fr);
1367        reason = (guint16)va_arg(ap, unsigned int);
1368        destn = va_arg(ap, char *);
1369        va_end(ap);
1370
[84b045d]1371        imcb_error(sess->aux_data, _("User information for %s unavailable: %s"), destn,
[b7d3cc34]1372                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1373
1374        return 1;
1375}
1376
1377static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1378        char *msg;
1379        guint16 id;
1380        va_list ap;
1381
1382        va_start(ap, fr);
1383        id  = (guint16)va_arg(ap, unsigned int);
1384        msg = va_arg(ap, char *);
1385        va_end(ap);
1386
1387        if (id < 4)
[84b045d]1388                imcb_error(sess->aux_data, _("Your connection may be lost."));
[b7d3cc34]1389
1390        return 1;
1391}
1392
1393static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1394        va_list ap;
1395        guint16 type;
[0da65d5]1396        struct im_connection *ic = sess->aux_data;
1397        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1398
1399        va_start(ap, fr);
1400        type = (guint16)va_arg(ap, unsigned int);
1401
1402        switch(type) {
1403                case 0x0002: {
1404                        guint8 maxrooms;
1405                        struct aim_chat_exchangeinfo *exchanges;
1406                        int exchangecount; // i;
1407
1408                        maxrooms = (guint8)va_arg(ap, unsigned int);
1409                        exchangecount = va_arg(ap, int);
1410                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1411                        va_end(ap);
1412
1413                        while (odata->create_rooms) {
1414                                struct create_room *cr = odata->create_rooms->data;
1415                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1416                                g_free(cr->name);
1417                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1418                                g_free(cr);
1419                        }
1420                        }
1421                        break;
1422                case 0x0008: {
1423                        char *fqcn, *name, *ck;
1424                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1425                        guint8 createperms;
1426                        guint32 createtime;
1427
1428                        fqcn = va_arg(ap, char *);
1429                        instance = (guint16)va_arg(ap, unsigned int);
1430                        exchange = (guint16)va_arg(ap, unsigned int);
1431                        flags = (guint16)va_arg(ap, unsigned int);
1432                        createtime = va_arg(ap, guint32);
1433                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1434                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1435                        createperms = (guint8)va_arg(ap, int);
1436                        unknown = (guint16)va_arg(ap, unsigned int);
1437                        name = va_arg(ap, char *);
1438                        ck = va_arg(ap, char *);
1439                        va_end(ap);
1440
1441                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1442                        }
1443                        break;
1444                default:
1445                        va_end(ap);
1446                        break;
1447        }
1448        return 1;
1449}
1450
1451static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1452        va_list ap;
1453        int count, i;
1454        aim_userinfo_t *info;
[0da65d5]1455        struct im_connection *g = sess->aux_data;
[b7d3cc34]1456
1457        struct chat_connection *c = NULL;
1458
1459        va_start(ap, fr);
1460        count = va_arg(ap, int);
1461        info  = va_arg(ap, aim_userinfo_t *);
1462        va_end(ap);
1463
1464        c = find_oscar_chat_by_conn(g, fr->conn);
1465        if (!c)
1466                return 1;
1467
1468        for (i = 0; i < count; i++)
[61ae52c]1469                imcb_chat_add_buddy(c->cnv, info[i].sn);
[b7d3cc34]1470
1471        return 1;
1472}
1473
1474static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1475        va_list ap;
1476        int count, i;
1477        aim_userinfo_t *info;
[0da65d5]1478        struct im_connection *g = sess->aux_data;
[b7d3cc34]1479
1480        struct chat_connection *c = NULL;
1481
1482        va_start(ap, fr);
1483        count = va_arg(ap, int);
1484        info  = va_arg(ap, aim_userinfo_t *);
1485        va_end(ap);
1486
1487        c = find_oscar_chat_by_conn(g, fr->conn);
1488        if (!c)
1489                return 1;
1490
1491        for (i = 0; i < count; i++)
[61ae52c]1492                imcb_chat_remove_buddy(c->cnv, info[i].sn, NULL);
[b7d3cc34]1493
1494        return 1;
1495}
1496
1497static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1498        va_list ap;
1499        aim_userinfo_t *userinfo;
1500        struct aim_chat_roominfo *roominfo;
1501        char *roomname;
1502        int usercount;
1503        char *roomdesc;
1504        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1505        guint32 creationtime;
[0da65d5]1506        struct im_connection *ic = sess->aux_data;
1507        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
[b7d3cc34]1508
1509        va_start(ap, fr);
1510        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1511        roomname = va_arg(ap, char *);
1512        usercount= va_arg(ap, int);
1513        userinfo = va_arg(ap, aim_userinfo_t *);
1514        roomdesc = va_arg(ap, char *);
1515        unknown_c9 = (guint16)va_arg(ap, int);
1516        creationtime = (guint32)va_arg(ap, unsigned long);
1517        maxmsglen = (guint16)va_arg(ap, int);
1518        unknown_d2 = (guint16)va_arg(ap, int);
1519        unknown_d5 = (guint16)va_arg(ap, int);
1520        maxvisiblemsglen = (guint16)va_arg(ap, int);
1521        va_end(ap);
1522
1523        ccon->maxlen = maxmsglen;
1524        ccon->maxvis = maxvisiblemsglen;
1525
1526        return 1;
1527}
1528
1529static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1530        va_list ap;
1531        aim_userinfo_t *info;
1532        char *msg;
[0da65d5]1533        struct im_connection *ic = sess->aux_data;
1534        struct chat_connection *ccon = find_oscar_chat_by_conn(ic, fr->conn);
[b7d3cc34]1535        char *tmp;
1536
1537        va_start(ap, fr);
1538        info = va_arg(ap, aim_userinfo_t *);
1539        msg  = va_arg(ap, char *);
1540
1541        tmp = g_malloc(BUF_LONG);
1542        g_snprintf(tmp, BUF_LONG, "%s", msg);
[61ae52c]1543        imcb_chat_msg(ccon->cnv, info->sn, tmp, 0, 0);
[b7d3cc34]1544        g_free(tmp);
1545
1546        return 1;
1547}
1548
1549static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1550#if 0
1551        static const char *codes[5] = {
1552                "invalid",
1553                 "change",
1554                 "warning",
1555                 "limit",
1556                 "limit cleared",
1557        };
1558#endif
1559        va_list ap;
1560        guint16 code, rateclass;
1561        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1562
1563        va_start(ap, fr); 
1564        code = (guint16)va_arg(ap, unsigned int);
1565        rateclass= (guint16)va_arg(ap, unsigned int);
1566        windowsize = (guint32)va_arg(ap, unsigned long);
1567        clear = (guint32)va_arg(ap, unsigned long);
1568        alert = (guint32)va_arg(ap, unsigned long);
1569        limit = (guint32)va_arg(ap, unsigned long);
1570        disconnect = (guint32)va_arg(ap, unsigned long);
1571        currentavg = (guint32)va_arg(ap, unsigned long);
1572        maxavg = (guint32)va_arg(ap, unsigned long);
1573        va_end(ap);
1574
1575        /* XXX fix these values */
1576        if (code == AIM_RATE_CODE_CHANGE) {
1577                if (currentavg >= clear)
1578                        aim_conn_setlatency(fr->conn, 0);
1579        } else if (code == AIM_RATE_CODE_WARNING) {
1580                aim_conn_setlatency(fr->conn, windowsize/4);
1581        } else if (code == AIM_RATE_CODE_LIMIT) {
[84b045d]1582                imcb_error(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
[aef4828]1583                          "Please wait 10 seconds and try again."));
[b7d3cc34]1584                aim_conn_setlatency(fr->conn, windowsize/2);
1585        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1586                aim_conn_setlatency(fr->conn, 0);
1587        }
1588
1589        return 1;
1590}
1591
1592static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1593        va_list ap;
1594        aim_userinfo_t *info;
[0da65d5]1595        struct im_connection *ic = sess->aux_data;
[b7d3cc34]1596
1597        va_start(ap, fr);
1598        info = va_arg(ap, aim_userinfo_t *);
1599        va_end(ap);
1600
[0da65d5]1601        ic->evil = info->warnlevel/10;
1602        /* ic->correction_time = (info->onlinesince - ic->login_time); */
[b7d3cc34]1603
1604        return 1;
1605}
1606
1607static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1608
1609        aim_reqpersonalinfo(sess, fr->conn);
1610        aim_bos_reqlocaterights(sess, fr->conn);
1611        aim_bos_reqbuddyrights(sess, fr->conn);
1612
1613        aim_reqicbmparams(sess);
1614
1615        aim_bos_reqrights(sess, fr->conn);
1616        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1617        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1618                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1619
1620        return 1;
1621}
1622
1623static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
[0da65d5]1624        struct im_connection *ic = sess->aux_data;
1625        struct oscar_data *od = ic->proto_data;
[b7d3cc34]1626
1627        aim_clientready(sess, fr->conn);
1628
1629        if (od->chpass) {
1630                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1631                g_free(od->oldp);
1632                od->oldp = NULL;
1633                g_free(od->newp);
1634                od->newp = NULL;
1635                od->chpass = FALSE;
1636        }
1637        if (od->setnick) {
1638                aim_admin_setnick(sess, fr->conn, od->newsn);
1639                g_free(od->newsn);
1640                od->newsn = NULL;
1641                od->setnick = FALSE;
1642        }
1643        if (od->conf) {
1644                aim_admin_reqconfirm(sess, fr->conn);
1645                od->conf = FALSE;
1646        }
1647        if (od->reqemail) {
1648                aim_admin_getinfo(sess, fr->conn, 0x0011);
1649                od->reqemail = FALSE;
1650        }
1651        if (od->setemail) {
1652                aim_admin_setemail(sess, fr->conn, od->email);
1653                g_free(od->email);
1654                od->setemail = FALSE;
1655        }
1656
1657        return 1;
1658}
1659
1660static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1661        struct aim_icbmparameters *params;
1662        va_list ap;
1663
1664        va_start(ap, fr);
1665        params = va_arg(ap, struct aim_icbmparameters *);
1666        va_end(ap);
1667
1668        /* Maybe senderwarn and recverwarn should be user preferences... */
[3e1de87]1669        params->flags = 0x0000000b;
[b7d3cc34]1670        params->maxmsglen = 8000;
1671        params->minmsginterval = 0;
1672
1673        aim_seticbmparam(sess, params);
1674
1675        return 1;
1676}
1677
1678static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1679{
1680        va_list ap;
1681        guint16 maxsiglen;
[0da65d5]1682        struct im_connection *ic = sess->aux_data;
1683        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1684
1685        va_start(ap, fr);
1686        maxsiglen = va_arg(ap, int);
1687        va_end(ap);
1688
1689        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1690
[0a3c243]1691        /* FIXME: It seems we're not really using this, and it broke now that
1692           struct aim_user is dead.
[0da65d5]1693        aim_bos_setprofile(sess, fr->conn, ic->user->user_info, NULL, gaim_caps);
[0a3c243]1694        */
1695       
[b7d3cc34]1696        return 1;
1697}
1698
1699static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1700        va_list ap;
1701        guint16 maxbuddies, maxwatchers;
[0da65d5]1702        struct im_connection *ic = sess->aux_data;
1703        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1704
1705        va_start(ap, fr);
1706        maxbuddies = (guint16)va_arg(ap, unsigned int);
1707        maxwatchers = (guint16)va_arg(ap, unsigned int);
1708        va_end(ap);
1709
1710        odata->rights.maxbuddies = (guint)maxbuddies;
1711        odata->rights.maxwatchers = (guint)maxwatchers;
1712
1713        return 1;
1714}
1715
1716static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1717        guint16 maxpermits, maxdenies;
1718        va_list ap;
[0da65d5]1719        struct im_connection *ic = sess->aux_data;
1720        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1721
1722        va_start(ap, fr);
1723        maxpermits = (guint16)va_arg(ap, unsigned int);
1724        maxdenies = (guint16)va_arg(ap, unsigned int);
1725        va_end(ap);
1726
1727        odata->rights.maxpermits = (guint)maxpermits;
1728        odata->rights.maxdenies = (guint)maxdenies;
1729
1730        aim_clientready(sess, fr->conn);
1731
1732        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1733
1734        aim_ssi_reqrights(sess, fr->conn);
1735        aim_ssi_reqalldata(sess, fr->conn);
1736
1737        return 1;
1738}
1739
1740static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1741        va_list ap;
1742        struct aim_icq_offlinemsg *msg;
[0da65d5]1743        struct im_connection *ic = sess->aux_data;
[b7d3cc34]1744
1745        va_start(ap, fr);
1746        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1747        va_end(ap);
1748
1749        switch (msg->type) {
1750                case 0x0001: { /* Basic offline message */
1751                        char sender[32];
1752                        char *dialog_msg = g_strdup(msg->msg);
1753                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1754                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1755                        strip_linefeed(dialog_msg);
[9624fdf]1756                        imcb_buddy_msg(ic, sender, dialog_msg, 0, t);
[b7d3cc34]1757                        g_free(dialog_msg);
1758                } break;
1759
1760                case 0x0004: { /* Someone sent you a URL */
1761                        char sender[32];
1762                        char *dialog_msg;
1763                        char **m;
1764
1765                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1766                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1767
1768                        m = g_strsplit(msg->msg, "\376", 2);
1769
1770                        if ((strlen(m[0]) != 0)) {
1771                          dialog_msg = g_strjoinv(" -- ", m);
1772                        } else {
1773                          dialog_msg = m[1];
1774                        }
1775
1776                        strip_linefeed(dialog_msg);
[9624fdf]1777                        imcb_buddy_msg(ic, sender, dialog_msg, 0, t);
[b7d3cc34]1778                        g_free(dialog_msg);
1779                        g_free(m);
1780                } break;
1781               
1782                case 0x0006: { /* Authorization request */
[0da65d5]1783                        gaim_icq_authask(ic, msg->sender, msg->msg);
[b7d3cc34]1784                } break;
1785
1786                case 0x0007: { /* Someone has denied you authorization */
[84b045d]1787                        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]1788                } break;
1789
1790                case 0x0008: { /* Someone has granted you authorization */
[84b045d]1791                        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]1792                } break;
1793
1794                case 0x0012: {
1795                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1796                } break;
1797
1798                default: {;
1799                }
1800        }
1801
1802        return 1;
1803}
1804
1805static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1806{
1807        aim_icq_ackofflinemsgs(sess);
1808        return 1;
1809}
1810
[0da65d5]1811static void oscar_keepalive(struct im_connection *ic) {
1812        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1813        aim_flap_nop(odata->sess, odata->conn);
1814}
1815
[f6c963b]1816static int oscar_buddy_msg(struct im_connection *ic, char *name, char *message, int imflags) {
[0da65d5]1817        struct oscar_data *odata = (struct oscar_data *)ic->proto_data;
1818        int ret = 0, len = strlen(message);
[6bbb939]1819        if (imflags & OPT_AWAY) {
[b7d3cc34]1820                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1821        } else {
1822                struct aim_sendimext_args args;
1823                char *s;
1824               
1825                args.flags = AIM_IMFLAGS_ACK;
1826                if (odata->icq)
1827                        args.flags |= AIM_IMFLAGS_OFFLINE;
1828                for (s = message; *s; s++)
1829                        if (*s & 128)
1830                                break;
1831               
1832                /* Message contains high ASCII chars, time for some translation! */
1833                if (*s) {
1834                        s = g_malloc(BUF_LONG);
1835                        /* Try if we can put it in an ISO8859-1 string first.
1836                           If we can't, fall back to UTF16. */
1837                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1838                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1839                                len = ret;
1840                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1841                                args.flags |= AIM_IMFLAGS_UNICODE;
1842                                len = ret;
1843                        } else {
1844                                /* OOF, translation failed... Oh well.. */
1845                                g_free( s );
1846                                s = message;
1847                        }
1848                } else {
1849                        s = message;
1850                }
1851               
1852                args.features = gaim_features;
1853                args.featureslen = sizeof(gaim_features);
1854               
1855                args.destsn = name;
1856                args.msg    = s;
1857                args.msglen = len;
1858               
1859                ret = aim_send_im_ext(odata->sess, &args);
1860               
1861                if (s != message) {
1862                        g_free(s);
1863                }
1864        }
1865        if (ret >= 0)
1866                return 1;
1867        return ret;
1868}
1869
[0da65d5]1870static void oscar_get_info(struct im_connection *g, char *name) {
[b7d3cc34]1871        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1872        if (odata->icq)
1873                aim_icq_getallinfo(odata->sess, name);
1874        else {
1875                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1876                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1877        }
1878}
1879
[0da65d5]1880static void oscar_get_away(struct im_connection *g, char *who) {
[b7d3cc34]1881        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1882        if (odata->icq) {
[f0cb961]1883                struct buddy *budlight = imcb_find_buddy(g, who);
[b7d3cc34]1884                if (budlight)
1885                        if ((budlight->uc & 0xff80) >> 7)
1886                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1887                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1888        } else
1889                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1890}
1891
[0da65d5]1892static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
[b7d3cc34]1893{
1894
1895        if (!g_strcasecmp(state, _("Visible"))) {
1896                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1897                return;
1898        } else if (!g_strcasecmp(state, _("Invisible"))) {
1899                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1900                return;
1901        } /* else... */
1902
1903        if (od->rights.maxawaymsglen == 0)
[84b045d]1904                imcb_error(ic, "oscar_set_away_aim called before locate rights received");
[b7d3cc34]1905
1906        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1907
[0da65d5]1908        if (ic->away)
1909                g_free(ic->away);
1910        ic->away = NULL;
[b7d3cc34]1911
1912        if (!message) {
1913                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1914                return;
1915        }
1916
1917        if (strlen(message) > od->rights.maxawaymsglen) {
[84b045d]1918                imcb_error(ic, "Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
[b7d3cc34]1919        }
1920
[0da65d5]1921        ic->away = g_strndup(message, od->rights.maxawaymsglen);
1922        aim_bos_setprofile(od->sess, od->conn, NULL, ic->away, gaim_caps);
[b7d3cc34]1923
1924        return;
1925}
1926
[0da65d5]1927static void oscar_set_away_icq(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
[b7d3cc34]1928{
1929    const char *msg = NULL;
1930        gboolean no_message = FALSE;
1931
1932        /* clean old states */
[0da65d5]1933    if (ic->away) {
1934                g_free(ic->away);
1935                ic->away = NULL;
[b7d3cc34]1936    }
1937        od->sess->aim_icq_state = 0;
1938
1939        /* if no message, then use an empty message */
1940    if (message) {
1941        msg = message;
1942    } else {
1943        msg = "";
1944                no_message = TRUE;
1945    }
1946
1947        if (!g_strcasecmp(state, "Online")) {
1948                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1949        } else if (!g_strcasecmp(state, "Away")) {
1950                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
[0da65d5]1951        ic->away = g_strdup(msg);
[b7d3cc34]1952                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1953        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1954                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
[0da65d5]1955        ic->away = g_strdup(msg);
[b7d3cc34]1956                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1957        } else if (!g_strcasecmp(state, "Not Available")) {
1958                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
[0da65d5]1959        ic->away = g_strdup(msg);
[b7d3cc34]1960                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1961        } else if (!g_strcasecmp(state, "Occupied")) {
1962                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
[0da65d5]1963        ic->away = g_strdup(msg);
[b7d3cc34]1964                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1965        } else if (!g_strcasecmp(state, "Free For Chat")) {
1966                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
[0da65d5]1967        ic->away = g_strdup(msg);
[b7d3cc34]1968                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1969        } else if (!g_strcasecmp(state, "Invisible")) {
1970                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
[0da65d5]1971        ic->away = g_strdup(msg);
[b7d3cc34]1972        } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
1973                if (no_message) {
1974                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1975                } else {
1976                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
[0da65d5]1977            ic->away = g_strdup(msg);
[b7d3cc34]1978                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1979                }
1980        }
1981
1982        return;
1983}
1984
[0da65d5]1985static void oscar_set_away(struct im_connection *ic, char *state, char *message)
[b7d3cc34]1986{
[0da65d5]1987        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]1988
[0da65d5]1989    oscar_set_away_aim(ic, od, state, message);
[b7d3cc34]1990        if (od->icq)
[0da65d5]1991                oscar_set_away_icq(ic, od, state, message);
[b7d3cc34]1992
1993        return;
1994}
1995
[0da65d5]1996static void oscar_add_buddy(struct im_connection *g, char *name, char *group) {
[b7d3cc34]1997        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1998        aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
1999}
2000
[0da65d5]2001static void oscar_remove_buddy(struct im_connection *g, char *name, char *group) {
[b7d3cc34]2002        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2003        struct aim_ssi_item *ssigroup;
2004        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
2005}
2006
2007static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
2008        return 1;
2009}
2010
2011static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
[0da65d5]2012        struct im_connection *ic = sess->aux_data;
[b7d3cc34]2013        struct aim_ssi_item *curitem;
2014        int tmp;
2015
2016        /* Add from server list to local list */
2017        tmp = 0;
2018        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
2019                switch (curitem->type) {
2020                        case 0x0000: /* Buddy */
[f0cb961]2021                                if ((curitem->name) && (!imcb_find_buddy(ic, curitem->name))) {
[b7d3cc34]2022                                        char *realname = NULL;
2023
2024                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
2025                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
2026                                               
[f0cb961]2027                                        imcb_add_buddy(ic, curitem->name, NULL);
[b7d3cc34]2028                                       
[f0cb961]2029                                        if (realname) {
[d06eabf]2030                                                imcb_buddy_nick_hint(ic, curitem->name, realname);
[f0cb961]2031                                                imcb_rename_buddy(ic, curitem->name, realname);
2032                                                g_free(realname);
2033                                        }
[b7d3cc34]2034                                }
2035                                break;
2036
2037                        case 0x0002: /* Permit buddy */
2038                                if (curitem->name) {
2039                                        GSList *list;
[0da65d5]2040                                        for (list=ic->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
[b7d3cc34]2041                                        if (!list) {
2042                                                char *name;
2043                                                name = g_strdup(normalize(curitem->name));
[0da65d5]2044                                                ic->permit = g_slist_append(ic->permit, name);
[b7d3cc34]2045                                                tmp++;
2046                                        }
2047                                }
2048                                break;
2049
2050                        case 0x0003: /* Deny buddy */
2051                                if (curitem->name) {
2052                                        GSList *list;
[0da65d5]2053                                        for (list=ic->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
[b7d3cc34]2054                                        if (!list) {
2055                                                char *name;
2056                                                name = g_strdup(normalize(curitem->name));
[0da65d5]2057                                                ic->deny = g_slist_append(ic->deny, name);
[b7d3cc34]2058                                                tmp++;
2059                                        }
2060                                }
2061                                break;
2062
2063                        case 0x0004: /* Permit/deny setting */
2064                                if (curitem->data) {
2065                                        guint8 permdeny;
[0da65d5]2066                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != ic->permdeny)) {
2067                                                ic->permdeny = permdeny;
[b7d3cc34]2068                                                tmp++;
2069                                        }
2070                                }
2071                                break;
2072
2073                        case 0x0005: /* Presence setting */
2074                                /* We don't want to change Gaim's setting because it applies to all accounts */
2075                                break;
2076                } /* End of switch on curitem->type */
2077        } /* End of for loop */
2078
2079        aim_ssi_enable(sess, fr->conn);
2080       
2081        /* Request offline messages, now that the buddy list is complete. */
2082        aim_icq_reqofflinemsgs(sess);
2083       
2084        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
[84b045d]2085        imcb_connected(ic);
[b7d3cc34]2086       
2087        return 1;
2088}
2089
2090static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2091{
2092        aim_snac_t *origsnac;
2093        va_list ap;
2094
2095        va_start( ap, fr );
2096        origsnac = va_arg( ap, aim_snac_t * );
2097        va_end( ap );
2098       
2099        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2100        {
2101                int i, st, count = aim_bstream_empty( &fr->data );
2102                char *list;
2103               
2104                if( count & 1 )
2105                {
2106                        /* Hmm, the length should be even... */
[84b045d]2107                        imcb_error( sess->aux_data, "Received SSI ACK package with non-even length");
[b7d3cc34]2108                        return( 0 );
2109                }
2110                count >>= 1;
2111               
2112                list = (char *) origsnac->data;
2113                for( i = 0; i < count; i ++ )
2114                {
2115                        st = aimbs_get16( &fr->data );
[f0cb961]2116                        if( st == 0x00 )
2117                        {
2118                                imcb_add_buddy( sess->aux_data, list, NULL );
2119                        }
2120                        else if( st == 0x0E )
[b7d3cc34]2121                        {
[84b045d]2122                                imcb_log( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
[b7d3cc34]2123                               
2124                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2125                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2126                        }
[f0cb961]2127                        else
2128                        {
2129                                imcb_error( sess->aux_data, "Error while adding buddy: 0x%04x", st );
2130                        }
[b7d3cc34]2131                        list += strlen( list ) + 1;
2132                }
2133        }
2134       
2135        return( 1 );
2136}
2137
[0da65d5]2138static void oscar_set_permit_deny(struct im_connection *ic) {
2139        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2140        if (od->icq) {
2141                GSList *list;
2142                char buf[MAXMSGLEN];
2143                int at;
2144
[0da65d5]2145                switch(ic->permdeny) {
[b7d3cc34]2146                case 1:
[c2fb3809]2147                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, ic->acc->user);
[b7d3cc34]2148                        break;
2149                case 2:
[c2fb3809]2150                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, ic->acc->user);
[b7d3cc34]2151                        break;
2152                case 3:
[0da65d5]2153                        list = ic->permit;
[b7d3cc34]2154                        at = 0;
2155                        while (list) {
2156                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2157                                list = list->next;
2158                        }
2159                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2160                        break;
2161                case 4:
[0da65d5]2162                        list = ic->deny;
[b7d3cc34]2163                        at = 0;
2164                        while (list) {
2165                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2166                                list = list->next;
2167                        }
2168                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2169                        break;
2170                        default:
2171                        break;
2172                }
2173        } else {
2174                if (od->sess->ssi.received_data)
[0da65d5]2175                        aim_ssi_setpermdeny(od->sess, od->conn, ic->permdeny, 0xffffffff);
[b7d3cc34]2176        }
2177}
2178
[0da65d5]2179static void oscar_add_permit(struct im_connection *ic, char *who) {
2180        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2181        if (od->icq) {
2182                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2183        } else {
2184                if (od->sess->ssi.received_data)
2185                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2186        }
2187}
2188
[0da65d5]2189static void oscar_add_deny(struct im_connection *ic, char *who) {
2190        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2191        if (od->icq) {
2192                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2193        } else {
2194                if (od->sess->ssi.received_data)
2195                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2196        }
2197}
2198
[0da65d5]2199static void oscar_rem_permit(struct im_connection *ic, char *who) {
2200        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2201        if (!od->icq) {
2202                if (od->sess->ssi.received_data)
2203                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2204        }
2205}
2206
[0da65d5]2207static void oscar_rem_deny(struct im_connection *ic, char *who) {
2208        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b7d3cc34]2209        if (!od->icq) {
2210                if (od->sess->ssi.received_data)
2211                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2212        }
2213}
2214
[0da65d5]2215static GList *oscar_away_states(struct im_connection *ic)
[b7d3cc34]2216{
[0da65d5]2217        struct oscar_data *od = ic->proto_data;
[b7d3cc34]2218        GList *m = NULL;
2219
2220        if (!od->icq)
2221                return g_list_append(m, GAIM_AWAY_CUSTOM);
2222
2223        m = g_list_append(m, "Online");
2224        m = g_list_append(m, "Away");
2225        m = g_list_append(m, "Do Not Disturb");
2226        m = g_list_append(m, "Not Available");
2227        m = g_list_append(m, "Occupied");
2228        m = g_list_append(m, "Free For Chat");
2229        m = g_list_append(m, "Invisible");
2230
2231        return m;
2232}
2233
2234static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2235{
[e64de00]2236        struct im_connection *ic = sess->aux_data;
2237        struct oscar_data *od = ic->proto_data;
2238        gchar who[16];
2239        GString *str;
2240        va_list ap;
2241        struct aim_icq_info *info;
2242        uint32_t ip;
2243
2244        va_start(ap, fr);
2245        info = va_arg(ap, struct aim_icq_info *);
2246        va_end(ap);
2247
2248        if (!info->uin)
2249                return 0;
2250
2251        str = g_string_sized_new(512);
2252        g_snprintf(who, sizeof(who), "%u", info->uin);
2253
2254        g_string_printf(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2255        info->nick ? info->nick : "-");
2256        g_string_append_printf(str, "\n%s: %s", _("First Name"), info->first);
2257        g_string_append_printf(str, "\n%s: %s", _("Last Name"), info->last);
2258        g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email);
2259        if (info->numaddresses && info->email2) {
2260                int i;
2261                for (i = 0; i < info->numaddresses; i++) {
2262                        g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email2[i]);
2263                }
2264        }
2265        if ((ip = (long) g_hash_table_lookup(od->ips, &info->uin)) != 0) {
2266                g_string_append_printf(str, "\n%s: %d.%d.%d.%d", _("Last used IP address"),
2267                                       (ip >> 24), (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
2268        }
2269        g_string_append_printf(str, "\n%s: %s", _("Mobile Phone"), info->mobile);
2270        if (info->gender != 0)
2271                g_string_append_printf(str, "\n%s: %s", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
2272        if (info->birthyear || info->birthmonth || info->birthday) {
2273                char date[30];
2274                struct tm tm;
2275                tm.tm_mday = (int)info->birthday;
2276                tm.tm_mon = (int)info->birthmonth-1;
2277                tm.tm_year = (int)info->birthyear%100;
2278                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2279                g_string_append_printf(str, "\n%s: %s", _("Birthday"), date);
2280        }
2281        if (info->age) {
2282                char age[5];
2283                g_snprintf(age, sizeof(age), "%hhd", info->age);
2284                g_string_append_printf(str, "\n%s: %s", _("Age"), age);
2285        }
2286        g_string_append_printf(str, "\n%s: %s", _("Personal Web Page"), info->personalwebpage);
2287        if (info->info && info->info[0]) {
2288                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2289                info->info, _("End of Additional Information"));
2290        }
2291        g_string_append_c(str, '\n');
2292        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2293                g_string_append_printf(str, "%s:", _("Home Address"));
2294                g_string_append_printf(str, "\n%s: %s", _("Address"), info->homeaddr);
2295                g_string_append_printf(str, "\n%s: %s", _("City"), info->homecity);
2296                g_string_append_printf(str, "\n%s: %s", _("State"), info->homestate); 
2297                g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->homezip);
2298                g_string_append_c(str, '\n');
2299        }
2300        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2301                g_string_append_printf(str, "%s:", _("Work Address"));
2302                g_string_append_printf(str, "\n%s: %s", _("Address"), info->workaddr);
2303                g_string_append_printf(str, "\n%s: %s", _("City"), info->workcity);
2304                g_string_append_printf(str, "\n%s: %s", _("State"), info->workstate);
2305                g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->workzip);
2306                g_string_append_c(str, '\n');
2307        }
2308        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2309                g_string_append_printf(str, "%s:", _("Work Information"));
2310                g_string_append_printf(str, "\n%s: %s", _("Company"), info->workcompany);
2311                g_string_append_printf(str, "\n%s: %s", _("Division"), info->workdivision);
2312                g_string_append_printf(str, "\n%s: %s", _("Position"), info->workposition);
2313                if (info->workwebpage && info->workwebpage[0]) {
2314                        g_string_append_printf(str, "\n%s: %s", _("Web Page"), info->workwebpage);
2315                }
2316                g_string_append_c(str, '\n');
2317        }
2318
2319        imcb_log(ic, "%s\n%s", _("User Info"), str->str);
2320        g_string_free(str, TRUE);
2321
2322        return 1;
[b7d3cc34]2323
2324}
2325
2326static char *oscar_encoding_extract(const char *encoding)
2327{
2328        char *ret = NULL;
2329        char *begin, *end;
2330
2331        g_return_val_if_fail(encoding != NULL, NULL);
2332
2333        /* Make sure encoding begins with charset= */
2334        if (strncmp(encoding, "text/plain; charset=", 20) &&
2335                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2336                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2337        {
2338                return NULL;
2339        }
2340
2341        begin = strchr(encoding, '"');
2342        end = strrchr(encoding, '"');
2343
2344        if ((begin == NULL) || (end == NULL) || (begin >= end))
2345                return NULL;
2346
2347        ret = g_strndup(begin+1, (end-1) - begin);
2348
2349        return ret;
2350}
2351
2352static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2353{
2354        char *utf8 = g_new0(char, 8192);
2355
2356        if ((encoding == NULL) || encoding[0] == '\0') {
2357                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2358        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2359                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2360        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2361                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2362        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2363                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2364        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2365                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2366                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2367                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2368        }
2369
2370        /*
2371         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2372         * we have been unable to convert the text to utf-8 from the encoding
2373         * that was specified.  So we assume it's UTF-8 and hope for the best.
2374         */
2375        if (*utf8 == 0) {
2376            strncpy(utf8, text, textlen);
2377        }
2378
2379        return utf8;
2380}
2381
2382static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2383{
[0da65d5]2384        struct im_connection *ic = sess->aux_data;
[b7d3cc34]2385        va_list ap;
2386        aim_userinfo_t *userinfo;
2387        guint16 infotype;
2388        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2389        guint16 text_length;
2390        char *utf8 = NULL;
2391
2392        va_start(ap, fr);
2393        userinfo = va_arg(ap, aim_userinfo_t *);
2394        infotype = va_arg(ap, int);
2395        text_encoding = va_arg(ap, char*);
2396        text = va_arg(ap, char*);
2397        text_length = va_arg(ap, int);
2398        va_end(ap);
2399
2400        if(text_encoding)
2401                extracted_encoding = oscar_encoding_extract(text_encoding);
2402        if(infotype == AIM_GETINFO_GENERALINFO) {
2403                /*Display idle time*/
2404                char buff[256];
2405                struct tm idletime;
2406                if(userinfo->idletime) {
2407                        memset(&idletime, 0, sizeof(struct tm));
2408                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2409                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2410                        idletime.tm_min = userinfo->idletime % 60;
2411                        idletime.tm_sec = 0;
2412                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
[84b045d]2413                        imcb_log(ic, "%s: %s", _("Idle Time"), buff);
[b7d3cc34]2414                }
2415               
2416                if(text) {
2417                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
[84b045d]2418                        imcb_log(ic, "%s\n%s", _("User Info"), utf8);
[b7d3cc34]2419                } else {
[84b045d]2420                        imcb_log(ic, _("No user info available."));
[b7d3cc34]2421                }
2422        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2423                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
[84b045d]2424                imcb_log(ic, "%s\n%s", _("Away Message"), utf8);
[b7d3cc34]2425        }
2426
2427        g_free(utf8);
2428   
2429        return 1;
2430}
2431
[3e1de87]2432int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)
2433{
[0da65d5]2434        struct im_connection * ic = sess->aux_data;
[3e1de87]2435        va_list ap;
2436        guint16 type1, type2;
2437        char * sn;
2438
2439        va_start(ap, fr);
2440        type1 = va_arg(ap, int);
2441        sn = va_arg(ap, char*);
2442        type2 = va_arg(ap, int);
2443        va_end(ap);
[e7f46c5]2444   
2445        if(type2 == 0x0002) {
2446                /* User is typing */
[9624fdf]2447                imcb_buddy_typing(ic, sn, OPT_TYPING);
[e7f46c5]2448        } 
2449        else if (type2 == 0x0001) {
2450                /* User has typed something, but is not actively typing (stale) */
[9624fdf]2451                imcb_buddy_typing(ic, sn, OPT_THINKING);
[e7f46c5]2452        }
2453        else {
2454                /* User has stopped typing */
[9624fdf]2455                imcb_buddy_typing(ic, sn, 0);
[e64de00]2456        }
[e7f46c5]2457       
[3e1de87]2458        return 1;
2459}
2460
[0da65d5]2461int oscar_send_typing(struct im_connection *ic, char * who, int typing)
[3e1de87]2462{
[0da65d5]2463        struct oscar_data *od = ic->proto_data;
[df1fb67]2464        return( aim_im_sendmtn(od->sess, 1, who, (typing & OPT_TYPING) ? 0x0002 : 0x0000) );
[3e1de87]2465}
2466
[f6c963b]2467void oscar_chat_msg(struct groupchat *c, char *message, int msgflags)
[b8ef1b1]2468{
[0da65d5]2469        struct im_connection *ic = c->ic;
2470        struct oscar_data * od = (struct oscar_data*)ic->proto_data;
[b8ef1b1]2471        struct chat_connection * ccon;
2472        int ret;
2473        guint8 len = strlen(message);
[73cf7fd]2474        guint16 flags;
[b8ef1b1]2475        char *s;
[11e090b]2476       
[fa29d093]2477        ccon = c->data;
[b8ef1b1]2478               
2479        for (s = message; *s; s++)
2480                if (*s & 128)
2481                        break;
[73cf7fd]2482       
2483        flags = AIM_CHATFLAGS_NOREFLECT;
2484       
[b8ef1b1]2485        /* Message contains high ASCII chars, time for some translation! */
2486        if (*s) {
2487                s = g_malloc(BUF_LONG);
2488                /* Try if we can put it in an ISO8859-1 string first.
2489                   If we can't, fall back to UTF16. */
2490                if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
[73cf7fd]2491                        flags |= AIM_CHATFLAGS_ISO_8859_1;
[b8ef1b1]2492                        len = ret;
2493                } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
[73cf7fd]2494                        flags |= AIM_CHATFLAGS_UNICODE;
[b8ef1b1]2495                        len = ret;
2496                } else {
2497                        /* OOF, translation failed... Oh well.. */
2498                        g_free( s );
2499                        s = message;
2500                }
2501        } else {
2502                s = message;
2503        }
2504               
[73cf7fd]2505        ret = aim_chat_send_im(od->sess, ccon->conn, flags, s, len);
[b8ef1b1]2506               
2507        if (s != message) {     
2508                g_free(s);
2509  }
2510 
[0da65d5]2511/*  return (ret >= 0); */
[b8ef1b1]2512}
2513
[c058ff9]2514void oscar_chat_invite(struct groupchat *c, char *who, char *message)
[b8ef1b1]2515{
[0da65d5]2516        struct im_connection *ic = c->ic;
2517        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
[fa29d093]2518        struct chat_connection *ccon = c->data;
[b8ef1b1]2519       
2520        aim_chat_invite(od->sess, od->conn, who, message ? message : "",
2521                                        ccon->exchange, ccon->name, 0x0);
2522}
2523
[0da65d5]2524void oscar_chat_kill(struct im_connection *ic, struct chat_connection *cc)
[b8ef1b1]2525{
[0da65d5]2526        struct oscar_data *od = (struct oscar_data *)ic->proto_data;
[b8ef1b1]2527
2528        /* Notify the conversation window that we've left the chat */
[e35d1a1]2529        imcb_chat_free(cc->cnv);
[b8ef1b1]2530
2531        /* Destroy the chat_connection */
2532        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
2533        if (cc->inpa > 0)
[ba9edaa]2534                b_event_remove(cc->inpa);
[b8ef1b1]2535        aim_conn_kill(od->sess, &cc->conn);
2536        g_free(cc->name);
2537        g_free(cc->show);
2538        g_free(cc);
2539}
2540
[0da65d5]2541void oscar_chat_leave(struct groupchat *c)
[b8ef1b1]2542{
[0da65d5]2543        oscar_chat_kill(c->ic, c->data);
[b8ef1b1]2544}
2545
[c737ba7]2546struct groupchat *oscar_chat_join(struct im_connection * ic, char * room, char * nick, char * password )
[b8ef1b1]2547{
[0da65d5]2548        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
[b8ef1b1]2549        aim_conn_t * cur;
2550
2551        if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
[c737ba7]2552                int st;
2553               
2554                st = aim_chatnav_createroom(od->sess, cur, room, 4);
2555               
2556                return NULL;
[b8ef1b1]2557        } else {
2558                struct create_room * cr = g_new0(struct create_room, 1);
[c737ba7]2559               
[b8ef1b1]2560                cr->exchange = 4;
[c737ba7]2561                cr->name = g_strdup(room);
[b8ef1b1]2562                od->create_rooms = g_slist_append(od->create_rooms, cr);
2563                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
[c737ba7]2564               
2565                return NULL;
[b8ef1b1]2566        }
2567}
2568
[0da65d5]2569struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)
[b8ef1b1]2570{
[0da65d5]2571        struct oscar_data * od = (struct oscar_data *)ic->proto_data;
[c737ba7]2572        struct groupchat *ret;
[b8ef1b1]2573        static int chat_id = 0;
[936ded6]2574        char * chatname;
[11e090b]2575       
[c2fb3809]2576        chatname = g_strdup_printf("%s%d", ic->acc->user, chat_id++);
[b8ef1b1]2577 
[c737ba7]2578        ret = oscar_chat_join(ic, chatname, NULL, NULL);
[b8ef1b1]2579
2580        aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);
2581
2582        g_free(chatname);
2583       
[fa29d093]2584        return NULL;
[b8ef1b1]2585}
2586
2587void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
2588{
[c737ba7]2589        oscar_chat_join(inv->ic, inv->name, NULL, NULL);
[b8ef1b1]2590        g_free(inv->name);
2591        g_free(inv);
2592}
2593
2594void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
2595{
2596        g_free(inv->name);
2597        g_free(inv);
2598}
2599
[0da65d5]2600void oscar_initmodule() 
[7b23afd]2601{
2602        struct prpl *ret = g_new0(struct prpl, 1);
2603        ret->name = "oscar";
[b7d3cc34]2604        ret->away_states = oscar_away_states;
[0da65d5]2605        ret->init = oscar_init;
[b7d3cc34]2606        ret->login = oscar_login;
[0da65d5]2607        ret->keepalive = oscar_keepalive;
2608        ret->logout = oscar_logout;
[f6c963b]2609        ret->buddy_msg = oscar_buddy_msg;
[b7d3cc34]2610        ret->get_info = oscar_get_info;
2611        ret->set_away = oscar_set_away;
2612        ret->get_away = oscar_get_away;
2613        ret->add_buddy = oscar_add_buddy;
2614        ret->remove_buddy = oscar_remove_buddy;
[f6c963b]2615        ret->chat_msg = oscar_chat_msg;
[b8ef1b1]2616        ret->chat_invite = oscar_chat_invite;
2617        ret->chat_leave = oscar_chat_leave;
[0da65d5]2618        ret->chat_with = oscar_chat_with;
[c737ba7]2619        ret->chat_join = oscar_chat_join;
[b7d3cc34]2620        ret->add_permit = oscar_add_permit;
2621        ret->add_deny = oscar_add_deny;
2622        ret->rem_permit = oscar_rem_permit;
2623        ret->rem_deny = oscar_rem_deny;
2624        ret->set_permit_deny = oscar_set_permit_deny;
[3e1de87]2625        ret->send_typing = oscar_send_typing;
[5b52a48]2626       
2627        ret->handle_cmp = aim_sncmp;
[3e1de87]2628
[7b23afd]2629        register_protocol(ret);
[b7d3cc34]2630}
Note: See TracBrowser for help on using the repository browser.