source: protocols/oscar/oscar.c @ 7d31002

Last change on this file since 7d31002 was aefa533e, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-03-21T08:12:22Z

Added a special +b usermode for easier parseability of some things.
(For now blist and qlist, but more should come)

  • Property mode set to 100644
File size: 75.6 KB
Line 
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 <errno.h>
24#include <ctype.h>
25#include <string.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <time.h>
29#include <sys/stat.h>
30#include <glib.h>
31#include "nogaim.h"
32#include "bitlbee.h"
33#include "proxy.h"
34#include "sock.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
64static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY | AIM_CAPS_CHAT;
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}
172
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 *, ...);
222static int gaim_parsemtn         (aim_session_t *, aim_frame_t *, ...);
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) {
276                if (aim_get_command(odata->sess, conn) >= 0) {
277                        aim_rxdispatch(odata->sess);
278                               if (odata->killme)
279                                       signoff(gc);
280                } else {
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"));
308                                }
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);
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                   We don't do those anymore, but let's stick with it, just in case
360                   it accidentally fixes something else too... */
361                gc->password[8] = 0;
362        } else {
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);
553        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parsemtn, 0);
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        /* [WvG] Wheeeee! Who needs error checking anyway? ;-) */
613        read(pos->fd, m, 16);
614        m[16] = '\0';
615        gaim_input_remove(pos->inpa);
616        closesocket(pos->fd);
617        aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
618        g_free(pos);
619}
620
621static void straight_to_hell(gpointer data, gint source, GaimInputCondition cond) {
622        struct pieceofcrap *pos = data;
623        char buf[BUF_LONG];
624
625        if (source < 0) {
626                do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
627                                " You may be disconnected shortly.", "Login Error");
628                if (pos->modname)
629                        g_free(pos->modname);
630                g_free(pos);
631                return;
632        }
633
634        g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA
635                        "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
636                        pos->offset, pos->len, pos->modname ? pos->modname : "");
637        write(pos->fd, buf, strlen(buf));
638        if (pos->modname)
639                g_free(pos->modname);
640        pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos);
641        return;
642}
643
644/* size of icbmui.ocm, the largest module in AIM 3.5 */
645#define AIM_MAX_FILE_SIZE 98304
646
647int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
648        va_list ap;
649        struct pieceofcrap *pos;
650        guint32 offset, len;
651        char *modname;
652        int fd;
653
654        va_start(ap, fr);
655        offset = (guint32)va_arg(ap, unsigned long);
656        len = (guint32)va_arg(ap, unsigned long);
657        modname = va_arg(ap, char *);
658        va_end(ap);
659
660        if (len == 0) {
661                aim_sendmemblock(sess, fr->conn, offset, len, NULL,
662                                AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
663                return 1;
664        }
665        /* uncomment this when you're convinced it's right. remember, it's been wrong before.
666        if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
667                char *buf;
668                int i = 8;
669                if (modname)
670                        i += strlen(modname);
671                buf = g_malloc(i);
672                i = 0;
673                if (modname) {
674                        memcpy(buf, modname, strlen(modname));
675                        i += strlen(modname);
676                }
677                buf[i++] = offset & 0xff;
678                buf[i++] = (offset >> 8) & 0xff;
679                buf[i++] = (offset >> 16) & 0xff;
680                buf[i++] = (offset >> 24) & 0xff;
681                buf[i++] = len & 0xff;
682                buf[i++] = (len >> 8) & 0xff;
683                buf[i++] = (len >> 16) & 0xff;
684                buf[i++] = (len >> 24) & 0xff;
685                aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
686                g_free(buf);
687                return 1;
688        }
689        */
690
691        pos = g_new0(struct pieceofcrap, 1);
692        pos->gc = sess->aux_data;
693        pos->conn = fr->conn;
694
695        pos->offset = offset;
696        pos->len = len;
697        pos->modname = modname ? g_strdup(modname) : NULL;
698
699        fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos);
700        if (fd < 0) {
701                if (pos->modname)
702                        g_free(pos->modname);
703                g_free(pos);
704                do_error_dialog(sess->aux_data, "Gaim was unable to get a valid hash for logging into AIM."
705                                " You may be disconnected shortly.", "Login Error");
706        }
707        pos->fd = fd;
708
709        return 1;
710}
711
712static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {
713#if 0
714        struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b};
715#else
716        struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD;
717#endif
718        char *key;
719        va_list ap;
720        struct gaim_connection *gc = sess->aux_data;
721
722        va_start(ap, fr);
723        key = va_arg(ap, char *);
724        va_end(ap);
725
726        aim_send_login(sess, fr->conn, gc->username, gc->password, &info, key);
727
728        return 1;
729}
730
731static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {
732        struct gaim_connection *gc = sess->aux_data;
733        struct chat_connection *chatcon;
734        static int id = 1;
735
736        aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0);
737        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
738        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
739        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
740        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
741
742        aim_clientready(sess, fr->conn);
743
744        chatcon = find_oscar_chat_by_conn(gc, fr->conn);
745        chatcon->id = id;
746        chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
747
748        return 1;
749}
750
751static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
752
753        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_ERROR, gaim_parse_genericerr, 0);
754        aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
755
756        aim_clientready(sess, fr->conn);
757
758        aim_chatnav_reqrights(sess, fr->conn);
759
760        return 1;
761}
762
763static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) {
764        struct gaim_connection *gc = data;
765        struct oscar_data *odata;
766        aim_session_t *sess;
767        aim_conn_t *tstconn;
768
769        if (!g_slist_find(get_connections(), gc)) {
770                closesocket(source);
771                return;
772        }
773
774        odata = gc->proto_data;
775        sess = odata->sess;
776        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
777
778        if (source < 0) {
779                aim_conn_kill(sess, &tstconn);
780                return;
781        }
782
783        aim_conn_completeconnect(sess, tstconn);
784        odata->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
785                                        oscar_callback, tstconn);
786}
787
788static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond)
789{
790        struct gaim_connection *gc = data;
791        struct oscar_data *odata;
792        aim_session_t *sess;
793        aim_conn_t *tstconn;
794
795        if (!g_slist_find(get_connections(), gc)) {
796                closesocket(source);
797                return;
798        }
799
800        odata = gc->proto_data;
801        sess = odata->sess;
802        tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
803
804        if (source < 0) {
805                aim_conn_kill(sess, &tstconn);
806                return;
807        }
808
809        aim_conn_completeconnect(sess, tstconn);
810        odata->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
811                                oscar_callback, tstconn);
812}
813
814static void oscar_chat_connect(gpointer data, gint source, GaimInputCondition cond)
815{
816        struct chat_connection *ccon = data;
817        struct gaim_connection *gc = ccon->gc;
818        struct oscar_data *odata;
819        aim_session_t *sess;
820        aim_conn_t *tstconn;
821
822        if (!g_slist_find(get_connections(), gc)) {
823                closesocket(source);
824                g_free(ccon->show);
825                g_free(ccon->name);
826                g_free(ccon);
827                return;
828        }
829
830        odata = gc->proto_data;
831        sess = odata->sess;
832        tstconn = ccon->conn;
833
834        if (source < 0) {
835                aim_conn_kill(sess, &tstconn);
836                g_free(ccon->show);
837                g_free(ccon->name);
838                g_free(ccon);
839                return;
840        }
841
842        aim_conn_completeconnect(sess, ccon->conn);
843        ccon->inpa = gaim_input_add(tstconn->fd,
844                        GAIM_INPUT_READ,
845                        oscar_callback, tstconn);
846        odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon);
847}
848
849/* Hrmph. I don't know how to make this look better. --mid */
850static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
851        va_list ap;
852        struct aim_redirect_data *redir;
853        struct gaim_connection *gc = sess->aux_data;
854        struct aim_user *user = gc->user;
855        aim_conn_t *tstconn;
856        int i;
857        char *host;
858        int port;
859
860        port = user->proto_opt[USEROPT_AUTHPORT][0] ?
861                atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
862
863        va_start(ap, fr);
864        redir = va_arg(ap, struct aim_redirect_data *);
865        va_end(ap);
866
867        for (i = 0; i < (int)strlen(redir->ip); i++) {
868                if (redir->ip[i] == ':') {
869                        port = atoi(&(redir->ip[i+1]));
870                        break;
871                }
872        }
873        host = g_strndup(redir->ip, i);
874
875        switch(redir->group) {
876        case 0x7: /* Authorizer */
877                tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
878                if (tstconn == NULL) {
879                        g_free(host);
880                        return 1;
881                }
882                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0);
883//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0);
884//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0);
885//              aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0);
886
887                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
888                tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc);
889                if (tstconn->fd < 0) {
890                        aim_conn_kill(sess, &tstconn);
891                        g_free(host);
892                        return 1;
893                }
894                aim_sendcookie(sess, tstconn, redir->cookie);
895                break;
896        case 0xd: /* ChatNav */
897                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL);
898                if (tstconn == NULL) {
899                        g_free(host);
900                        return 1;
901                }
902                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0);
903
904                tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
905                tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc);
906                if (tstconn->fd < 0) {
907                        aim_conn_kill(sess, &tstconn);
908                        g_free(host);
909                        return 1;
910                }
911                aim_sendcookie(sess, tstconn, redir->cookie);
912                break;
913        case 0xe: /* Chat */
914                {
915                struct chat_connection *ccon;
916
917                tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
918                if (tstconn == NULL) {
919                        g_free(host);
920                        return 1;
921                }
922
923                aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0);
924
925                ccon = g_new0(struct chat_connection, 1);
926                ccon->conn = tstconn;
927                ccon->gc = gc;
928                ccon->fd = -1;
929                ccon->name = g_strdup(redir->chat.room);
930                ccon->exchange = redir->chat.exchange;
931                ccon->instance = redir->chat.instance;
932                ccon->show = extract_name(redir->chat.room);
933               
934                ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
935                ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon);
936                if (ccon->conn->fd < 0) {
937                        aim_conn_kill(sess, &tstconn);
938                        g_free(host);
939                        g_free(ccon->show);
940                        g_free(ccon->name);
941                        g_free(ccon);
942                        return 1;
943                }
944                aim_sendcookie(sess, tstconn, redir->cookie);
945                }
946                break;
947        default: /* huh? */
948                break;
949        }
950
951        g_free(host);
952        return 1;
953}
954
955static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {
956        struct gaim_connection *gc = sess->aux_data;
957        struct oscar_data *od = gc->proto_data;
958        aim_userinfo_t *info;
959        time_t time_idle = 0, signon = 0;
960        int type = 0;
961        int caps = 0;
962        char *tmp;
963
964        va_list ap;
965        va_start(ap, fr);
966        info = va_arg(ap, aim_userinfo_t *);
967        va_end(ap);
968
969        if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
970                caps = info->capabilities;
971        if (info->flags & AIM_FLAG_ACTIVEBUDDY)
972                type |= UC_AB;
973
974        if ((!od->icq) && (info->present & AIM_USERINFO_PRESENT_FLAGS)) {
975                if (info->flags & AIM_FLAG_UNCONFIRMED)
976                        type |= UC_UNCONFIRMED;
977                if (info->flags & AIM_FLAG_ADMINISTRATOR)
978                        type |= UC_ADMIN;
979                if (info->flags & AIM_FLAG_AOL)
980                        type |= UC_AOL;
981                if (info->flags & AIM_FLAG_FREE)
982                        type |= UC_NORMAL;
983                if (info->flags & AIM_FLAG_AWAY)
984                        type |= UC_UNAVAILABLE;
985                if (info->flags & AIM_FLAG_WIRELESS)
986                        type |= UC_WIRELESS;
987        }
988        if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
989                type = (info->icqinfo.status << 7);
990                if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
991                      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
992                        type |= UC_UNAVAILABLE;
993                }
994        }
995
996        if (caps & AIM_CAPS_ICQ)
997                caps ^= AIM_CAPS_ICQ;
998
999        if (info->present & AIM_USERINFO_PRESENT_IDLE) {
1000                time(&time_idle);
1001                time_idle -= info->idletime*60;
1002        }
1003
1004        if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
1005                signon = time(NULL) - info->sessionlen;
1006
1007        tmp = g_strdup(normalize(gc->username));
1008        if (!strcmp(tmp, normalize(info->sn)))
1009                g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", info->sn);
1010        g_free(tmp);
1011
1012        serv_got_update(gc, info->sn, 1, info->warnlevel/10, signon,
1013                        time_idle, type, caps);
1014
1015        return 1;
1016}
1017
1018static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
1019        aim_userinfo_t *info;
1020        va_list ap;
1021        struct gaim_connection *gc = sess->aux_data;
1022
1023        va_start(ap, fr);
1024        info = va_arg(ap, aim_userinfo_t *);
1025        va_end(ap);
1026
1027        serv_got_update(gc, info->sn, 0, 0, 0, 0, 0, 0);
1028
1029        return 1;
1030}
1031
1032static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
1033        char *tmp = g_malloc(BUF_LONG + 1);
1034        struct gaim_connection *gc = sess->aux_data;
1035        int flags = 0;
1036       
1037        if (args->icbmflags & AIM_IMFLAGS_AWAY)
1038                flags |= IM_FLAG_AWAY;
1039       
1040        if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) {
1041                char *src;
1042               
1043                if (args->icbmflags & AIM_IMFLAGS_UNICODE)
1044                        src = "UNICODEBIG";
1045                else
1046                        src = "ISO8859-1";
1047               
1048                /* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */
1049                if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) {
1050                        // Successfully converted!
1051                } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
1052                        int i;
1053                       
1054                        for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
1055                                unsigned short uni;
1056                               
1057                                uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
1058       
1059                                if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
1060                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
1061                                } else { /* something else, do UNICODE entity */
1062                                        g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
1063                                }
1064                        }
1065                } else {
1066                        g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1067                }
1068        } else
1069                g_snprintf(tmp, BUF_LONG, "%s", args->msg);
1070       
1071        strip_linefeed(tmp);
1072        serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1);
1073        g_free(tmp);
1074       
1075        return 1;
1076}
1077
1078void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv);
1079void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv);
1080       
1081static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
1082        struct gaim_connection *gc = sess->aux_data;
1083
1084        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1085                return 1;
1086
1087        if (args->reqclass & AIM_CAPS_CHAT) {
1088                char *name = extract_name(args->info.chat.roominfo.name);
1089                int *exch = g_new0(int, 1);
1090                GList *m = NULL;
1091                char txt[1024];
1092                struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1);
1093
1094                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1095                *exch = args->info.chat.roominfo.exchange;
1096                m = g_list_append(m, exch);
1097
1098                g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg );
1099
1100                inv->gc = gc;
1101                inv->exchange = *exch;
1102                inv->name = g_strdup(name);
1103               
1104                do_ask_dialog( gc, txt, inv, oscar_accept_chat, oscar_reject_chat);
1105       
1106                if (name)
1107                        g_free(name);
1108        }
1109
1110        return 1;
1111}
1112
1113static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
1114        char *uin, message;
1115        struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
1116       
1117        uin = g_strdup_printf("%u", data->uin);
1118        message = 0;
1119        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1120        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
1121        show_got_added(data->gc, NULL, uin, NULL, NULL);
1122       
1123        g_free(uin);
1124        g_free(data);
1125}
1126
1127static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
1128        char *uin, *message;
1129        struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
1130       
1131        uin = g_strdup_printf("%u", data->uin);
1132        message = g_strdup_printf("No reason given.");
1133        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1134        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1135        g_free(message);
1136       
1137        g_free(uin);
1138        g_free(data);
1139}
1140
1141/*
1142 * For when other people ask you for authorization
1143 */
1144static void gaim_icq_authask(struct gaim_connection *gc, guint32 uin, char *msg) {
1145        struct icq_auth *data = g_new(struct icq_auth, 1);
1146        char *reason = NULL;
1147        char *dialog_msg;
1148       
1149        if (strlen(msg) > 6)
1150                reason = msg + 6;
1151       
1152        dialog_msg = g_strdup_printf("The user %u wants to add you to their buddy list for the following reason: %s", uin, reason ? reason : "No reason given.");
1153        data->gc = gc;
1154        data->uin = uin;
1155        do_ask_dialog(gc, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
1156        g_free(dialog_msg);
1157}
1158
1159static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
1160        struct gaim_connection *gc = sess->aux_data;
1161
1162        switch (args->type) {
1163                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1164                        char *uin, *message;
1165                        uin = g_strdup_printf("%u", args->uin);
1166                        message = g_strdup(args->msg);
1167                        strip_linefeed(message);
1168                        serv_got_im(gc, uin, message, 0, time(NULL), -1);
1169                        g_free(uin);
1170                        g_free(message);
1171                } break;
1172
1173                case 0x0004: { /* Someone sent you a URL */
1174                        char *uin, *message;
1175                        char **m;
1176       
1177                        uin = g_strdup_printf("%u", args->uin);
1178                        m = g_strsplit(args->msg, "\376", 2);
1179
1180                        if ((strlen(m[0]) != 0)) {
1181                          message = g_strjoinv(" -- ", m);
1182                        } else {
1183                          message = m[1];
1184                        }
1185
1186                        strip_linefeed(message);
1187                        serv_got_im(gc, uin, message, 0, time(NULL), -1);
1188                        g_free(uin);
1189                        g_free(m);
1190                        g_free(message);
1191                } break;
1192               
1193                case 0x0006: { /* Someone requested authorization */
1194                        gaim_icq_authask(gc, args->uin, args->msg);
1195                } break;
1196
1197                case 0x0007: { /* Someone has denied you authorization */
1198                        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.") );
1199                } break;
1200
1201                case 0x0008: { /* Someone has granted you authorization */
1202                        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.") );
1203                } break;
1204
1205                case 0x0012: {
1206                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1207                } break;
1208
1209                default: {;
1210                } break;
1211        }
1212
1213        return 1;
1214}
1215
1216static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1217        int channel, ret = 0;
1218        aim_userinfo_t *userinfo;
1219        va_list ap;
1220
1221        va_start(ap, fr);
1222        channel = va_arg(ap, int);
1223        userinfo = va_arg(ap, aim_userinfo_t *);
1224
1225    if (set_getint(sess->aux_data, "debug")) {
1226        serv_got_crap(sess->aux_data, "channel %i called", channel);
1227    }
1228
1229        switch (channel) {
1230                case 1: { /* standard message */
1231                        struct aim_incomingim_ch1_args *args;
1232                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1233                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1234                } break;
1235
1236                case 2: { /* rendevous */
1237                        struct aim_incomingim_ch2_args *args;
1238                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1239                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1240                } break;
1241
1242                case 4: { /* ICQ */
1243                        struct aim_incomingim_ch4_args *args;
1244                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1245                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1246                } break;
1247
1248                default: {;
1249                } break;
1250        }
1251
1252        va_end(ap);
1253
1254        return ret;
1255}
1256
1257static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1258        va_list ap;
1259        guint16 chan, nummissed, reason;
1260        aim_userinfo_t *userinfo;
1261        char buf[1024];
1262
1263        va_start(ap, fr);
1264        chan = (guint16)va_arg(ap, unsigned int);
1265        userinfo = va_arg(ap, aim_userinfo_t *);
1266        nummissed = (guint16)va_arg(ap, unsigned int);
1267        reason = (guint16)va_arg(ap, unsigned int);
1268        va_end(ap);
1269
1270        switch(reason) {
1271                case 0:
1272                        /* Invalid (0) */
1273                        g_snprintf(buf,
1274                                   sizeof(buf),
1275                                   nummissed == 1 ? 
1276                                   _("You missed %d message from %s because it was invalid.") :
1277                                   _("You missed %d messages from %s because they were invalid."),
1278                                   nummissed,
1279                                   userinfo->sn);
1280                        break;
1281                case 1:
1282                        /* Message too large */
1283                        g_snprintf(buf,
1284                                   sizeof(buf),
1285                                   nummissed == 1 ?
1286                                   _("You missed %d message from %s because it was too large.") :
1287                                   _("You missed %d messages from %s because they were too large."),
1288                                   nummissed,
1289                                   userinfo->sn);
1290                        break;
1291                case 2:
1292                        /* Rate exceeded */
1293                        g_snprintf(buf,
1294                                   sizeof(buf),
1295                                   nummissed == 1 ? 
1296                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1297                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1298                                   nummissed,
1299                                   userinfo->sn);
1300                        break;
1301                case 3:
1302                        /* Evil Sender */
1303                        g_snprintf(buf,
1304                                   sizeof(buf),
1305                                   nummissed == 1 ?
1306                                   _("You missed %d message from %s because it was too evil.") : 
1307                                   _("You missed %d messages from %s because they are too evil."),
1308                                   nummissed,
1309                                   userinfo->sn);
1310                        break;
1311                case 4:
1312                        /* Evil Receiver */
1313                        g_snprintf(buf,
1314                                   sizeof(buf),
1315                                   nummissed == 1 ? 
1316                                   _("You missed %d message from %s because you are too evil.") :
1317                                   _("You missed %d messages from %s because you are too evil."),
1318                                   nummissed,
1319                                   userinfo->sn);
1320                        break;
1321                default:
1322                        g_snprintf(buf,
1323                                   sizeof(buf),
1324                                   nummissed == 1 ? 
1325                                   _("You missed %d message from %s for unknown reasons.") :
1326                                   _("You missed %d messages from %s for unknown reasons."),
1327                                   nummissed,
1328                                   userinfo->sn);
1329                        break;
1330        }
1331        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1332
1333        return 1;
1334}
1335
1336static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1337        va_list ap;
1338        guint16 reason;
1339        char *m;
1340
1341        va_start(ap, fr);
1342        reason = (guint16)va_arg(ap, unsigned int);
1343        va_end(ap);
1344
1345        m = g_strdup_printf(_("SNAC threw error: %s\n"),
1346                        reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1347        do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error"));
1348        g_free(m);
1349
1350        return 1;
1351}
1352
1353static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1354        va_list ap;
1355        char *destn;
1356        guint16 reason;
1357        char buf[1024];
1358
1359        va_start(ap, fr);
1360        reason = (guint16)va_arg(ap, unsigned int);
1361        destn = va_arg(ap, char *);
1362        va_end(ap);
1363
1364        sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
1365                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1366        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1367
1368        return 1;
1369}
1370
1371static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1372        va_list ap;
1373        char *destn;
1374        guint16 reason;
1375        char buf[1024];
1376
1377        va_start(ap, fr);
1378        reason = (guint16)va_arg(ap, unsigned int);
1379        destn = va_arg(ap, char *);
1380        va_end(ap);
1381
1382        sprintf(buf, _("User information for %s unavailable: %s"), destn,
1383                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1384        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1385
1386
1387        return 1;
1388}
1389
1390static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1391        char *msg;
1392        guint16 id;
1393        va_list ap;
1394
1395        va_start(ap, fr);
1396        id  = (guint16)va_arg(ap, unsigned int);
1397        msg = va_arg(ap, char *);
1398        va_end(ap);
1399
1400        if (id < 4)
1401                do_error_dialog(sess->aux_data, _("Your connection may be lost."),
1402                                _("AOL error"));
1403
1404        return 1;
1405}
1406
1407static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1408        va_list ap;
1409        guint16 type;
1410        struct gaim_connection *gc = sess->aux_data;
1411        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1412
1413        va_start(ap, fr);
1414        type = (guint16)va_arg(ap, unsigned int);
1415
1416        switch(type) {
1417                case 0x0002: {
1418                        guint8 maxrooms;
1419                        struct aim_chat_exchangeinfo *exchanges;
1420                        int exchangecount; // i;
1421
1422                        maxrooms = (guint8)va_arg(ap, unsigned int);
1423                        exchangecount = va_arg(ap, int);
1424                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1425                        va_end(ap);
1426
1427                        while (odata->create_rooms) {
1428                                struct create_room *cr = odata->create_rooms->data;
1429                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1430                                g_free(cr->name);
1431                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1432                                g_free(cr);
1433                        }
1434                        }
1435                        break;
1436                case 0x0008: {
1437                        char *fqcn, *name, *ck;
1438                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1439                        guint8 createperms;
1440                        guint32 createtime;
1441
1442                        fqcn = va_arg(ap, char *);
1443                        instance = (guint16)va_arg(ap, unsigned int);
1444                        exchange = (guint16)va_arg(ap, unsigned int);
1445                        flags = (guint16)va_arg(ap, unsigned int);
1446                        createtime = va_arg(ap, guint32);
1447                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1448                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1449                        createperms = (guint8)va_arg(ap, int);
1450                        unknown = (guint16)va_arg(ap, unsigned int);
1451                        name = va_arg(ap, char *);
1452                        ck = va_arg(ap, char *);
1453                        va_end(ap);
1454
1455                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1456                        }
1457                        break;
1458                default:
1459                        va_end(ap);
1460                        break;
1461        }
1462        return 1;
1463}
1464
1465static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1466        va_list ap;
1467        int count, i;
1468        aim_userinfo_t *info;
1469        struct gaim_connection *g = sess->aux_data;
1470
1471        struct chat_connection *c = NULL;
1472
1473        va_start(ap, fr);
1474        count = va_arg(ap, int);
1475        info  = va_arg(ap, aim_userinfo_t *);
1476        va_end(ap);
1477
1478        c = find_oscar_chat_by_conn(g, fr->conn);
1479        if (!c)
1480                return 1;
1481
1482        for (i = 0; i < count; i++)
1483                add_chat_buddy(c->cnv, info[i].sn);
1484
1485        return 1;
1486}
1487
1488static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1489        va_list ap;
1490        int count, i;
1491        aim_userinfo_t *info;
1492        struct gaim_connection *g = sess->aux_data;
1493
1494        struct chat_connection *c = NULL;
1495
1496        va_start(ap, fr);
1497        count = va_arg(ap, int);
1498        info  = va_arg(ap, aim_userinfo_t *);
1499        va_end(ap);
1500
1501        c = find_oscar_chat_by_conn(g, fr->conn);
1502        if (!c)
1503                return 1;
1504
1505        for (i = 0; i < count; i++)
1506                remove_chat_buddy(c->cnv, info[i].sn, NULL);
1507
1508        return 1;
1509}
1510
1511static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1512        va_list ap;
1513        aim_userinfo_t *userinfo;
1514        struct aim_chat_roominfo *roominfo;
1515        char *roomname;
1516        int usercount;
1517        char *roomdesc;
1518        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1519        guint32 creationtime;
1520        struct gaim_connection *gc = sess->aux_data;
1521        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1522
1523        va_start(ap, fr);
1524        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1525        roomname = va_arg(ap, char *);
1526        usercount= va_arg(ap, int);
1527        userinfo = va_arg(ap, aim_userinfo_t *);
1528        roomdesc = va_arg(ap, char *);
1529        unknown_c9 = (guint16)va_arg(ap, int);
1530        creationtime = (guint32)va_arg(ap, unsigned long);
1531        maxmsglen = (guint16)va_arg(ap, int);
1532        unknown_d2 = (guint16)va_arg(ap, int);
1533        unknown_d5 = (guint16)va_arg(ap, int);
1534        maxvisiblemsglen = (guint16)va_arg(ap, int);
1535        va_end(ap);
1536
1537        ccon->maxlen = maxmsglen;
1538        ccon->maxvis = maxvisiblemsglen;
1539
1540        return 1;
1541}
1542
1543static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1544        va_list ap;
1545        aim_userinfo_t *info;
1546        char *msg;
1547        struct gaim_connection *gc = sess->aux_data;
1548        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1549        char *tmp;
1550
1551        va_start(ap, fr);
1552        info = va_arg(ap, aim_userinfo_t *);
1553        msg  = va_arg(ap, char *);
1554
1555        tmp = g_malloc(BUF_LONG);
1556        g_snprintf(tmp, BUF_LONG, "%s", msg);
1557        serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL));
1558        g_free(tmp);
1559
1560        return 1;
1561}
1562
1563static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1564#if 0
1565        static const char *codes[5] = {
1566                "invalid",
1567                 "change",
1568                 "warning",
1569                 "limit",
1570                 "limit cleared",
1571        };
1572#endif
1573        va_list ap;
1574        guint16 code, rateclass;
1575        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1576
1577        va_start(ap, fr); 
1578        code = (guint16)va_arg(ap, unsigned int);
1579        rateclass= (guint16)va_arg(ap, unsigned int);
1580        windowsize = (guint32)va_arg(ap, unsigned long);
1581        clear = (guint32)va_arg(ap, unsigned long);
1582        alert = (guint32)va_arg(ap, unsigned long);
1583        limit = (guint32)va_arg(ap, unsigned long);
1584        disconnect = (guint32)va_arg(ap, unsigned long);
1585        currentavg = (guint32)va_arg(ap, unsigned long);
1586        maxavg = (guint32)va_arg(ap, unsigned long);
1587        va_end(ap);
1588
1589        /* XXX fix these values */
1590        if (code == AIM_RATE_CODE_CHANGE) {
1591                if (currentavg >= clear)
1592                        aim_conn_setlatency(fr->conn, 0);
1593        } else if (code == AIM_RATE_CODE_WARNING) {
1594                aim_conn_setlatency(fr->conn, windowsize/4);
1595        } else if (code == AIM_RATE_CODE_LIMIT) {
1596                do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1597                                  "Please wait 10 seconds and try again."), _("Gaim - Error"));
1598                aim_conn_setlatency(fr->conn, windowsize/2);
1599        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1600                aim_conn_setlatency(fr->conn, 0);
1601        }
1602
1603        return 1;
1604}
1605
1606static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1607        va_list ap;
1608        aim_userinfo_t *info;
1609        struct gaim_connection *gc = sess->aux_data;
1610
1611        va_start(ap, fr);
1612        info = va_arg(ap, aim_userinfo_t *);
1613        va_end(ap);
1614
1615        gc->evil = info->warnlevel/10;
1616        /* gc->correction_time = (info->onlinesince - gc->login_time); */
1617
1618        return 1;
1619}
1620
1621static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1622
1623        aim_reqpersonalinfo(sess, fr->conn);
1624        aim_bos_reqlocaterights(sess, fr->conn);
1625        aim_bos_reqbuddyrights(sess, fr->conn);
1626
1627        aim_reqicbmparams(sess);
1628
1629        aim_bos_reqrights(sess, fr->conn);
1630        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1631        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1632                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1633
1634        return 1;
1635}
1636
1637static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1638        struct gaim_connection *gc = sess->aux_data;
1639        struct oscar_data *od = gc->proto_data;
1640
1641        aim_clientready(sess, fr->conn);
1642
1643        if (od->chpass) {
1644                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1645                g_free(od->oldp);
1646                od->oldp = NULL;
1647                g_free(od->newp);
1648                od->newp = NULL;
1649                od->chpass = FALSE;
1650        }
1651        if (od->setnick) {
1652                aim_admin_setnick(sess, fr->conn, od->newsn);
1653                g_free(od->newsn);
1654                od->newsn = NULL;
1655                od->setnick = FALSE;
1656        }
1657        if (od->conf) {
1658                aim_admin_reqconfirm(sess, fr->conn);
1659                od->conf = FALSE;
1660        }
1661        if (od->reqemail) {
1662                aim_admin_getinfo(sess, fr->conn, 0x0011);
1663                od->reqemail = FALSE;
1664        }
1665        if (od->setemail) {
1666                aim_admin_setemail(sess, fr->conn, od->email);
1667                g_free(od->email);
1668                od->setemail = FALSE;
1669        }
1670
1671        return 1;
1672}
1673
1674static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1675        struct aim_icbmparameters *params;
1676        va_list ap;
1677
1678        va_start(ap, fr);
1679        params = va_arg(ap, struct aim_icbmparameters *);
1680        va_end(ap);
1681
1682        /* Maybe senderwarn and recverwarn should be user preferences... */
1683        params->flags = 0x0000000b;
1684        params->maxmsglen = 8000;
1685        params->minmsginterval = 0;
1686
1687        aim_seticbmparam(sess, params);
1688
1689        return 1;
1690}
1691
1692static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1693{
1694        va_list ap;
1695        guint16 maxsiglen;
1696        struct gaim_connection *gc = sess->aux_data;
1697        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1698
1699        va_start(ap, fr);
1700        maxsiglen = va_arg(ap, int);
1701        va_end(ap);
1702
1703        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1704
1705        aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps);
1706
1707        return 1;
1708}
1709
1710static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1711        va_list ap;
1712        guint16 maxbuddies, maxwatchers;
1713        struct gaim_connection *gc = sess->aux_data;
1714        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1715
1716        va_start(ap, fr);
1717        maxbuddies = (guint16)va_arg(ap, unsigned int);
1718        maxwatchers = (guint16)va_arg(ap, unsigned int);
1719        va_end(ap);
1720
1721        odata->rights.maxbuddies = (guint)maxbuddies;
1722        odata->rights.maxwatchers = (guint)maxwatchers;
1723
1724        return 1;
1725}
1726
1727static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1728        guint16 maxpermits, maxdenies;
1729        va_list ap;
1730        struct gaim_connection *gc = sess->aux_data;
1731        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1732
1733        va_start(ap, fr);
1734        maxpermits = (guint16)va_arg(ap, unsigned int);
1735        maxdenies = (guint16)va_arg(ap, unsigned int);
1736        va_end(ap);
1737
1738        odata->rights.maxpermits = (guint)maxpermits;
1739        odata->rights.maxdenies = (guint)maxdenies;
1740
1741        aim_clientready(sess, fr->conn);
1742
1743        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1744
1745        aim_ssi_reqrights(sess, fr->conn);
1746        aim_ssi_reqalldata(sess, fr->conn);
1747
1748        return 1;
1749}
1750
1751static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1752        va_list ap;
1753        struct aim_icq_offlinemsg *msg;
1754        struct gaim_connection *gc = sess->aux_data;
1755
1756        va_start(ap, fr);
1757        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1758        va_end(ap);
1759
1760        switch (msg->type) {
1761                case 0x0001: { /* Basic offline message */
1762                        char sender[32];
1763                        char *dialog_msg = g_strdup(msg->msg);
1764                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1765                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1766                        strip_linefeed(dialog_msg);
1767                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1768                        g_free(dialog_msg);
1769                } break;
1770
1771                case 0x0004: { /* Someone sent you a URL */
1772                        char sender[32];
1773                        char *dialog_msg;
1774                        char **m;
1775
1776                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1777                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1778
1779                        m = g_strsplit(msg->msg, "\376", 2);
1780
1781                        if ((strlen(m[0]) != 0)) {
1782                          dialog_msg = g_strjoinv(" -- ", m);
1783                        } else {
1784                          dialog_msg = m[1];
1785                        }
1786
1787                        strip_linefeed(dialog_msg);
1788                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1789                        g_free(dialog_msg);
1790                        g_free(m);
1791                } break;
1792               
1793                case 0x0006: { /* Authorization request */
1794                        gaim_icq_authask(gc, msg->sender, msg->msg);
1795                } break;
1796
1797                case 0x0007: { /* Someone has denied you authorization */
1798                        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.") );
1799                } break;
1800
1801                case 0x0008: { /* Someone has granted you authorization */
1802                        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.") );
1803                } break;
1804
1805                case 0x0012: {
1806                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1807                } break;
1808
1809                default: {;
1810                }
1811        }
1812
1813        return 1;
1814}
1815
1816static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1817{
1818        aim_icq_ackofflinemsgs(sess);
1819        return 1;
1820}
1821
1822static void oscar_keepalive(struct gaim_connection *gc) {
1823        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1824        aim_flap_nop(odata->sess, odata->conn);
1825}
1826
1827static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) {
1828        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1829        int ret = 0;
1830        if (imflags & IM_FLAG_AWAY) {
1831                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1832        } else {
1833                struct aim_sendimext_args args;
1834                char *s;
1835               
1836                args.flags = AIM_IMFLAGS_ACK;
1837                if (odata->icq)
1838                        args.flags |= AIM_IMFLAGS_OFFLINE;
1839                for (s = message; *s; s++)
1840                        if (*s & 128)
1841                                break;
1842               
1843                /* Message contains high ASCII chars, time for some translation! */
1844                if (*s) {
1845                        s = g_malloc(BUF_LONG);
1846                        /* Try if we can put it in an ISO8859-1 string first.
1847                           If we can't, fall back to UTF16. */
1848                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1849                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1850                                len = ret;
1851                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1852                                args.flags |= AIM_IMFLAGS_UNICODE;
1853                                len = ret;
1854                        } else {
1855                                /* OOF, translation failed... Oh well.. */
1856                                g_free( s );
1857                                s = message;
1858                        }
1859                } else {
1860                        s = message;
1861                }
1862               
1863                args.features = gaim_features;
1864                args.featureslen = sizeof(gaim_features);
1865               
1866                args.destsn = name;
1867                args.msg    = s;
1868                args.msglen = len;
1869               
1870                ret = aim_send_im_ext(odata->sess, &args);
1871               
1872                if (s != message) {
1873                        g_free(s);
1874                }
1875        }
1876        if (ret >= 0)
1877                return 1;
1878        return ret;
1879}
1880
1881static void oscar_get_info(struct gaim_connection *g, char *name) {
1882        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1883        if (odata->icq)
1884                aim_icq_getallinfo(odata->sess, name);
1885        else {
1886                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1887                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1888        }
1889}
1890
1891static void oscar_get_away(struct gaim_connection *g, char *who) {
1892        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1893        if (odata->icq) {
1894                struct buddy *budlight = find_buddy(g, who);
1895                if (budlight)
1896                        if ((budlight->uc & 0xff80) >> 7)
1897                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1898                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1899        } else
1900                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1901}
1902
1903static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1904{
1905
1906        if (!g_strcasecmp(state, _("Visible"))) {
1907                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1908                return;
1909        } else if (!g_strcasecmp(state, _("Invisible"))) {
1910                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1911                return;
1912        } /* else... */
1913
1914        if (od->rights.maxawaymsglen == 0)
1915                do_error_dialog(gc, "oscar_set_away_aim called before locate rights received", "Protocol Error");
1916
1917        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1918
1919        if (gc->away)
1920                g_free(gc->away);
1921        gc->away = NULL;
1922
1923        if (!message) {
1924                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1925                return;
1926        }
1927
1928        if (strlen(message) > od->rights.maxawaymsglen) {
1929                gchar *errstr;
1930
1931                errstr = g_strdup_printf("Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
1932
1933                do_error_dialog(gc, errstr, "Away Message Too Long");
1934
1935                g_free(errstr);
1936        }
1937
1938        gc->away = g_strndup(message, od->rights.maxawaymsglen);
1939        aim_bos_setprofile(od->sess, od->conn, NULL, gc->away, gaim_caps);
1940
1941        return;
1942}
1943
1944static void oscar_set_away_icq(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1945{
1946    const char *msg = NULL;
1947        gboolean no_message = FALSE;
1948
1949        /* clean old states */
1950    if (gc->away) {
1951                g_free(gc->away);
1952                gc->away = NULL;
1953    }
1954        od->sess->aim_icq_state = 0;
1955
1956        /* if no message, then use an empty message */
1957    if (message) {
1958        msg = message;
1959    } else {
1960        msg = "";
1961                no_message = TRUE;
1962    }
1963
1964        if (!g_strcasecmp(state, "Online")) {
1965                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1966        } else if (!g_strcasecmp(state, "Away")) {
1967                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1968        gc->away = g_strdup(msg);
1969                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1970        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1971                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
1972        gc->away = g_strdup(msg);
1973                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1974        } else if (!g_strcasecmp(state, "Not Available")) {
1975                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
1976        gc->away = g_strdup(msg);
1977                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1978        } else if (!g_strcasecmp(state, "Occupied")) {
1979                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
1980        gc->away = g_strdup(msg);
1981                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1982        } else if (!g_strcasecmp(state, "Free For Chat")) {
1983                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
1984        gc->away = g_strdup(msg);
1985                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1986        } else if (!g_strcasecmp(state, "Invisible")) {
1987                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1988        gc->away = g_strdup(msg);
1989        } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
1990                if (no_message) {
1991                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1992                } else {
1993                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1994            gc->away = g_strdup(msg);
1995                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1996                }
1997        }
1998
1999        return;
2000}
2001
2002static void oscar_set_away(struct gaim_connection *gc, char *state, char *message)
2003{
2004        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2005
2006    oscar_set_away_aim(gc, od, state, message);
2007        if (od->icq)
2008                oscar_set_away_icq(gc, od, state, message);
2009
2010        return;
2011}
2012
2013static void oscar_add_buddy(struct gaim_connection *g, char *name) {
2014        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2015        aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
2016}
2017
2018static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) {
2019        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2020        struct aim_ssi_item *ssigroup;
2021        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
2022}
2023
2024static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
2025        return 1;
2026}
2027
2028static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
2029        struct gaim_connection *gc = sess->aux_data;
2030        struct aim_ssi_item *curitem;
2031        int tmp;
2032
2033        /* Add from server list to local list */
2034        tmp = 0;
2035        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
2036                switch (curitem->type) {
2037                        case 0x0000: /* Buddy */
2038                                if ((curitem->name) && (!find_buddy(gc, curitem->name))) {
2039                                        char *realname = NULL;
2040
2041                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
2042                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
2043                                               
2044                                        add_buddy(gc, NULL, curitem->name, realname);
2045                                       
2046                                        if (realname)
2047                                            g_free(realname);
2048                                }
2049                                break;
2050
2051                        case 0x0002: /* Permit buddy */
2052                                if (curitem->name) {
2053                                        GSList *list;
2054                                        for (list=gc->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2055                                        if (!list) {
2056                                                char *name;
2057                                                name = g_strdup(normalize(curitem->name));
2058                                                gc->permit = g_slist_append(gc->permit, name);
2059                                                build_allow_list();
2060                                                tmp++;
2061                                        }
2062                                }
2063                                break;
2064
2065                        case 0x0003: /* Deny buddy */
2066                                if (curitem->name) {
2067                                        GSList *list;
2068                                        for (list=gc->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2069                                        if (!list) {
2070                                                char *name;
2071                                                name = g_strdup(normalize(curitem->name));
2072                                                gc->deny = g_slist_append(gc->deny, name);
2073                                                build_block_list();
2074                                                tmp++;
2075                                        }
2076                                }
2077                                break;
2078
2079                        case 0x0004: /* Permit/deny setting */
2080                                if (curitem->data) {
2081                                        guint8 permdeny;
2082                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != gc->permdeny)) {
2083                                                gc->permdeny = permdeny;
2084                                                tmp++;
2085                                        }
2086                                }
2087                                break;
2088
2089                        case 0x0005: /* Presence setting */
2090                                /* We don't want to change Gaim's setting because it applies to all accounts */
2091                                break;
2092                } /* End of switch on curitem->type */
2093        } /* End of for loop */
2094
2095        aim_ssi_enable(sess, fr->conn);
2096       
2097        /* Request offline messages, now that the buddy list is complete. */
2098        aim_icq_reqofflinemsgs(sess);
2099       
2100        /* Now that we have a buddy list, we can tell BitlBee that we're online. */
2101        account_online(gc);
2102       
2103        return 1;
2104}
2105
2106static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
2107{
2108        aim_snac_t *origsnac;
2109        va_list ap;
2110
2111        va_start( ap, fr );
2112        origsnac = va_arg( ap, aim_snac_t * );
2113        va_end( ap );
2114       
2115        if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
2116        {
2117                int i, st, count = aim_bstream_empty( &fr->data );
2118                char *list;
2119               
2120                if( count & 1 )
2121                {
2122                        /* Hmm, the length should be even... */
2123                        do_error_dialog( sess->aux_data, "Received SSI ACK package with non-even length", "Gaim - Error" );
2124                        return( 0 );
2125                }
2126                count >>= 1;
2127               
2128                list = (char *) origsnac->data;
2129                for( i = 0; i < count; i ++ )
2130                {
2131                        st = aimbs_get16( &fr->data );
2132                        if( st == 0x0E )
2133                        {
2134                                serv_got_crap( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
2135                               
2136                                aim_ssi_auth_request( sess, fr->conn, list, "" );
2137                                aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
2138                        }
2139                        list += strlen( list ) + 1;
2140                }
2141        }
2142       
2143        return( 1 );
2144}
2145
2146static void oscar_set_permit_deny(struct gaim_connection *gc) {
2147        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2148        if (od->icq) {
2149                GSList *list;
2150                char buf[MAXMSGLEN];
2151                int at;
2152
2153                switch(gc->permdeny) {
2154                case 1:
2155                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gc->username);
2156                        break;
2157                case 2:
2158                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gc->username);
2159                        break;
2160                case 3:
2161                        list = gc->permit;
2162                        at = 0;
2163                        while (list) {
2164                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2165                                list = list->next;
2166                        }
2167                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
2168                        break;
2169                case 4:
2170                        list = gc->deny;
2171                        at = 0;
2172                        while (list) {
2173                                at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
2174                                list = list->next;
2175                        }
2176                        aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
2177                        break;
2178                        default:
2179                        break;
2180                }
2181                signoff_blocked(gc);
2182        } else {
2183                if (od->sess->ssi.received_data)
2184                        aim_ssi_setpermdeny(od->sess, od->conn, gc->permdeny, 0xffffffff);
2185        }
2186}
2187
2188static void oscar_add_permit(struct gaim_connection *gc, char *who) {
2189        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2190        if (od->icq) {
2191                aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
2192        } else {
2193                if (od->sess->ssi.received_data)
2194                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2195        }
2196}
2197
2198static void oscar_add_deny(struct gaim_connection *gc, char *who) {
2199        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2200        if (od->icq) {
2201                aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
2202        } else {
2203                if (od->sess->ssi.received_data)
2204                        aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2205        }
2206}
2207
2208static void oscar_rem_permit(struct gaim_connection *gc, char *who) {
2209        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2210        if (!od->icq) {
2211                if (od->sess->ssi.received_data)
2212                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
2213        }
2214}
2215
2216static void oscar_rem_deny(struct gaim_connection *gc, char *who) {
2217        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2218        if (!od->icq) {
2219                if (od->sess->ssi.received_data)
2220                        aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
2221        }
2222}
2223
2224static GList *oscar_away_states(struct gaim_connection *gc)
2225{
2226        struct oscar_data *od = gc->proto_data;
2227        GList *m = NULL;
2228
2229        if (!od->icq)
2230                return g_list_append(m, GAIM_AWAY_CUSTOM);
2231
2232        m = g_list_append(m, "Online");
2233        m = g_list_append(m, "Away");
2234        m = g_list_append(m, "Do Not Disturb");
2235        m = g_list_append(m, "Not Available");
2236        m = g_list_append(m, "Occupied");
2237        m = g_list_append(m, "Free For Chat");
2238        m = g_list_append(m, "Invisible");
2239
2240        return m;
2241}
2242
2243static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
2244{
2245        struct gaim_connection *gc = sess->aux_data;
2246        gchar who[16];
2247        GString *str;
2248        va_list ap;
2249        struct aim_icq_info *info;
2250
2251        va_start(ap, fr);
2252        info = va_arg(ap, struct aim_icq_info *);
2253        va_end(ap);
2254
2255        if (!info->uin)
2256                return 0;
2257
2258        str = g_string_sized_new(100);
2259        g_snprintf(who, sizeof(who), "%u", info->uin);
2260
2261        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
2262                                info->nick ? info->nick : "-");
2263        info_string_append(str, "\n", _("First Name"), info->first);
2264        info_string_append(str, "\n", _("Last Name"), info->last);
2265                info_string_append(str, "\n", _("Email Address"), info->email);
2266        if (info->numaddresses && info->email2) {
2267                int i;
2268                for (i = 0; i < info->numaddresses; i++) {
2269                                        info_string_append(str, "\n", _("Email Address"), info->email2[i]);
2270                }
2271        }
2272        info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
2273        info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
2274        if (info->birthyear || info->birthmonth || info->birthday) {
2275                char date[30];
2276                struct tm tm;
2277                tm.tm_mday = (int)info->birthday;
2278                tm.tm_mon = (int)info->birthmonth-1;
2279                tm.tm_year = (int)info->birthyear-1900;
2280                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
2281                info_string_append(str, "\n", _("Birthday"), date);
2282        }
2283        if (info->age) {
2284                char age[5];
2285                g_snprintf(age, sizeof(age), "%hhd", info->age);
2286                info_string_append(str, "\n", _("Age"), age);
2287        }
2288                info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
2289        if (info->info && info->info[0]) {
2290                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
2291                                                info->info, _("End of Additional Information"));
2292        }
2293        g_string_sprintfa(str, "\n");
2294        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
2295                g_string_sprintfa(str, "%s:", _("Home Address"));
2296                info_string_append(str, "\n", _("Address"), info->homeaddr);
2297                info_string_append(str, "\n", _("City"), info->homecity);
2298                info_string_append(str, "\n", _("State"), info->homestate); 
2299                                info_string_append(str, "\n", _("Zip Code"), info->homezip);
2300                g_string_sprintfa(str, "\n");
2301        }
2302        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
2303                g_string_sprintfa(str, "%s:", _("Work Address"));
2304                info_string_append(str, "\n", _("Address"), info->workaddr);
2305                info_string_append(str, "\n", _("City"), info->workcity);
2306                info_string_append(str, "\n", _("State"), info->workstate);
2307                                info_string_append(str, "\n", _("Zip Code"), info->workzip);
2308                g_string_sprintfa(str, "\n");
2309        }
2310        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
2311                g_string_sprintfa(str, "%s:", _("Work Information"));
2312                info_string_append(str, "\n", _("Company"), info->workcompany);
2313                info_string_append(str, "\n", _("Division"), info->workdivision);
2314                info_string_append(str, "\n", _("Position"), info->workposition);
2315                if (info->workwebpage && info->workwebpage[0]) {
2316                        info_string_append(str, "\n", _("Web Page"), info->workwebpage);
2317                }
2318                g_string_sprintfa(str, "\n");
2319        }
2320
2321                serv_got_crap(gc, "%s\n%s", _("User Info"), str->str);
2322        g_string_free(str, TRUE);
2323
2324        return 1;
2325
2326}
2327
2328static char *oscar_encoding_extract(const char *encoding)
2329{
2330        char *ret = NULL;
2331        char *begin, *end;
2332
2333        g_return_val_if_fail(encoding != NULL, NULL);
2334
2335        /* Make sure encoding begins with charset= */
2336        if (strncmp(encoding, "text/plain; charset=", 20) &&
2337                strncmp(encoding, "text/aolrtf; charset=", 21) &&
2338                strncmp(encoding, "text/x-aolrtf; charset=", 23))
2339        {
2340                return NULL;
2341        }
2342
2343        begin = strchr(encoding, '"');
2344        end = strrchr(encoding, '"');
2345
2346        if ((begin == NULL) || (end == NULL) || (begin >= end))
2347                return NULL;
2348
2349        ret = g_strndup(begin+1, (end-1) - begin);
2350
2351        return ret;
2352}
2353
2354static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
2355{
2356        char *utf8 = g_new0(char, 8192);
2357
2358        if ((encoding == NULL) || encoding[0] == '\0') {
2359                /*              gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
2360        } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
2361                do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
2362        } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
2363                do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
2364        } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
2365                do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
2366        } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
2367                /*              gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
2368                  "attempting to convert to UTF-8 anyway\n", encoding);*/
2369                do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
2370        }
2371
2372        /*
2373         * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
2374         * we have been unable to convert the text to utf-8 from the encoding
2375         * that was specified.  So we assume it's UTF-8 and hope for the best.
2376         */
2377        if (*utf8 == 0) {
2378            strncpy(utf8, text, textlen);
2379        }
2380
2381        return utf8;
2382}
2383
2384static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
2385{
2386        struct gaim_connection *gc = sess->aux_data;
2387        va_list ap;
2388        aim_userinfo_t *userinfo;
2389        guint16 infotype;
2390        char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
2391        guint16 text_length;
2392        char *utf8 = NULL;
2393
2394        va_start(ap, fr);
2395        userinfo = va_arg(ap, aim_userinfo_t *);
2396        infotype = va_arg(ap, int);
2397        text_encoding = va_arg(ap, char*);
2398        text = va_arg(ap, char*);
2399        text_length = va_arg(ap, int);
2400        va_end(ap);
2401
2402        if(text_encoding)
2403                extracted_encoding = oscar_encoding_extract(text_encoding);
2404        if(infotype == AIM_GETINFO_GENERALINFO) {
2405                /*Display idle time*/
2406                char buff[256];
2407                struct tm idletime;
2408                if(userinfo->idletime) {
2409                        memset(&idletime, 0, sizeof(struct tm));
2410                        idletime.tm_mday = (userinfo->idletime / 60) / 24;
2411                        idletime.tm_hour = (userinfo->idletime / 60) % 24;
2412                        idletime.tm_min = userinfo->idletime % 60;
2413                        idletime.tm_sec = 0;
2414                        strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
2415                        serv_got_crap(gc, "%s: %s", _("Idle Time"), buff);
2416                }
2417               
2418                if(text) {
2419                        utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2420                        serv_got_crap(gc, "%s\n%s", _("User Info"), utf8);
2421                } else {
2422                        serv_got_crap(gc, _("No user info available."));
2423                }
2424        } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
2425                utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
2426                serv_got_crap(gc, "%s\n%s", _("Away Message"), utf8);
2427        }
2428
2429        g_free(utf8);
2430   
2431        return 1;
2432}
2433
2434int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)
2435{
2436        struct gaim_connection * gc = sess->aux_data;
2437        va_list ap;
2438        guint16 type1, type2;
2439        char * sn;
2440
2441        va_start(ap, fr);
2442        type1 = va_arg(ap, int);
2443        sn = va_arg(ap, char*);
2444        type2 = va_arg(ap, int);
2445        va_end(ap);
2446   
2447        if(type2 == 0x0002) {
2448                /* User is typing */
2449                serv_got_typing(gc, sn, 0, 1);
2450        } 
2451        else if (type2 == 0x0001) {
2452                /* User has typed something, but is not actively typing (stale) */
2453                serv_got_typing(gc, sn, 0, 2);
2454        }
2455        else {
2456                /* User has stopped typing */
2457                serv_got_typing(gc, sn, 0, 0);
2458        }       
2459       
2460        return 1;
2461}
2462
2463static char *oscar_get_status_string( struct gaim_connection *gc, int number )
2464{
2465        struct oscar_data *od = gc->proto_data;
2466       
2467        if( ! number & UC_UNAVAILABLE )
2468        {
2469                return( NULL );
2470        }
2471        else if( od->icq )
2472        {
2473                number >>= 7;
2474                if( number & AIM_ICQ_STATE_DND )
2475                        return( "Do Not Disturb" );
2476                else if( number & AIM_ICQ_STATE_OUT )
2477                        return( "Not Available" );
2478                else if( number & AIM_ICQ_STATE_BUSY )
2479                        return( "Occupied" );
2480                else if( number & AIM_ICQ_STATE_INVISIBLE )
2481                        return( "Invisible" );
2482                else
2483                        return( "Away" );
2484        }
2485        else
2486        {
2487                return( "Away" );
2488        }
2489}
2490
2491int oscar_send_typing(struct gaim_connection *gc, char * who, int typing)
2492{
2493        struct oscar_data *od = gc->proto_data;
2494        return( aim_im_sendmtn(od->sess, 1, who, typing ? 0x0002 : 0x0000) );
2495}
2496
2497int oscar_chat_send(struct gaim_connection * gc, int id, char *message)
2498{
2499        struct oscar_data * od = (struct oscar_data*)gc->proto_data;
2500        struct chat_connection * ccon;
2501        int ret;
2502        guint8 len = strlen(message);
2503        char *s;
2504       
2505        if(!(ccon = find_oscar_chat(gc, id)))
2506                return -1;
2507               
2508        for (s = message; *s; s++)
2509                if (*s & 128)
2510                        break;
2511               
2512        /* Message contains high ASCII chars, time for some translation! */
2513        if (*s) {
2514                s = g_malloc(BUF_LONG);
2515                /* Try if we can put it in an ISO8859-1 string first.
2516                   If we can't, fall back to UTF16. */
2517                if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
2518                        len = ret;
2519                } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
2520                        len = ret;
2521                } else {
2522                        /* OOF, translation failed... Oh well.. */
2523                        g_free( s );
2524                        s = message;
2525                }
2526        } else {
2527                s = message;
2528        }
2529               
2530        ret = aim_chat_send_im(od->sess, ccon->conn, AIM_CHATFLAGS_NOREFLECT, s, len);
2531               
2532        if (s != message) {     
2533                g_free(s);
2534  }
2535 
2536  return (ret >= 0);
2537}
2538
2539void oscar_chat_invite(struct gaim_connection * gc, int id, char *message, char *who)
2540{
2541        struct oscar_data * od = (struct oscar_data *)gc->proto_data;
2542        struct chat_connection *ccon = find_oscar_chat(gc, id);
2543       
2544        if (ccon == NULL)
2545                return;
2546       
2547        aim_chat_invite(od->sess, od->conn, who, message ? message : "",
2548                                        ccon->exchange, ccon->name, 0x0);
2549}
2550
2551void oscar_chat_kill(struct gaim_connection *gc, struct chat_connection *cc)
2552{
2553        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2554
2555        /* Notify the conversation window that we've left the chat */
2556        serv_got_chat_left(gc, cc->id);
2557
2558        /* Destroy the chat_connection */
2559        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
2560        if (cc->inpa > 0)
2561                gaim_input_remove(cc->inpa);
2562        aim_conn_kill(od->sess, &cc->conn);
2563        g_free(cc->name);
2564        g_free(cc->show);
2565        g_free(cc);
2566}
2567
2568void oscar_chat_leave(struct gaim_connection * gc, int id)
2569{
2570        struct chat_connection * ccon = find_oscar_chat(gc, id);
2571
2572        if(ccon == NULL)
2573                return;
2574
2575        oscar_chat_kill(gc, ccon);
2576}
2577
2578int oscar_chat_join(struct gaim_connection * gc, char * name)
2579{
2580    struct oscar_data * od = (struct oscar_data *)gc->proto_data;
2581       
2582        aim_conn_t * cur;
2583
2584        if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) {
2585       
2586                return (aim_chatnav_createroom(od->sess, cur, name, 4) == 0);
2587       
2588        } else {
2589                struct create_room * cr = g_new0(struct create_room, 1);
2590                cr->exchange = 4;
2591                cr->name = g_strdup(name);
2592                od->create_rooms = g_slist_append(od->create_rooms, cr);
2593                aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV);
2594                return 1;
2595        }
2596}
2597
2598int oscar_chat_open(struct gaim_connection * gc, char *who)
2599{
2600        struct oscar_data * od = (struct oscar_data *)gc->proto_data;
2601        int ret;
2602        static int chat_id = 0;
2603        char * chatname = g_new0(char, strlen(gc->username)+4);
2604       
2605        g_snprintf(chatname, strlen(gc->username) + 4, "%s%d", gc->username, chat_id++);
2606 
2607        ret = oscar_chat_join(gc, chatname);
2608
2609        aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0);
2610
2611        g_free(chatname);
2612       
2613        return ret;
2614}
2615
2616void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
2617{
2618        oscar_chat_join(inv->gc, inv->name);
2619        g_free(inv->name);
2620        g_free(inv);
2621}
2622
2623void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
2624{
2625        g_free(inv->name);
2626        g_free(inv);
2627}
2628
2629void oscar_init() 
2630{
2631        struct prpl *ret = g_new0(struct prpl, 1);
2632        ret->name = "oscar";
2633        ret->away_states = oscar_away_states;
2634        ret->login = oscar_login;
2635        ret->close = oscar_close;
2636        ret->send_im = oscar_send_im;
2637        ret->get_info = oscar_get_info;
2638        ret->set_away = oscar_set_away;
2639        ret->get_away = oscar_get_away;
2640        ret->add_buddy = oscar_add_buddy;
2641        ret->remove_buddy = oscar_remove_buddy;
2642        ret->chat_send = oscar_chat_send;
2643        ret->chat_invite = oscar_chat_invite;
2644        ret->chat_leave = oscar_chat_leave;
2645        ret->chat_open = oscar_chat_open;
2646        ret->add_permit = oscar_add_permit;
2647        ret->add_deny = oscar_add_deny;
2648        ret->rem_permit = oscar_rem_permit;
2649        ret->rem_deny = oscar_rem_deny;
2650        ret->set_permit_deny = oscar_set_permit_deny;
2651        ret->keepalive = oscar_keepalive;
2652        ret->cmp_buddynames = aim_sncmp;
2653        ret->get_status_string = oscar_get_status_string;
2654        ret->send_typing = oscar_send_typing;
2655
2656        register_protocol(ret);
2657}
Note: See TracBrowser for help on using the repository browser.