source: protocols/oscar/oscar.c @ e8f8b18

Last change on this file since e8f8b18 was e8f8b18, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-01-19T13:45:15Z

Fixed read() error handling in Jabber module.

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