source: protocols/oscar/oscar.c @ 75cde5d

Last change on this file since 75cde5d was 5aa9551e, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-04-17T08:09:55Z

Y2K! ;-)

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