source: protocols/oscar/oscar.c @ e7f46c5

Last change on this file since e7f46c5 was e7f46c5, checked in by Matt Sparks <root@…>, at 2005-12-04T12:58:41Z

Implements solution to typing notifications in ticket #45

  • Property mode set to 100644
File size: 72.0 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 "sock.h"
24#include <errno.h>
25#include <ctype.h>
26#include <string.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <time.h>
30#include <sys/stat.h>
31#include <glib.h>
32#include "nogaim.h"
33#include "bitlbee.h"
34#include "proxy.h"
35
36#include "aim.h"
37#include "icq.h"
38#include "bos.h"
39#include "ssi.h"
40#include "im.h"
41#include "info.h"
42#include "buddylist.h"
43#include "chat.h"
44#include "chatnav.h"
45
46/* constants to identify proto_opts */
47#define USEROPT_AUTH      0
48#define USEROPT_AUTHPORT  1
49
50#define UC_AOL          0x02
51#define UC_ADMIN        0x04
52#define UC_UNCONFIRMED  0x08
53#define UC_NORMAL       0x10
54#define UC_AB           0x20
55#define UC_WIRELESS     0x40
56
57#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
58
59#define OSCAR_GROUP "Friends"
60
61/* Don't know if support for UTF8 is really working. For now it's UTF16 here.
62   static int gaim_caps = AIM_CAPS_UTF8; */
63
64static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY;
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
158#if 0
159static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) {
160        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
161        struct chat_connection *c = NULL;
162
163        while (g) {
164                c = (struct chat_connection *)g->data;
165                if (c->id == id)
166                        break;
167                g = g->next;
168                c = NULL;
169        }
170
171        return c;
172}
173#endif
174
175static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc,
176                                                        aim_conn_t *conn) {
177        GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
178        struct chat_connection *c = NULL;
179
180        while (g) {
181                c = (struct chat_connection *)g->data;
182                if (c->conn == conn)
183                        break;
184                g = g->next;
185                c = NULL;
186        }
187
188        return c;
189}
190
191static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
192static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
193static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
194static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
195static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
196static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
197static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
198static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
199static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
200static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
201static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
202static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
203static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
204static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
205static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
206static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
207static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
208static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
209static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
210static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
211static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
212static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
213static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
214static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
215static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
216static int gaim_memrequest       (aim_session_t *, aim_frame_t *, ...);
217static int gaim_selfinfo         (aim_session_t *, aim_frame_t *, ...);
218static int gaim_offlinemsg       (aim_session_t *, aim_frame_t *, ...);
219static int gaim_offlinemsgdone   (aim_session_t *, aim_frame_t *, ...);
220static int gaim_ssi_parserights  (aim_session_t *, aim_frame_t *, ...);
221static int gaim_ssi_parselist    (aim_session_t *, aim_frame_t *, ...);
222static int gaim_ssi_parseack     (aim_session_t *, aim_frame_t *, ...);
223static int gaim_parsemtn         (aim_session_t *, aim_frame_t *, ...);
224static int gaim_icqinfo          (aim_session_t *, aim_frame_t *, ...);
225static int gaim_parseaiminfo     (aim_session_t *, aim_frame_t *, ...);
226
227static char *msgerrreason[] = {
228        "Invalid error",
229        "Invalid SNAC",
230        "Rate to host",
231        "Rate to client",
232        "Not logged in",
233        "Service unavailable",
234        "Service not defined",
235        "Obsolete SNAC",
236        "Not supported by host",
237        "Not supported by client",
238        "Refused by client",
239        "Reply too big",
240        "Responses lost",
241        "Request denied",
242        "Busted SNAC payload",
243        "Insufficient rights",
244        "In local permit/deny",
245        "Too evil (sender)",
246        "Too evil (receiver)",
247        "User temporarily unavailable",
248        "No match",
249        "List overflow",
250        "Request ambiguous",
251        "Queue full",
252        "Not while on AOL"
253};
254static int msgerrreasonlen = 25;
255
256static void oscar_callback(gpointer data, gint source,
257                                GaimInputCondition condition) {
258        aim_conn_t *conn = (aim_conn_t *)data;
259        aim_session_t *sess = aim_conn_getsess(conn);
260        struct gaim_connection *gc = sess ? sess->aux_data : NULL;
261        struct oscar_data *odata;
262
263        if (!gc) {
264                /* gc is null. we return, else we seg SIGSEG on next line. */
265                return;
266        }
267     
268        if (!g_slist_find(get_connections(), gc)) {
269                /* oh boy. this is probably bad. i guess the only thing we
270                 * can really do is return? */
271                return;
272        }
273
274        odata = (struct oscar_data *)gc->proto_data;
275
276        if (condition & GAIM_INPUT_READ) {
277                if (aim_get_command(odata->sess, conn) >= 0) {
278                        aim_rxdispatch(odata->sess);
279                               if (odata->killme)
280                                       signoff(gc);
281                } else {
282                        if ((conn->type == AIM_CONN_TYPE_BOS) ||
283                                   !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) {
284                                hide_login_progress_error(gc, _("Disconnected."));
285                                signoff(gc);
286                        } else if (conn->type == AIM_CONN_TYPE_CHAT) {
287                                struct chat_connection *c = find_oscar_chat_by_conn(gc, conn);
288                                char buf[BUF_LONG];
289                                c->conn = NULL;
290                                if (c->inpa > 0)
291                                        gaim_input_remove(c->inpa);
292                                c->inpa = 0;
293                                c->fd = -1;
294                                aim_conn_kill(odata->sess, &conn);
295                                sprintf(buf, _("You have been disconnected from chat room %s."), c->name);
296                                do_error_dialog(sess->aux_data, buf, _("Chat Error!"));
297                        } else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
298                                if (odata->cnpa > 0)
299                                        gaim_input_remove(odata->cnpa);
300                                odata->cnpa = 0;
301                                while (odata->create_rooms) {
302                                        struct create_room *cr = odata->create_rooms->data;
303                                        g_free(cr->name);
304                                        odata->create_rooms =
305                                                g_slist_remove(odata->create_rooms, cr);
306                                        g_free(cr);
307                                        do_error_dialog(sess->aux_data, _("Chat is currently unavailable"),
308                                                        _("Gaim - Chat"));
309                                }
310                                aim_conn_kill(odata->sess, &conn);
311                        } else if (conn->type == AIM_CONN_TYPE_AUTH) {
312                                if (odata->paspa > 0)
313                                        gaim_input_remove(odata->paspa);
314                                odata->paspa = 0;
315                                aim_conn_kill(odata->sess, &conn);
316                        } else {
317                                aim_conn_kill(odata->sess, &conn);
318                        }
319                }
320        }
321}
322
323static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond)
324{
325        struct gaim_connection *gc = data;
326        struct oscar_data *odata;
327        aim_session_t *sess;
328        aim_conn_t *conn;
329
330        if (!g_slist_find(get_connections(), gc)) {
331                closesocket(source);
332                return;
333        }
334
335        odata = gc->proto_data;
336        sess = odata->sess;
337        conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
338
339        if (source < 0) {
340                hide_login_progress(gc, _("Couldn't connect to host"));
341                signoff(gc);
342                return;
343        }
344
345        aim_conn_completeconnect(sess, conn);
346        gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ,
347                        oscar_callback, conn);
348}
349
350static void oscar_login(struct aim_user *user) {
351        aim_session_t *sess;
352        aim_conn_t *conn;
353        char buf[256];
354        struct gaim_connection *gc = new_gaim_conn(user);
355        struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1);
356
357        if (isdigit(*user->username)) {
358                odata->icq = TRUE;
359                /* this is odd but it's necessary for a proper do_import and do_export */
360                gc->protocol = PROTO_ICQ;
361                gc->password[8] = 0;
362        } else {
363                gc->protocol = PROTO_TOC;
364                gc->flags |= OPT_CONN_HTML;
365        }
366
367        sess = g_new0(aim_session_t, 1);
368
369        aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
370
371        /* we need an immediate queue because we don't use a while-loop to
372         * see if things need to be sent. */
373        aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
374        odata->sess = sess;
375        sess->aux_data = gc;
376
377        conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
378        if (conn == NULL) {
379                hide_login_progress(gc, _("Unable to login to AIM"));
380                signoff(gc);
381                return;
382        }
383       
384        if (g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.icq.com") != 0 &&
385            g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.oscar.aol.com") != 0) {
386                serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.");
387        }
388       
389        g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username);
390        set_login_progress(gc, 2, buf);
391
392        aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
393        aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
394
395        conn->status |= AIM_CONN_STATUS_INPROGRESS;
396        conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ?
397                                        user->proto_opt[USEROPT_AUTH] : AIM_DEFAULT_LOGIN_SERVER,
398                                 user->proto_opt[USEROPT_AUTHPORT][0] ?
399                                        atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
400                                 oscar_login_connect, gc);
401        if (conn->fd < 0) {
402                hide_login_progress(gc, _("Couldn't connect to host"));
403                signoff(gc);
404                return;
405        }
406        aim_request_login(sess, conn, gc->username);
407}
408
409static void oscar_close(struct gaim_connection *gc) {
410        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
411       
412        while (odata->oscar_chats) {
413                struct chat_connection *n = odata->oscar_chats->data;
414                if (n->inpa > 0)
415                        gaim_input_remove(n->inpa);
416                g_free(n->name);
417                g_free(n->show);
418                odata->oscar_chats = g_slist_remove(odata->oscar_chats, n);
419                g_free(n);
420        }
421        while (odata->create_rooms) {
422                struct create_room *cr = odata->create_rooms->data;
423                g_free(cr->name);
424                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
425                g_free(cr);
426        }
427        if (odata->email)
428                g_free(odata->email);
429        if (odata->newp)
430                g_free(odata->newp);
431        if (odata->oldp)
432                g_free(odata->oldp);
433        if (gc->inpa > 0)
434                gaim_input_remove(gc->inpa);
435        if (odata->cnpa > 0)
436                gaim_input_remove(odata->cnpa);
437        if (odata->paspa > 0)
438                gaim_input_remove(odata->paspa);
439        aim_session_kill(odata->sess);
440        g_free(odata->sess);
441        odata->sess = NULL;
442        g_free(gc->proto_data);
443        gc->proto_data = NULL;
444}
445
446static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) {
447        struct gaim_connection *gc = data;
448        struct oscar_data *odata;
449        aim_session_t *sess;
450        aim_conn_t *bosconn;
451
452        if (!g_slist_find(get_connections(), gc)) {
453                closesocket(source);
454                return;
455        }
456
457        odata = gc->proto_data;
458        sess = odata->sess;
459        bosconn = odata->conn;
460
461        if (source < 0) {
462                hide_login_progress(gc, _("Could Not Connect"));
463                signoff(gc);
464                return;
465        }
466
467        aim_conn_completeconnect(sess, bosconn);
468        gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ,
469                        oscar_callback, bosconn);
470        set_login_progress(gc, 4, _("Connection established, cookie sent"));
471}
472
473static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
474        va_list ap;
475        struct aim_authresp_info *info;
476        int i; char *host; int port;
477        struct aim_user *user;
478        aim_conn_t *bosconn;
479
480        struct gaim_connection *gc = sess->aux_data;
481        struct oscar_data *od = gc->proto_data;
482        user = gc->user;
483        port = user->proto_opt[USEROPT_AUTHPORT][0] ?
484                atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
485
486        va_start(ap, fr);
487        info = va_arg(ap, struct aim_authresp_info *);
488        va_end(ap);
489
490        if (info->errorcode || !info->bosip || !info->cookie) {
491                switch (info->errorcode) {
492                case 0x05:
493                        /* Incorrect nick/password */
494                        hide_login_progress(gc, _("Incorrect nickname or password."));
495//                      plugin_event(event_error, (void *)980, 0, 0, 0);
496                        break;
497                case 0x11:
498                        /* Suspended account */
499                        hide_login_progress(gc, _("Your account is currently suspended."));
500                        break;
501                case 0x18:
502                        /* connecting too frequently */
503                        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."));
504                        break;
505                case 0x1c:
506                        /* client too old */
507                        hide_login_progress(gc, _("The client version you are using is too old. Please upgrade at " WEBSITE));
508                        break;
509                default:
510                        hide_login_progress(gc, _("Authentication Failed"));
511                        break;
512                }
513                od->killme = TRUE;
514                return 1;
515        }
516
517
518        aim_conn_kill(sess, &fr->conn);
519
520        bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL);
521        if (bosconn == NULL) {
522                hide_login_progress(gc, _("Internal Error"));
523                od->killme = TRUE;
524                return 0;
525        }
526
527        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0);
528        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_RIGHTS, gaim_bosrights, 0);
529        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
530        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
531        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0);
532        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
533        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
534        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
535        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
536        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
537        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
538        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
539        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
540        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
541        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_PARAMINFO, gaim_icbm_param_info, 0);
542        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_ERROR, gaim_parse_genericerr, 0);
543        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ERROR, gaim_parse_genericerr, 0);
544        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_ERROR, gaim_parse_genericerr, 0);
545        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x1f, gaim_memrequest, 0);
546        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SELFINFO, gaim_selfinfo, 0);
547        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
548        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
549        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0);
550        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
551        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
552        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);
553        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0);
554        aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parsemtn, 0);
555
556        ((struct oscar_data *)gc->proto_data)->conn = bosconn;
557        for (i = 0; i < (int)strlen(info->bosip); i++) {
558                if (info->bosip[i] == ':') {
559                        port = atoi(&(info->bosip[i+1]));
560                        break;
561                }
562        }
563        host = g_strndup(info->bosip, i);
564        bosconn->status |= AIM_CONN_STATUS_INPROGRESS;
565        bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc);
566        g_free(host);
567        if (bosconn->fd < 0) {
568                hide_login_progress(gc, _("Could Not Connect"));
569                od->killme = TRUE;
570                return 0;
571        }
572        aim_sendcookie(sess, bosconn, info->cookie);
573        gaim_input_remove(gc->inpa);
574
575        return 1;
576}
577
578struct pieceofcrap {
579        struct gaim_connection *gc;
580        unsigned long offset;
581        unsigned long len;
582        char *modname;
583        int fd;
584        aim_conn_t *conn;
585        unsigned int inpa;
586};
587
588static void damn_you(gpointer data, gint source, GaimInputCondition c)
589{
590        struct pieceofcrap *pos = data;
591        struct oscar_data *od = pos->gc->proto_data;
592        char in = '\0';
593        int x = 0;
594        unsigned char m[17];
595
596        while (read(pos->fd, &in, 1) == 1) {
597                if (in == '\n')
598                        x++;
599                else if (in != '\r')
600                        x = 0;
601                if (x == 2)
602                        break;
603                in = '\0';
604        }
605        if (in != '\n') {
606                do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
607                                " You may be disconnected shortly.", "Login Error");
608                gaim_input_remove(pos->inpa);
609                closesocket(pos->fd);
610                g_free(pos);
611                return;
612        }
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
1078static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
1079#if 0
1080        struct gaim_connection *gc = sess->aux_data;
1081#endif
1082
1083        if (args->status != AIM_RENDEZVOUS_PROPOSE)
1084                return 1;
1085#if 0
1086        if (args->reqclass & AIM_CAPS_CHAT) {
1087                char *name = extract_name(args->info.chat.roominfo.name);
1088                int *exch = g_new0(int, 1);
1089                GList *m = NULL;
1090                m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
1091                *exch = args->info.chat.roominfo.exchange;
1092                m = g_list_append(m, exch);
1093                serv_got_chat_invite(gc,
1094                                     name ? name : args->info.chat.roominfo.name,
1095                                     userinfo->sn,
1096                                     (char *)args->msg,
1097                                     m);
1098                if (name)
1099                        g_free(name);
1100        }
1101#endif
1102        return 1;
1103}
1104
1105static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
1106        char *uin, message;
1107        struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
1108       
1109        uin = g_strdup_printf("%u", data->uin);
1110        message = 0;
1111        aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
1112        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
1113        show_got_added(data->gc, NULL, uin, NULL, NULL);
1114       
1115        g_free(uin);
1116        g_free(data);
1117}
1118
1119static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
1120        char *uin, *message;
1121        struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
1122       
1123        uin = g_strdup_printf("%u", data->uin);
1124        message = g_strdup_printf("No reason given.");
1125        aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
1126        // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
1127        g_free(message);
1128       
1129        g_free(uin);
1130        g_free(data);
1131}
1132
1133/*
1134 * For when other people ask you for authorization
1135 */
1136static void gaim_icq_authask(struct gaim_connection *gc, guint32 uin, char *msg) {
1137        struct icq_auth *data = g_new(struct icq_auth, 1);
1138        char *reason = NULL;
1139        char *dialog_msg;
1140       
1141        if (strlen(msg) > 6)
1142                reason = msg + 6;
1143       
1144        dialog_msg = g_strdup_printf("The user %u wants to add you to their buddy list for the following reason:\n\n%s", uin, reason ? reason : "No reason given.");
1145        data->gc = gc;
1146        data->uin = uin;
1147        do_ask_dialog(gc, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
1148        g_free(dialog_msg);
1149}
1150
1151static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
1152        struct gaim_connection *gc = sess->aux_data;
1153
1154        switch (args->type) {
1155                case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
1156                        char *uin, *message;
1157                        uin = g_strdup_printf("%u", args->uin);
1158                        message = g_strdup(args->msg);
1159                        strip_linefeed(message);
1160                        serv_got_im(gc, uin, message, 0, time(NULL), -1);
1161                        g_free(uin);
1162                        g_free(message);
1163                } break;
1164
1165                case 0x0004: { /* Someone sent you a URL */
1166                        char *uin, *message;
1167                        char **m;
1168       
1169                        uin = g_strdup_printf("%u", args->uin);
1170                        m = g_strsplit(args->msg, "\376", 2);
1171
1172                        if ((strlen(m[0]) != 0)) {
1173                          message = g_strjoinv(" -- ", m);
1174                        } else {
1175                          message = m[1];
1176                        }
1177
1178                        strip_linefeed(message);
1179                        serv_got_im(gc, uin, message, 0, time(NULL), -1);
1180                        g_free(uin);
1181                        g_free(m);
1182                        g_free(message);
1183                } break;
1184               
1185                case 0x0006: { /* Someone requested authorization */
1186                        gaim_icq_authask(gc, args->uin, args->msg);
1187                } break;
1188
1189                case 0x0007: { /* Someone has denied you authorization */
1190                        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.") );
1191                } break;
1192
1193                case 0x0008: { /* Someone has granted you authorization */
1194                        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.") );
1195                } break;
1196
1197                case 0x0012: {
1198                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1199                } break;
1200
1201                default: {;
1202                } break;
1203        }
1204
1205        return 1;
1206}
1207
1208static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
1209        int channel, ret = 0;
1210        aim_userinfo_t *userinfo;
1211        va_list ap;
1212
1213        va_start(ap, fr);
1214        channel = va_arg(ap, int);
1215        userinfo = va_arg(ap, aim_userinfo_t *);
1216
1217    if (set_getint(sess->aux_data, "debug")) {
1218        serv_got_crap(sess->aux_data, "channel %i called", channel);
1219    }
1220
1221        switch (channel) {
1222                case 1: { /* standard message */
1223                        struct aim_incomingim_ch1_args *args;
1224                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
1225                        ret = incomingim_chan1(sess, fr->conn, userinfo, args);
1226                } break;
1227
1228                case 2: { /* rendevous */
1229                        struct aim_incomingim_ch2_args *args;
1230                        args = va_arg(ap, struct aim_incomingim_ch2_args *);
1231                        ret = incomingim_chan2(sess, fr->conn, userinfo, args);
1232                } break;
1233
1234                case 4: { /* ICQ */
1235                        struct aim_incomingim_ch4_args *args;
1236                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
1237                        ret = incomingim_chan4(sess, fr->conn, userinfo, args);
1238                } break;
1239
1240                default: {;
1241                } break;
1242        }
1243
1244        va_end(ap);
1245
1246        return ret;
1247}
1248
1249static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
1250        va_list ap;
1251        guint16 chan, nummissed, reason;
1252        aim_userinfo_t *userinfo;
1253        char buf[1024];
1254
1255        va_start(ap, fr);
1256        chan = (guint16)va_arg(ap, unsigned int);
1257        userinfo = va_arg(ap, aim_userinfo_t *);
1258        nummissed = (guint16)va_arg(ap, unsigned int);
1259        reason = (guint16)va_arg(ap, unsigned int);
1260        va_end(ap);
1261
1262        switch(reason) {
1263                case 0:
1264                        /* Invalid (0) */
1265                        g_snprintf(buf,
1266                                   sizeof(buf),
1267                                   nummissed == 1 ? 
1268                                   _("You missed %d message from %s because it was invalid.") :
1269                                   _("You missed %d messages from %s because they were invalid."),
1270                                   nummissed,
1271                                   userinfo->sn);
1272                        break;
1273                case 1:
1274                        /* Message too large */
1275                        g_snprintf(buf,
1276                                   sizeof(buf),
1277                                   nummissed == 1 ?
1278                                   _("You missed %d message from %s because it was too large.") :
1279                                   _("You missed %d messages from %s because they were too large."),
1280                                   nummissed,
1281                                   userinfo->sn);
1282                        break;
1283                case 2:
1284                        /* Rate exceeded */
1285                        g_snprintf(buf,
1286                                   sizeof(buf),
1287                                   nummissed == 1 ? 
1288                                   _("You missed %d message from %s because the rate limit has been exceeded.") :
1289                                   _("You missed %d messages from %s because the rate limit has been exceeded."),
1290                                   nummissed,
1291                                   userinfo->sn);
1292                        break;
1293                case 3:
1294                        /* Evil Sender */
1295                        g_snprintf(buf,
1296                                   sizeof(buf),
1297                                   nummissed == 1 ?
1298                                   _("You missed %d message from %s because it was too evil.") : 
1299                                   _("You missed %d messages from %s because they are too evil."),
1300                                   nummissed,
1301                                   userinfo->sn);
1302                        break;
1303                case 4:
1304                        /* Evil Receiver */
1305                        g_snprintf(buf,
1306                                   sizeof(buf),
1307                                   nummissed == 1 ? 
1308                                   _("You missed %d message from %s because you are too evil.") :
1309                                   _("You missed %d messages from %s because you are too evil."),
1310                                   nummissed,
1311                                   userinfo->sn);
1312                        break;
1313                default:
1314                        g_snprintf(buf,
1315                                   sizeof(buf),
1316                                   nummissed == 1 ? 
1317                                   _("You missed %d message from %s for unknown reasons.") :
1318                                   _("You missed %d messages from %s for unknown reasons."),
1319                                   nummissed,
1320                                   userinfo->sn);
1321                        break;
1322        }
1323        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1324
1325        return 1;
1326}
1327
1328static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1329        va_list ap;
1330        guint16 reason;
1331        char *m;
1332
1333        va_start(ap, fr);
1334        reason = (guint16)va_arg(ap, unsigned int);
1335        va_end(ap);
1336
1337        m = g_strdup_printf(_("SNAC threw error: %s\n"),
1338                        reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
1339        do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error"));
1340        g_free(m);
1341
1342        return 1;
1343}
1344
1345static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1346        va_list ap;
1347        char *destn;
1348        guint16 reason;
1349        char buf[1024];
1350
1351        va_start(ap, fr);
1352        reason = (guint16)va_arg(ap, unsigned int);
1353        destn = va_arg(ap, char *);
1354        va_end(ap);
1355
1356        sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
1357                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1358        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1359
1360        return 1;
1361}
1362
1363static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
1364        va_list ap;
1365        char *destn;
1366        guint16 reason;
1367        char buf[1024];
1368
1369        va_start(ap, fr);
1370        reason = (guint16)va_arg(ap, unsigned int);
1371        destn = va_arg(ap, char *);
1372        va_end(ap);
1373
1374        sprintf(buf, _("User information for %s unavailable: %s"), destn,
1375                        (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
1376        do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
1377
1378
1379        return 1;
1380}
1381
1382static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
1383        char *msg;
1384        guint16 id;
1385        va_list ap;
1386
1387        va_start(ap, fr);
1388        id  = (guint16)va_arg(ap, unsigned int);
1389        msg = va_arg(ap, char *);
1390        va_end(ap);
1391
1392        if (id < 4)
1393                do_error_dialog(sess->aux_data, _("Your connection may be lost."),
1394                                _("AOL error"));
1395
1396        return 1;
1397}
1398
1399static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1400        va_list ap;
1401        guint16 type;
1402        struct gaim_connection *gc = sess->aux_data;
1403        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1404
1405        va_start(ap, fr);
1406        type = (guint16)va_arg(ap, unsigned int);
1407
1408        switch(type) {
1409                case 0x0002: {
1410                        guint8 maxrooms;
1411                        struct aim_chat_exchangeinfo *exchanges;
1412                        int exchangecount; // i;
1413
1414                        maxrooms = (guint8)va_arg(ap, unsigned int);
1415                        exchangecount = va_arg(ap, int);
1416                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
1417                        va_end(ap);
1418
1419                        while (odata->create_rooms) {
1420                                struct create_room *cr = odata->create_rooms->data;
1421                                aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
1422                                g_free(cr->name);
1423                                odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
1424                                g_free(cr);
1425                        }
1426                        }
1427                        break;
1428                case 0x0008: {
1429                        char *fqcn, *name, *ck;
1430                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
1431                        guint8 createperms;
1432                        guint32 createtime;
1433
1434                        fqcn = va_arg(ap, char *);
1435                        instance = (guint16)va_arg(ap, unsigned int);
1436                        exchange = (guint16)va_arg(ap, unsigned int);
1437                        flags = (guint16)va_arg(ap, unsigned int);
1438                        createtime = va_arg(ap, guint32);
1439                        maxmsglen = (guint16)va_arg(ap, unsigned int);
1440                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
1441                        createperms = (guint8)va_arg(ap, int);
1442                        unknown = (guint16)va_arg(ap, unsigned int);
1443                        name = va_arg(ap, char *);
1444                        ck = va_arg(ap, char *);
1445                        va_end(ap);
1446
1447                        aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
1448                        }
1449                        break;
1450                default:
1451                        va_end(ap);
1452                        break;
1453        }
1454        return 1;
1455}
1456
1457static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
1458        va_list ap;
1459        int count, i;
1460        aim_userinfo_t *info;
1461        struct gaim_connection *g = sess->aux_data;
1462
1463        struct chat_connection *c = NULL;
1464
1465        va_start(ap, fr);
1466        count = va_arg(ap, int);
1467        info  = va_arg(ap, aim_userinfo_t *);
1468        va_end(ap);
1469
1470        c = find_oscar_chat_by_conn(g, fr->conn);
1471        if (!c)
1472                return 1;
1473
1474        for (i = 0; i < count; i++)
1475                add_chat_buddy(c->cnv, info[i].sn);
1476
1477        return 1;
1478}
1479
1480static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
1481        va_list ap;
1482        int count, i;
1483        aim_userinfo_t *info;
1484        struct gaim_connection *g = sess->aux_data;
1485
1486        struct chat_connection *c = NULL;
1487
1488        va_start(ap, fr);
1489        count = va_arg(ap, int);
1490        info  = va_arg(ap, aim_userinfo_t *);
1491        va_end(ap);
1492
1493        c = find_oscar_chat_by_conn(g, fr->conn);
1494        if (!c)
1495                return 1;
1496
1497        for (i = 0; i < count; i++)
1498                remove_chat_buddy(c->cnv, info[i].sn, NULL);
1499
1500        return 1;
1501}
1502
1503static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
1504        va_list ap;
1505        aim_userinfo_t *userinfo;
1506        struct aim_chat_roominfo *roominfo;
1507        char *roomname;
1508        int usercount;
1509        char *roomdesc;
1510        guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
1511        guint32 creationtime;
1512        struct gaim_connection *gc = sess->aux_data;
1513        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1514
1515        va_start(ap, fr);
1516        roominfo = va_arg(ap, struct aim_chat_roominfo *);
1517        roomname = va_arg(ap, char *);
1518        usercount= va_arg(ap, int);
1519        userinfo = va_arg(ap, aim_userinfo_t *);
1520        roomdesc = va_arg(ap, char *);
1521        unknown_c9 = (guint16)va_arg(ap, int);
1522        creationtime = (guint32)va_arg(ap, unsigned long);
1523        maxmsglen = (guint16)va_arg(ap, int);
1524        unknown_d2 = (guint16)va_arg(ap, int);
1525        unknown_d5 = (guint16)va_arg(ap, int);
1526        maxvisiblemsglen = (guint16)va_arg(ap, int);
1527        va_end(ap);
1528
1529        ccon->maxlen = maxmsglen;
1530        ccon->maxvis = maxvisiblemsglen;
1531
1532        return 1;
1533}
1534
1535static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
1536        va_list ap;
1537        aim_userinfo_t *info;
1538        char *msg;
1539        struct gaim_connection *gc = sess->aux_data;
1540        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
1541        char *tmp;
1542
1543        va_start(ap, fr);
1544        info = va_arg(ap, aim_userinfo_t *);
1545        msg  = va_arg(ap, char *);
1546
1547        tmp = g_malloc(BUF_LONG);
1548        g_snprintf(tmp, BUF_LONG, "%s", msg);
1549        serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL));
1550        g_free(tmp);
1551
1552        return 1;
1553}
1554
1555static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
1556#if 0
1557        static const char *codes[5] = {
1558                "invalid",
1559                 "change",
1560                 "warning",
1561                 "limit",
1562                 "limit cleared",
1563        };
1564#endif
1565        va_list ap;
1566        guint16 code, rateclass;
1567        guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
1568
1569        va_start(ap, fr); 
1570        code = (guint16)va_arg(ap, unsigned int);
1571        rateclass= (guint16)va_arg(ap, unsigned int);
1572        windowsize = (guint32)va_arg(ap, unsigned long);
1573        clear = (guint32)va_arg(ap, unsigned long);
1574        alert = (guint32)va_arg(ap, unsigned long);
1575        limit = (guint32)va_arg(ap, unsigned long);
1576        disconnect = (guint32)va_arg(ap, unsigned long);
1577        currentavg = (guint32)va_arg(ap, unsigned long);
1578        maxavg = (guint32)va_arg(ap, unsigned long);
1579        va_end(ap);
1580
1581        /* XXX fix these values */
1582        if (code == AIM_RATE_CODE_CHANGE) {
1583                if (currentavg >= clear)
1584                        aim_conn_setlatency(fr->conn, 0);
1585        } else if (code == AIM_RATE_CODE_WARNING) {
1586                aim_conn_setlatency(fr->conn, windowsize/4);
1587        } else if (code == AIM_RATE_CODE_LIMIT) {
1588                do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
1589                                  "Please wait 10 seconds and try again."), _("Gaim - Error"));
1590                aim_conn_setlatency(fr->conn, windowsize/2);
1591        } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
1592                aim_conn_setlatency(fr->conn, 0);
1593        }
1594
1595        return 1;
1596}
1597
1598static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
1599        va_list ap;
1600        aim_userinfo_t *info;
1601        struct gaim_connection *gc = sess->aux_data;
1602
1603        va_start(ap, fr);
1604        info = va_arg(ap, aim_userinfo_t *);
1605        va_end(ap);
1606
1607        gc->evil = info->warnlevel/10;
1608        /* gc->correction_time = (info->onlinesince - gc->login_time); */
1609
1610        return 1;
1611}
1612
1613static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
1614
1615        aim_reqpersonalinfo(sess, fr->conn);
1616        aim_bos_reqlocaterights(sess, fr->conn);
1617        aim_bos_reqbuddyrights(sess, fr->conn);
1618
1619        aim_reqicbmparams(sess);
1620
1621        aim_bos_reqrights(sess, fr->conn);
1622        aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
1623        aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
1624                                                     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
1625
1626        return 1;
1627}
1628
1629static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
1630        struct gaim_connection *gc = sess->aux_data;
1631        struct oscar_data *od = gc->proto_data;
1632
1633        aim_clientready(sess, fr->conn);
1634
1635        if (od->chpass) {
1636                aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
1637                g_free(od->oldp);
1638                od->oldp = NULL;
1639                g_free(od->newp);
1640                od->newp = NULL;
1641                od->chpass = FALSE;
1642        }
1643        if (od->setnick) {
1644                aim_admin_setnick(sess, fr->conn, od->newsn);
1645                g_free(od->newsn);
1646                od->newsn = NULL;
1647                od->setnick = FALSE;
1648        }
1649        if (od->conf) {
1650                aim_admin_reqconfirm(sess, fr->conn);
1651                od->conf = FALSE;
1652        }
1653        if (od->reqemail) {
1654                aim_admin_getinfo(sess, fr->conn, 0x0011);
1655                od->reqemail = FALSE;
1656        }
1657        if (od->setemail) {
1658                aim_admin_setemail(sess, fr->conn, od->email);
1659                g_free(od->email);
1660                od->setemail = FALSE;
1661        }
1662
1663        return 1;
1664}
1665
1666static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
1667        struct aim_icbmparameters *params;
1668        va_list ap;
1669
1670        va_start(ap, fr);
1671        params = va_arg(ap, struct aim_icbmparameters *);
1672        va_end(ap);
1673
1674        /* Maybe senderwarn and recverwarn should be user preferences... */
1675        params->flags = 0x0000000b;
1676        params->maxmsglen = 8000;
1677        params->minmsginterval = 0;
1678
1679        aim_seticbmparam(sess, params);
1680
1681        return 1;
1682}
1683
1684static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
1685{
1686        va_list ap;
1687        guint16 maxsiglen;
1688        struct gaim_connection *gc = sess->aux_data;
1689        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1690
1691        va_start(ap, fr);
1692        maxsiglen = va_arg(ap, int);
1693        va_end(ap);
1694
1695        odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
1696
1697        aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps);
1698
1699        return 1;
1700}
1701
1702static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1703        va_list ap;
1704        guint16 maxbuddies, maxwatchers;
1705        struct gaim_connection *gc = sess->aux_data;
1706        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1707
1708        va_start(ap, fr);
1709        maxbuddies = (guint16)va_arg(ap, unsigned int);
1710        maxwatchers = (guint16)va_arg(ap, unsigned int);
1711        va_end(ap);
1712
1713        odata->rights.maxbuddies = (guint)maxbuddies;
1714        odata->rights.maxwatchers = (guint)maxwatchers;
1715
1716        return 1;
1717}
1718
1719static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
1720        guint16 maxpermits, maxdenies;
1721        va_list ap;
1722        struct gaim_connection *gc = sess->aux_data;
1723        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1724
1725        va_start(ap, fr);
1726        maxpermits = (guint16)va_arg(ap, unsigned int);
1727        maxdenies = (guint16)va_arg(ap, unsigned int);
1728        va_end(ap);
1729
1730        odata->rights.maxpermits = (guint)maxpermits;
1731        odata->rights.maxdenies = (guint)maxdenies;
1732
1733//      serv_finish_login(gc);
1734
1735        if (bud_list_cache_exists(gc))
1736                do_import(gc, NULL);
1737
1738        aim_clientready(sess, fr->conn);
1739
1740        aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
1741
1742        aim_ssi_reqrights(sess, fr->conn);
1743        aim_ssi_reqalldata(sess, fr->conn);
1744
1745        return 1;
1746}
1747
1748static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
1749        va_list ap;
1750        struct aim_icq_offlinemsg *msg;
1751        struct gaim_connection *gc = sess->aux_data;
1752
1753        va_start(ap, fr);
1754        msg = va_arg(ap, struct aim_icq_offlinemsg *);
1755        va_end(ap);
1756
1757        switch (msg->type) {
1758                case 0x0001: { /* Basic offline message */
1759                        char sender[32];
1760                        char *dialog_msg = g_strdup(msg->msg);
1761                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1762                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1763                        strip_linefeed(dialog_msg);
1764                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1765                        g_free(dialog_msg);
1766                } break;
1767
1768                case 0x0004: { /* Someone sent you a URL */
1769                        char sender[32];
1770                        char *dialog_msg;
1771                        char **m;
1772
1773                        time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
1774                        g_snprintf(sender, sizeof(sender), "%u", msg->sender);
1775
1776                        m = g_strsplit(msg->msg, "\376", 2);
1777
1778                        if ((strlen(m[0]) != 0)) {
1779                          dialog_msg = g_strjoinv(" -- ", m);
1780                        } else {
1781                          dialog_msg = m[1];
1782                        }
1783
1784                        strip_linefeed(dialog_msg);
1785                        serv_got_im(gc, sender, dialog_msg, 0, t, -1);
1786                        g_free(dialog_msg);
1787                        g_free(m);
1788                } break;
1789               
1790                case 0x0006: { /* Authorization request */
1791                        gaim_icq_authask(gc, msg->sender, msg->msg);
1792                } break;
1793
1794                case 0x0007: { /* Someone has denied you authorization */
1795                        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.") );
1796                } break;
1797
1798                case 0x0008: { /* Someone has granted you authorization */
1799                        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.") );
1800                } break;
1801
1802                case 0x0012: {
1803                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
1804                } break;
1805
1806                default: {;
1807                }
1808        }
1809
1810        return 1;
1811}
1812
1813static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
1814{
1815        aim_icq_ackofflinemsgs(sess);
1816        return 1;
1817}
1818
1819static void oscar_keepalive(struct gaim_connection *gc) {
1820        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1821        aim_flap_nop(odata->sess, odata->conn);
1822}
1823
1824static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) {
1825        struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
1826        int ret = 0;
1827        if (imflags & IM_FLAG_AWAY) {
1828                ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
1829        } else {
1830                struct aim_sendimext_args args;
1831                char *s;
1832               
1833                args.flags = AIM_IMFLAGS_ACK;
1834                if (odata->icq)
1835                        args.flags |= AIM_IMFLAGS_OFFLINE;
1836                for (s = message; *s; s++)
1837                        if (*s & 128)
1838                                break;
1839               
1840                /* Message contains high ASCII chars, time for some translation! */
1841                if (*s) {
1842                        s = g_malloc(BUF_LONG);
1843                        /* Try if we can put it in an ISO8859-1 string first.
1844                           If we can't, fall back to UTF16. */
1845                        if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
1846                                args.flags |= AIM_IMFLAGS_ISO_8859_1;
1847                                len = ret;
1848                        } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
1849                                args.flags |= AIM_IMFLAGS_UNICODE;
1850                                len = ret;
1851                        } else {
1852                                /* OOF, translation failed... Oh well.. */
1853                                g_free( s );
1854                                s = message;
1855                        }
1856                } else {
1857                        s = message;
1858                }
1859               
1860                args.features = gaim_features;
1861                args.featureslen = sizeof(gaim_features);
1862               
1863                args.destsn = name;
1864                args.msg    = s;
1865                args.msglen = len;
1866               
1867                ret = aim_send_im_ext(odata->sess, &args);
1868               
1869                if (s != message) {
1870                        g_free(s);
1871                }
1872        }
1873        if (ret >= 0)
1874                return 1;
1875        return ret;
1876}
1877
1878static void oscar_get_info(struct gaim_connection *g, char *name) {
1879        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1880        if (odata->icq)
1881                aim_icq_getallinfo(odata->sess, name);
1882        else {
1883                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
1884                aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
1885        }
1886}
1887
1888static void oscar_get_away(struct gaim_connection *g, char *who) {
1889        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
1890        if (odata->icq) {
1891                struct buddy *budlight = find_buddy(g, who);
1892                if (budlight)
1893                        if ((budlight->uc & 0xff80) >> 7)
1894                                if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
1895                                        aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
1896        } else
1897                aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
1898}
1899
1900static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1901{
1902
1903        if (!g_strcasecmp(state, _("Visible"))) {
1904                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1905                return;
1906        } else if (!g_strcasecmp(state, _("Invisible"))) {
1907                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1908                return;
1909        } /* else... */
1910
1911        if (od->rights.maxawaymsglen == 0)
1912                do_error_dialog(gc, "oscar_set_away_aim called before locate rights received", "Protocol Error");
1913
1914        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1915
1916        if (gc->away)
1917                g_free(gc->away);
1918        gc->away = NULL;
1919
1920        if (!message) {
1921                aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
1922                return;
1923        }
1924
1925        if (strlen(message) > od->rights.maxawaymsglen) {
1926                gchar *errstr;
1927
1928                errstr = g_strdup_printf("Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
1929
1930                do_error_dialog(gc, errstr, "Away Message Too Long");
1931
1932                g_free(errstr);
1933        }
1934
1935        gc->away = g_strndup(message, od->rights.maxawaymsglen);
1936        aim_bos_setprofile(od->sess, od->conn, NULL, gc->away, gaim_caps);
1937
1938        return;
1939}
1940
1941static void oscar_set_away_icq(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
1942{
1943    const char *msg = NULL;
1944        gboolean no_message = FALSE;
1945
1946        /* clean old states */
1947    if (gc->away) {
1948                g_free(gc->away);
1949                gc->away = NULL;
1950    }
1951        od->sess->aim_icq_state = 0;
1952
1953        /* if no message, then use an empty message */
1954    if (message) {
1955        msg = message;
1956    } else {
1957        msg = "";
1958                no_message = TRUE;
1959    }
1960
1961        if (!g_strcasecmp(state, "Online")) {
1962                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1963        } else if (!g_strcasecmp(state, "Away")) {
1964                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1965        gc->away = g_strdup(msg);
1966                od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1967        } else if (!g_strcasecmp(state, "Do Not Disturb")) {
1968                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
1969        gc->away = g_strdup(msg);
1970                od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
1971        } else if (!g_strcasecmp(state, "Not Available")) {
1972                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
1973        gc->away = g_strdup(msg);
1974                od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
1975        } else if (!g_strcasecmp(state, "Occupied")) {
1976                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
1977        gc->away = g_strdup(msg);
1978                od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
1979        } else if (!g_strcasecmp(state, "Free For Chat")) {
1980                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
1981        gc->away = g_strdup(msg);
1982                od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
1983        } else if (!g_strcasecmp(state, "Invisible")) {
1984                aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
1985        gc->away = g_strdup(msg);
1986        } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
1987                if (no_message) {
1988                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
1989                } else {
1990                        aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
1991            gc->away = g_strdup(msg);
1992                        od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
1993                }
1994        }
1995
1996        return;
1997}
1998
1999static void oscar_set_away(struct gaim_connection *gc, char *state, char *message)
2000{
2001        struct oscar_data *od = (struct oscar_data *)gc->proto_data;
2002
2003    oscar_set_away_aim(gc, od, state, message);
2004        if (od->icq)
2005                oscar_set_away_icq(gc, od, state, message);
2006
2007        return;
2008}
2009
2010static void oscar_add_buddy(struct gaim_connection *g, char *name) {
2011        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2012        aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
2013}
2014
2015static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) {
2016        struct oscar_data *odata = (struct oscar_data *)g->proto_data;
2017        struct aim_ssi_item *ssigroup;
2018        while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
2019}
2020
2021static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
2022        return 1;
2023}
2024
2025static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
2026        struct gaim_connection *gc = sess->aux_data;
2027        struct aim_ssi_item *curitem;
2028        int tmp;
2029
2030        /* Add from server list to local list */
2031        tmp = 0;
2032        for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
2033                switch (curitem->type) {
2034                        case 0x0000: /* Buddy */
2035                                if ((curitem->name) && (!find_buddy(gc, curitem->name))) {
2036                                        char *realname = NULL;
2037
2038                                        if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
2039                                                    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
2040                                               
2041                                        add_buddy(gc, NULL, curitem->name, realname);
2042                                       
2043                                        if (realname)
2044                                            g_free(realname);
2045                                }
2046                                break;
2047
2048                        case 0x0002: /* Permit buddy */
2049                                if (curitem->name) {
2050                                        GSList *list;
2051                                        for (list=gc->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2052                                        if (!list) {
2053                                                char *name;
2054                                                name = g_strdup(normalize(curitem->name));
2055                                                gc->permit = g_slist_append(gc->permit, name);
2056                                                build_allow_list();
2057                                                tmp++;
2058                                        }
2059                                }
2060                                break;
2061
2062                        case 0x0003: /* Deny buddy */
2063                                if (curitem->name) {
2064                                        GSList *list;
2065                                        for (list=gc->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
2066                                        if (!list) {
2067                                                char *name;
2068                                                name = g_strdup(normalize(curitem->name));
2069                                                gc->deny = g_slist_append(gc->deny, name);
2070                                                build_block_list();
2071                                                tmp++;
2072                                        }
2073                                }
2074                                break;
2075
2076                        case 0x0004: /* Permit/deny setting */
2077                                if (curitem->data) {
2078                                        guint8 permdeny;
2079                                        if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != gc->permdeny)) {
2080                                                gc->permdeny = permdeny;
2081                                                tmp++;
2082                                        }
2083                                }
2084                                break;
2085
2086                        case 0x0005: /* Presence setting */
2087                                /* We don't want to change Gaim's setting because it applies to all accounts */
2088                                break;
2089                } /* End of switch on curitem->type */
2090        } /* End of for loop */
2091
2092        if (tmp)
2093                do_export(gc);
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-1900;
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
2496static struct prpl *my_protocol = NULL;
2497
2498void oscar_init(struct prpl *ret) {
2499        ret->protocol = PROTO_OSCAR;
2500        ret->away_states = oscar_away_states;
2501        ret->login = oscar_login;
2502        ret->close = oscar_close;
2503        ret->send_im = oscar_send_im;
2504        ret->get_info = oscar_get_info;
2505        ret->set_away = oscar_set_away;
2506        ret->get_away = oscar_get_away;
2507        ret->add_buddy = oscar_add_buddy;
2508        ret->remove_buddy = oscar_remove_buddy;
2509        ret->add_permit = oscar_add_permit;
2510        ret->add_deny = oscar_add_deny;
2511        ret->rem_permit = oscar_rem_permit;
2512        ret->rem_deny = oscar_rem_deny;
2513        ret->set_permit_deny = oscar_set_permit_deny;
2514        ret->keepalive = oscar_keepalive;
2515        ret->cmp_buddynames = aim_sncmp;
2516        ret->get_status_string = oscar_get_status_string;
2517
2518        ret->send_typing = oscar_send_typing;
2519
2520        my_protocol = ret;
2521}
Note: See TracBrowser for help on using the repository browser.