source: protocols/oscar/oscar.c @ 11e090b

Last change on this file since 11e090b was 11e090b, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-15T09:14:38Z

Moved variable declarations to the right place to make older compilers happy.

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