source: protocols/oscar/oscar.c @ 846cec61

Last change on this file since 846cec61 was d3a672c, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-08-04T13:56:53Z

Added a per-account web_aware setting for ICQ connections.

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