source: protocols/oscar/oscar.c @ ac55e50

Last change on this file since ac55e50 was b20b32f, checked in by Jelmer Vernooij <jelmer@…>, at 2005-11-28T01:14:06Z

Merge from Wilmer

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