comparison libpurple/protocols/oscar/oscar.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 21bc8d84974f
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
1 /*
2 * gaim
3 *
4 * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 * Some code copyright (C) 1999-2001, Eric Warmenhoven
6 * Some code copyright (C) 2001-2003, Sean Egan
7 * Some code copyright (C) 2001-2005, Mark Doliner <thekingant@users.sourceforge.net>
8 * Some code copyright (C) 2005, Jonathan Clark <ardentlygnarly@users.sourceforge.net>
9 *
10 * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx>
11 * Some libfaim code copyright (C) 2001-2004 Mark Doliner <thekingant@users.sourceforge.net>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 */
28
29 #include "internal.h"
30
31 #include "account.h"
32 #include "accountopt.h"
33 #include "buddyicon.h"
34 #include "cipher.h"
35 #include "conversation.h"
36 #include "core.h"
37 #include "debug.h"
38 #include "imgstore.h"
39 #include "network.h"
40 #include "notify.h"
41 #include "privacy.h"
42 #include "prpl.h"
43 #include "proxy.h"
44 #include "request.h"
45 #include "util.h"
46 #include "version.h"
47
48 #include "oscarcommon.h"
49 #include "oscar.h"
50 #include "peer.h"
51
52 #define OSCAR_STATUS_ID_INVISIBLE "invisible"
53 #define OSCAR_STATUS_ID_OFFLINE "offline"
54 #define OSCAR_STATUS_ID_AVAILABLE "available"
55 #define OSCAR_STATUS_ID_AWAY "away"
56 #define OSCAR_STATUS_ID_DND "dnd"
57 #define OSCAR_STATUS_ID_NA "na"
58 #define OSCAR_STATUS_ID_OCCUPIED "occupied"
59 #define OSCAR_STATUS_ID_FREE4CHAT "free4chat"
60 #define OSCAR_STATUS_ID_CUSTOM "custom"
61
62 #define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
63
64 #define OSCAR_CONNECT_STEPS 6
65
66 static OscarCapability gaim_caps = OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM | OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE | OSCAR_CAPABILITY_ICHAT;
67
68 static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
69 static guint8 features_icq[] = {0x01, 0x06};
70 static guint8 features_icq_offline[] = {0x01};
71 static guint8 ck[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
72
73 struct create_room {
74 char *name;
75 int exchange;
76 };
77
78 struct oscar_ask_directim_data
79 {
80 OscarData *od;
81 char *who;
82 };
83
84 /*
85 * Various PRPL-specific buddy info that we want to keep track of
86 * Some other info is maintained by locate.c, and I'd like to move
87 * the rest of this to libfaim, mostly im.c
88 *
89 * TODO: More of this should use the status API.
90 */
91 struct buddyinfo {
92 gboolean typingnot;
93 guint32 ipaddr;
94
95 unsigned long ico_me_len;
96 unsigned long ico_me_csum;
97 time_t ico_me_time;
98 gboolean ico_informed;
99
100 unsigned long ico_len;
101 unsigned long ico_csum;
102 time_t ico_time;
103 gboolean ico_need;
104 gboolean ico_sent;
105 };
106
107 struct name_data {
108 GaimConnection *gc;
109 gchar *name;
110 gchar *nick;
111 };
112
113 static char *msgerrreason[] = {
114 N_("Invalid error"),
115 N_("Invalid SNAC"),
116 N_("Rate to host"),
117 N_("Rate to client"),
118 N_("Not logged in"),
119 N_("Service unavailable"),
120 N_("Service not defined"),
121 N_("Obsolete SNAC"),
122 N_("Not supported by host"),
123 N_("Not supported by client"),
124 N_("Refused by client"),
125 N_("Reply too big"),
126 N_("Responses lost"),
127 N_("Request denied"),
128 N_("Busted SNAC payload"),
129 N_("Insufficient rights"),
130 N_("In local permit/deny"),
131 N_("Too evil (sender)"),
132 N_("Too evil (receiver)"),
133 N_("User temporarily unavailable"),
134 N_("No match"),
135 N_("List overflow"),
136 N_("Request ambiguous"),
137 N_("Queue full"),
138 N_("Not while on AOL")
139 };
140 static int msgerrreasonlen = 25;
141
142 /* All the libfaim->gaim callback functions */
143 static int gaim_parse_auth_resp (OscarData *, FlapConnection *, FlapFrame *, ...);
144 static int gaim_parse_login (OscarData *, FlapConnection *, FlapFrame *, ...);
145 static int gaim_parse_auth_securid_request(OscarData *, FlapConnection *, FlapFrame *, ...);
146 static int gaim_handle_redirect (OscarData *, FlapConnection *, FlapFrame *, ...);
147 static int gaim_info_change (OscarData *, FlapConnection *, FlapFrame *, ...);
148 static int gaim_account_confirm (OscarData *, FlapConnection *, FlapFrame *, ...);
149 static int gaim_parse_oncoming (OscarData *, FlapConnection *, FlapFrame *, ...);
150 static int gaim_parse_offgoing (OscarData *, FlapConnection *, FlapFrame *, ...);
151 static int gaim_parse_incoming_im(OscarData *, FlapConnection *, FlapFrame *, ...);
152 static int gaim_parse_misses (OscarData *, FlapConnection *, FlapFrame *, ...);
153 static int gaim_parse_clientauto (OscarData *, FlapConnection *, FlapFrame *, ...);
154 static int gaim_parse_userinfo (OscarData *, FlapConnection *, FlapFrame *, ...);
155 static int gaim_got_infoblock (OscarData *, FlapConnection *, FlapFrame *, ...);
156 static int gaim_parse_motd (OscarData *, FlapConnection *, FlapFrame *, ...);
157 static int gaim_chatnav_info (OscarData *, FlapConnection *, FlapFrame *, ...);
158 static int gaim_conv_chat_join (OscarData *, FlapConnection *, FlapFrame *, ...);
159 static int gaim_conv_chat_leave (OscarData *, FlapConnection *, FlapFrame *, ...);
160 static int gaim_conv_chat_info_update (OscarData *, FlapConnection *, FlapFrame *, ...);
161 static int gaim_conv_chat_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...);
162 static int gaim_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...);
163 static int gaim_icon_error (OscarData *, FlapConnection *, FlapFrame *, ...);
164 static int gaim_icon_parseicon (OscarData *, FlapConnection *, FlapFrame *, ...);
165 static int oscar_icon_req (OscarData *, FlapConnection *, FlapFrame *, ...);
166 static int gaim_parse_msgack (OscarData *, FlapConnection *, FlapFrame *, ...);
167 static int gaim_parse_ratechange (OscarData *, FlapConnection *, FlapFrame *, ...);
168 static int gaim_parse_evilnotify (OscarData *, FlapConnection *, FlapFrame *, ...);
169 static int gaim_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...);
170 static int gaim_parse_searchreply(OscarData *, FlapConnection *, FlapFrame *, ...);
171 static int gaim_bosrights (OscarData *, FlapConnection *, FlapFrame *, ...);
172 static int gaim_connerr (OscarData *, FlapConnection *, FlapFrame *, ...);
173 static int gaim_parse_msgerr (OscarData *, FlapConnection *, FlapFrame *, ...);
174 static int gaim_parse_mtn (OscarData *, FlapConnection *, FlapFrame *, ...);
175 static int gaim_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...);
176 static int gaim_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...);
177 static int gaim_parse_locerr (OscarData *, FlapConnection *, FlapFrame *, ...);
178 static int gaim_icbm_param_info (OscarData *, FlapConnection *, FlapFrame *, ...);
179 static int gaim_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...);
180 static int gaim_memrequest (OscarData *, FlapConnection *, FlapFrame *, ...);
181 static int gaim_selfinfo (OscarData *, FlapConnection *, FlapFrame *, ...);
182 static int gaim_offlinemsg (OscarData *, FlapConnection *, FlapFrame *, ...);
183 static int gaim_offlinemsgdone (OscarData *, FlapConnection *, FlapFrame *, ...);
184 static int gaim_icqalias (OscarData *, FlapConnection *, FlapFrame *, ...);
185 static int gaim_icqinfo (OscarData *, FlapConnection *, FlapFrame *, ...);
186 static int gaim_popup (OscarData *, FlapConnection *, FlapFrame *, ...);
187 static int gaim_ssi_parseerr (OscarData *, FlapConnection *, FlapFrame *, ...);
188 static int gaim_ssi_parserights (OscarData *, FlapConnection *, FlapFrame *, ...);
189 static int gaim_ssi_parselist (OscarData *, FlapConnection *, FlapFrame *, ...);
190 static int gaim_ssi_parseack (OscarData *, FlapConnection *, FlapFrame *, ...);
191 static int gaim_ssi_parseadd (OscarData *, FlapConnection *, FlapFrame *, ...);
192 static int gaim_ssi_authgiven (OscarData *, FlapConnection *, FlapFrame *, ...);
193 static int gaim_ssi_authrequest (OscarData *, FlapConnection *, FlapFrame *, ...);
194 static int gaim_ssi_authreply (OscarData *, FlapConnection *, FlapFrame *, ...);
195 static int gaim_ssi_gotadded (OscarData *, FlapConnection *, FlapFrame *, ...);
196
197 static gboolean gaim_icon_timerfunc(gpointer data);
198
199 static void recent_buddies_cb(const char *name, GaimPrefType type, gconstpointer value, gpointer data);
200 void oscar_set_info(GaimConnection *gc, const char *info);
201 static void oscar_set_info_and_status(GaimAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, GaimStatus *status);
202 static void oscar_set_extendedstatus(GaimConnection *gc);
203 static gboolean gaim_ssi_rerequestdata(gpointer data);
204
205 static void oscar_free_name_data(struct name_data *data) {
206 g_free(data->name);
207 g_free(data->nick);
208 g_free(data);
209 }
210
211 #ifdef _WIN32
212 const char *oscar_get_locale_charset(void) {
213 static const char *charset = NULL;
214 if (charset == NULL)
215 g_get_charset(&charset);
216 return charset;
217 }
218 #endif
219
220 /**
221 * Determine how we can send this message. Per the warnings elsewhere
222 * in this file, these little checks determine the simplest encoding
223 * we can use for a given message send using it.
224 */
225 static guint32
226 oscar_charset_check(const char *utf8)
227 {
228 int i = 0;
229 int charset = AIM_CHARSET_ASCII;
230
231 /*
232 * Can we get away with using our custom encoding?
233 */
234 while (utf8[i])
235 {
236 if ((unsigned char)utf8[i] > 0x7f) {
237 /* not ASCII! */
238 charset = AIM_CHARSET_CUSTOM;
239 break;
240 }
241 i++;
242 }
243
244 /*
245 * Must we send this message as UNICODE (in the UCS-2BE encoding)?
246 */
247 while (utf8[i])
248 {
249 /* ISO-8859-1 is 0x00-0xbf in the first byte
250 * followed by 0xc0-0xc3 in the second */
251 if ((unsigned char)utf8[i] < 0x80) {
252 i++;
253 continue;
254 } else if (((unsigned char)utf8[i] & 0xfc) == 0xc0 &&
255 ((unsigned char)utf8[i + 1] & 0xc0) == 0x80) {
256 i += 2;
257 continue;
258 }
259 charset = AIM_CHARSET_UNICODE;
260 break;
261 }
262
263 return charset;
264 }
265
266 /**
267 * Take a string of the form charset="bleh" where bleh is
268 * one of us-ascii, utf-8, iso-8859-1, or unicode-2-0, and
269 * return a newly allocated string containing bleh.
270 */
271 gchar *
272 oscar_encoding_extract(const char *encoding)
273 {
274 gchar *ret = NULL;
275 char *begin, *end;
276
277 g_return_val_if_fail(encoding != NULL, NULL);
278
279 /* Make sure encoding begins with charset= */
280 if (strncmp(encoding, "text/aolrtf; charset=", 21) &&
281 strncmp(encoding, "text/x-aolrtf; charset=", 23))
282 {
283 return NULL;
284 }
285
286 begin = strchr(encoding, '"');
287 end = strrchr(encoding, '"');
288
289 if ((begin == NULL) || (end == NULL) || (begin >= end))
290 return NULL;
291
292 ret = g_strndup(begin+1, (end-1) - begin);
293
294 return ret;
295 }
296
297 gchar *
298 oscar_encoding_to_utf8(const char *encoding, const char *text, int textlen)
299 {
300 gchar *utf8 = NULL;
301
302 if ((encoding == NULL) || encoding[0] == '\0') {
303 gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");
304 } else if (!strcasecmp(encoding, "iso-8859-1")) {
305 utf8 = g_convert(text, textlen, "UTF-8", "iso-8859-1", NULL, NULL, NULL);
306 } else if (!strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1") ||
307 !strcasecmp(encoding, "us-ascii"))
308 {
309 utf8 = g_convert(text, textlen, "UTF-8", "Windows-1252", NULL, NULL, NULL);
310 } else if (!strcasecmp(encoding, "unicode-2-0")) {
311 utf8 = g_convert(text, textlen, "UTF-8", "UCS-2BE", NULL, NULL, NULL);
312 } else if (strcasecmp(encoding, "utf-8")) {
313 gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
314 "attempting to convert to UTF-8 anyway\n", encoding);
315 utf8 = g_convert(text, textlen, "UTF-8", encoding, NULL, NULL, NULL);
316 }
317
318 /*
319 * If utf8 is still NULL then either the encoding is utf-8 or
320 * we have been unable to convert the text to utf-8 from the encoding
321 * that was specified. So we check if the text is valid utf-8 then
322 * just copy it.
323 */
324 if (utf8 == NULL) {
325 if (textlen != 0 && *text != '\0'
326 && !g_utf8_validate(text, textlen, NULL))
327 utf8 = g_strdup(_("(There was an error receiving this message. The buddy you are speaking with is probably using a different encoding than expected. If you know what encoding he is using, you can specify it in the advanced account options for your AIM/ICQ account.)"));
328 else
329 utf8 = g_strndup(text, textlen);
330 }
331
332 return utf8;
333 }
334
335 static gchar *
336 oscar_utf8_try_convert(GaimAccount *account, const gchar *msg)
337 {
338 const char *charset = NULL;
339 char *ret = NULL;
340
341 if(aim_sn_is_icq(gaim_account_get_username(account)))
342 charset = gaim_account_get_string(account, "encoding", NULL);
343
344 if(charset && *charset)
345 ret = g_convert(msg, -1, "UTF-8", charset, NULL, NULL, NULL);
346
347 if(!ret)
348 ret = gaim_utf8_try_convert(msg);
349
350 return ret;
351 }
352
353 static gchar *
354 gaim_plugin_oscar_convert_to_utf8(const gchar *data, gsize datalen, const char *charsetstr, gboolean fallback)
355 {
356 gchar *ret = NULL;
357 GError *err = NULL;
358
359 if ((charsetstr == NULL) || (*charsetstr == '\0'))
360 return NULL;
361
362 if (strcasecmp("UTF-8", charsetstr)) {
363 if (fallback)
364 ret = g_convert_with_fallback(data, datalen, "UTF-8", charsetstr, "?", NULL, NULL, &err);
365 else
366 ret = g_convert(data, datalen, "UTF-8", charsetstr, NULL, NULL, &err);
367 if (err != NULL) {
368 gaim_debug_warning("oscar", "Conversion from %s failed: %s.\n",
369 charsetstr, err->message);
370 g_error_free(err);
371 }
372 } else {
373 if (g_utf8_validate(data, datalen, NULL))
374 ret = g_strndup(data, datalen);
375 else
376 gaim_debug_warning("oscar", "String is not valid UTF-8.\n");
377 }
378
379 return ret;
380 }
381
382 /**
383 * This attemps to decode an incoming IM into a UTF8 string.
384 *
385 * We try decoding using two different character sets. The charset
386 * specified in the IM determines the order in which we attempt to
387 * decode. We do this because there are lots of broken ICQ clients
388 * that don't correctly send non-ASCII messages. And if Gaim isn't
389 * able to deal with that crap, then people complain like banshees.
390 * charsetstr1 is always set to what the correct encoding should be.
391 */
392 gchar *
393 gaim_plugin_oscar_decode_im_part(GaimAccount *account, const char *sourcesn, guint16 charset, guint16 charsubset, const gchar *data, gsize datalen)
394 {
395 gchar *ret = NULL;
396 const gchar *charsetstr1, *charsetstr2;
397
398 gaim_debug_info("oscar", "Parsing IM part, charset=0x%04hx, charsubset=0x%04hx, datalen=%hd\n", charset, charsubset, datalen);
399
400 if ((datalen == 0) || (data == NULL))
401 return NULL;
402
403 if (charset == AIM_CHARSET_UNICODE) {
404 charsetstr1 = "UCS-2BE";
405 charsetstr2 = "UTF-8";
406 } else if (charset == AIM_CHARSET_CUSTOM) {
407 if ((sourcesn != NULL) && isdigit(sourcesn[0]))
408 charsetstr1 = gaim_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
409 else
410 charsetstr1 = "ISO-8859-1";
411 charsetstr2 = "UTF-8";
412 } else if (charset == AIM_CHARSET_ASCII) {
413 /* Should just be "ASCII" */
414 charsetstr1 = "ASCII";
415 charsetstr2 = gaim_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
416 } else if (charset == 0x000d) {
417 /* Mobile AIM client on a Nokia 3100 and an LG VX6000 */
418 charsetstr1 = "ISO-8859-1";
419 charsetstr2 = gaim_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
420 } else {
421 /* Unknown, hope for valid UTF-8... */
422 charsetstr1 = "UTF-8";
423 charsetstr2 = gaim_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
424 }
425
426 ret = gaim_plugin_oscar_convert_to_utf8(data, datalen, charsetstr1, FALSE);
427 if (ret == NULL)
428 ret = gaim_plugin_oscar_convert_to_utf8(data, datalen, charsetstr2, TRUE);
429 if (ret == NULL) {
430 char *str, *salvage, *tmp;
431
432 str = g_malloc(datalen + 1);
433 strncpy(str, data, datalen);
434 str[datalen] = '\0';
435 salvage = gaim_utf8_salvage(str);
436 tmp = g_strdup_printf(_("(There was an error receiving this message. Either you and %s have different encodings selected, or %s has a buggy client.)"),
437 sourcesn, sourcesn);
438 ret = g_strdup_printf("%s %s", salvage, tmp);
439 g_free(tmp);
440 g_free(str);
441 g_free(salvage);
442 }
443
444 return ret;
445 }
446
447 /**
448 * Figure out what encoding to use when sending a given outgoing message.
449 */
450 static void
451 gaim_plugin_oscar_convert_to_best_encoding(GaimConnection *gc,
452 const char *destsn, const gchar *from,
453 gchar **msg, int *msglen_int,
454 guint16 *charset, guint16 *charsubset)
455 {
456 OscarData *od = gc->proto_data;
457 GaimAccount *account = gaim_connection_get_account(gc);
458 GError *err = NULL;
459 aim_userinfo_t *userinfo = NULL;
460 const gchar *charsetstr;
461 gsize msglen;
462
463 /* Attempt to send as ASCII */
464 if (oscar_charset_check(from) == AIM_CHARSET_ASCII) {
465 *msg = g_convert(from, strlen(from), "ASCII", "UTF-8", NULL, &msglen, NULL);
466 *charset = AIM_CHARSET_ASCII;
467 *charsubset = 0x0000;
468 *msglen_int = msglen;
469 return;
470 }
471
472 /*
473 * If we're sending to an ICQ user, and they are in our
474 * buddy list, and they are advertising the Unicode
475 * capability, and they are online, then attempt to send
476 * as UCS-2BE.
477 */
478 if ((destsn != NULL) && aim_sn_is_icq(destsn))
479 userinfo = aim_locate_finduserinfo(od, destsn);
480
481 if ((userinfo != NULL) && (userinfo->capabilities & OSCAR_CAPABILITY_UNICODE))
482 {
483 GaimBuddy *b;
484 b = gaim_find_buddy(account, destsn);
485 if ((b != NULL) && (GAIM_BUDDY_IS_ONLINE(b)))
486 {
487 *msg = g_convert(from, strlen(from), "UCS-2BE", "UTF-8", NULL, &msglen, NULL);
488 if (*msg != NULL)
489 {
490 *charset = AIM_CHARSET_UNICODE;
491 *charsubset = 0x0000;
492 *msglen_int = msglen;
493 return;
494 }
495 }
496 }
497
498 /*
499 * If this is AIM then attempt to send as ISO-8859-1. If this is
500 * ICQ then attempt to send as the user specified character encoding.
501 */
502 charsetstr = "ISO-8859-1";
503 if ((destsn != NULL) && aim_sn_is_icq(destsn))
504 charsetstr = gaim_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
505
506 /*
507 * XXX - We need a way to only attempt to convert if we KNOW "from"
508 * can be converted to "charsetstr"
509 */
510 *msg = g_convert(from, strlen(from), charsetstr, "UTF-8", NULL, &msglen, NULL);
511 if (*msg != NULL) {
512 *charset = AIM_CHARSET_CUSTOM;
513 *charsubset = 0x0000;
514 *msglen_int = msglen;
515 return;
516 }
517
518 /*
519 * Nothing else worked, so send as UCS-2BE.
520 */
521 *msg = g_convert(from, strlen(from), "UCS-2BE", "UTF-8", NULL, &msglen, &err);
522 if (*msg != NULL) {
523 *charset = AIM_CHARSET_UNICODE;
524 *charsubset = 0x0000;
525 *msglen_int = msglen;
526 return;
527 }
528
529 gaim_debug_error("oscar", "Error converting a Unicode message: %s\n", err->message);
530 g_error_free(err);
531
532 gaim_debug_error("oscar", "This should NEVER happen! Sending UTF-8 text flagged as ASCII.\n");
533 *msg = g_strdup(from);
534 *msglen_int = strlen(*msg);
535 *charset = AIM_CHARSET_ASCII;
536 *charsubset = 0x0000;
537 return;
538 }
539
540 /**
541 * Looks for %n, %d, or %t in a string, and replaces them with the
542 * specified name, date, and time, respectively.
543 *
544 * @param str The string that may contain the special variables.
545 * @param name The sender name.
546 *
547 * @return A newly allocated string where the special variables are
548 * expanded. This should be g_free'd by the caller.
549 */
550 static gchar *
551 gaim_str_sub_away_formatters(const char *str, const char *name)
552 {
553 char *c;
554 GString *cpy;
555 time_t t;
556 struct tm *tme;
557
558 g_return_val_if_fail(str != NULL, NULL);
559 g_return_val_if_fail(name != NULL, NULL);
560
561 /* Create an empty GString that is hopefully big enough for most messages */
562 cpy = g_string_sized_new(1024);
563
564 t = time(NULL);
565 tme = localtime(&t);
566
567 c = (char *)str;
568 while (*c) {
569 switch (*c) {
570 case '%':
571 if (*(c + 1)) {
572 switch (*(c + 1)) {
573 case 'n':
574 /* append name */
575 g_string_append(cpy, name);
576 c++;
577 break;
578 case 'd':
579 /* append date */
580 g_string_append(cpy, gaim_date_format_short(tme));
581 c++;
582 break;
583 case 't':
584 /* append time */
585 g_string_append(cpy, gaim_time_format(tme));
586 c++;
587 break;
588 default:
589 g_string_append_c(cpy, *c);
590 }
591 } else {
592 g_string_append_c(cpy, *c);
593 }
594 break;
595 default:
596 g_string_append_c(cpy, *c);
597 }
598 c++;
599 }
600
601 return g_string_free(cpy, FALSE);
602 }
603
604 static gchar *oscar_caps_to_string(OscarCapability caps)
605 {
606 GString *str;
607 const gchar *tmp;
608 guint bit = 1;
609
610 str = g_string_new("");
611
612 if (!caps) {
613 return NULL;
614 } else while (bit <= OSCAR_CAPABILITY_LAST) {
615 if (bit & caps) {
616 switch (bit) {
617 case OSCAR_CAPABILITY_BUDDYICON:
618 tmp = _("Buddy Icon");
619 break;
620 case OSCAR_CAPABILITY_TALK:
621 tmp = _("Voice");
622 break;
623 case OSCAR_CAPABILITY_DIRECTIM:
624 tmp = _("AIM Direct IM");
625 break;
626 case OSCAR_CAPABILITY_CHAT:
627 tmp = _("Chat");
628 break;
629 case OSCAR_CAPABILITY_GETFILE:
630 tmp = _("Get File");
631 break;
632 case OSCAR_CAPABILITY_SENDFILE:
633 tmp = _("Send File");
634 break;
635 case OSCAR_CAPABILITY_GAMES:
636 case OSCAR_CAPABILITY_GAMES2:
637 tmp = _("Games");
638 break;
639 case OSCAR_CAPABILITY_ADDINS:
640 tmp = _("Add-Ins");
641 break;
642 case OSCAR_CAPABILITY_SENDBUDDYLIST:
643 tmp = _("Send Buddy List");
644 break;
645 case OSCAR_CAPABILITY_ICQ_DIRECT:
646 tmp = _("ICQ Direct Connect");
647 break;
648 case OSCAR_CAPABILITY_APINFO:
649 tmp = _("AP User");
650 break;
651 case OSCAR_CAPABILITY_ICQRTF:
652 tmp = _("ICQ RTF");
653 break;
654 case OSCAR_CAPABILITY_EMPTY:
655 tmp = _("Nihilist");
656 break;
657 case OSCAR_CAPABILITY_ICQSERVERRELAY:
658 tmp = _("ICQ Server Relay");
659 break;
660 case OSCAR_CAPABILITY_UNICODEOLD:
661 tmp = _("Old ICQ UTF8");
662 break;
663 case OSCAR_CAPABILITY_TRILLIANCRYPT:
664 tmp = _("Trillian Encryption");
665 break;
666 case OSCAR_CAPABILITY_UNICODE:
667 tmp = _("ICQ UTF8");
668 break;
669 case OSCAR_CAPABILITY_HIPTOP:
670 tmp = _("Hiptop");
671 break;
672 case OSCAR_CAPABILITY_SECUREIM:
673 tmp = _("Security Enabled");
674 break;
675 case OSCAR_CAPABILITY_VIDEO:
676 tmp = _("Video Chat");
677 break;
678 /* Not actually sure about this one... WinAIM doesn't show anything */
679 case OSCAR_CAPABILITY_ICHATAV:
680 tmp = _("iChat AV");
681 break;
682 case OSCAR_CAPABILITY_LIVEVIDEO:
683 tmp = _("Live Video");
684 break;
685 case OSCAR_CAPABILITY_CAMERA:
686 tmp = _("Camera");
687 break;
688 default:
689 tmp = NULL;
690 break;
691 }
692 if (tmp)
693 g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
694 }
695 bit <<= 1;
696 }
697
698 return g_string_free(str, FALSE);
699 }
700
701 static char *oscar_icqstatus(int state) {
702 /* Make a cute little string that shows the status of the dude or dudet */
703 if (state & AIM_ICQ_STATE_CHAT)
704 return g_strdup_printf(_("Free For Chat"));
705 else if (state & AIM_ICQ_STATE_DND)
706 return g_strdup_printf(_("Do Not Disturb"));
707 else if (state & AIM_ICQ_STATE_OUT)
708 return g_strdup_printf(_("Not Available"));
709 else if (state & AIM_ICQ_STATE_BUSY)
710 return g_strdup_printf(_("Occupied"));
711 else if (state & AIM_ICQ_STATE_AWAY)
712 return g_strdup_printf(_("Away"));
713 else if (state & AIM_ICQ_STATE_WEBAWARE)
714 return g_strdup_printf(_("Web Aware"));
715 else if (state & AIM_ICQ_STATE_INVISIBLE)
716 return g_strdup_printf(_("Invisible"));
717 else
718 return g_strdup_printf(_("Online"));
719 }
720
721 static void
722 oscar_user_info_add_pair(GaimNotifyUserInfo *user_info, const char *name, const char *value)
723 {
724 if (value && value[0]) {
725 gaim_notify_user_info_add_pair(user_info, name, value);
726 }
727 }
728
729 static void
730 oscar_user_info_convert_and_add_pair(GaimAccount *account, GaimNotifyUserInfo *user_info,
731 const char *name, const char *value)
732 {
733 gchar *utf8;
734
735 if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
736 gaim_notify_user_info_add_pair(user_info, name, utf8);
737 g_free(utf8);
738 }
739 }
740
741 static void
742 oscar_string_convert_and_append(GaimAccount *account, GString *str, const char *newline,
743 const char *name, const char *value)
744 {
745 gchar *utf8;
746
747 if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
748 g_string_append_printf(str, "%s<b>%s:</b> %s", newline, name, utf8);
749 g_free(utf8);
750 }
751 }
752
753 static void
754 oscar_user_info_convert_and_add(GaimAccount *account, GaimNotifyUserInfo *user_info,
755 const char *name, const char *value)
756 {
757 gchar *utf8;
758
759 if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
760 gaim_notify_user_info_add_pair(user_info, name, value);
761 g_free(utf8);
762 }
763 }
764
765 static void oscar_string_append_info(GaimConnection *gc, GaimNotifyUserInfo *user_info, GaimBuddy *b, aim_userinfo_t *userinfo)
766 {
767 OscarData *od;
768 GaimAccount *account;
769 GaimPresence *presence = NULL;
770 GaimStatus *status = NULL;
771 GaimGroup *g = NULL;
772 struct buddyinfo *bi = NULL;
773 char *tmp;
774
775 od = gc->proto_data;
776 account = gaim_connection_get_account(gc);
777
778 if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
779 return;
780
781 if (userinfo == NULL)
782 userinfo = aim_locate_finduserinfo(od, b->name);
783
784 if (b == NULL)
785 b = gaim_find_buddy(account, userinfo->sn);
786
787 if (b != NULL) {
788 g = gaim_buddy_get_group(b);
789 presence = gaim_buddy_get_presence(b);
790 status = gaim_presence_get_active_status(presence);
791 }
792
793 if (userinfo != NULL)
794 bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(account, userinfo->sn));
795
796 if (b != NULL) {
797 if (gaim_presence_is_online(presence)) {
798 if (aim_sn_is_icq(b->name)) {
799 GaimStatus *status = gaim_presence_get_active_status(presence);
800 oscar_user_info_add_pair(user_info, _("Status"), gaim_status_get_name(status));
801 }
802 } else {
803 tmp = aim_ssi_itemlist_findparentname(od->ssi.local, b->name);
804 if (aim_ssi_waitingforauth(od->ssi.local, tmp, b->name))
805 oscar_user_info_add_pair(user_info, _("Status"), _("Not Authorized"));
806 else
807 oscar_user_info_add_pair(user_info, _("Status"), _("Offline"));
808 }
809 }
810
811 if ((bi != NULL) && (bi->ipaddr != 0)) {
812 tmp = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
813 (bi->ipaddr & 0xff000000) >> 24,
814 (bi->ipaddr & 0x00ff0000) >> 16,
815 (bi->ipaddr & 0x0000ff00) >> 8,
816 (bi->ipaddr & 0x000000ff));
817 oscar_user_info_add_pair(user_info, _("IP Address"), tmp);
818 g_free(tmp);
819 }
820
821
822 if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
823 tmp = g_strdup_printf("%d", (int)(userinfo->warnlevel/10.0 + .5));
824 oscar_user_info_add_pair(user_info, _("Warning Level"), tmp);
825 g_free(tmp);
826 }
827
828 if ((b != NULL) && (b->name != NULL) && (g != NULL) && (g->name != NULL)) {
829 tmp = aim_ssi_getcomment(od->ssi.local, g->name, b->name);
830 if (tmp != NULL) {
831 char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
832 g_free(tmp);
833
834 oscar_user_info_convert_and_add_pair(account, user_info, _("Buddy Comment"), tmp2);
835 g_free(tmp2);
836 }
837 }
838 }
839
840 static char *extract_name(const char *name) {
841 char *tmp, *x;
842 int i, j;
843
844 if (!name)
845 return NULL;
846
847 x = strchr(name, '-');
848 if (!x)
849 return NULL;
850
851 x = strchr(x + 1, '-');
852 if (!x)
853 return NULL;
854
855 tmp = g_strdup(++x);
856
857 for (i = 0, j = 0; x[i]; i++) {
858 char hex[3];
859 if (x[i] != '%') {
860 tmp[j++] = x[i];
861 continue;
862 }
863 strncpy(hex, x + ++i, 2);
864 hex[2] = 0;
865 i++;
866 tmp[j++] = strtol(hex, NULL, 16);
867 }
868
869 tmp[j] = 0;
870 return tmp;
871 }
872
873 static struct chat_connection *
874 find_oscar_chat(GaimConnection *gc, int id)
875 {
876 OscarData *od = (OscarData *)gc->proto_data;
877 GSList *cur;
878 struct chat_connection *cc;
879
880 for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
881 {
882 cc = (struct chat_connection *)cur->data;
883 if (cc->id == id)
884 return cc;
885 }
886
887 return NULL;
888 }
889
890 static struct chat_connection *
891 find_oscar_chat_by_conn(GaimConnection *gc, FlapConnection *conn)
892 {
893 OscarData *od = (OscarData *)gc->proto_data;
894 GSList *cur;
895 struct chat_connection *cc;
896
897 for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
898 {
899 cc = (struct chat_connection *)cur->data;
900 if (cc->conn == conn)
901 return cc;
902 }
903
904 return NULL;
905 }
906
907 static struct chat_connection *
908 find_oscar_chat_by_conv(GaimConnection *gc, GaimConversation *conv)
909 {
910 OscarData *od = (OscarData *)gc->proto_data;
911 GSList *cur;
912 struct chat_connection *cc;
913
914 for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
915 {
916 cc = (struct chat_connection *)cur->data;
917 if (cc->conv == conv)
918 return cc;
919 }
920
921 return NULL;
922 }
923
924 void
925 oscar_chat_destroy(struct chat_connection *cc)
926 {
927 g_free(cc->name);
928 g_free(cc->show);
929 g_free(cc);
930 }
931
932 static void
933 oscar_chat_kill(GaimConnection *gc, struct chat_connection *cc)
934 {
935 OscarData *od = (OscarData *)gc->proto_data;
936
937 /* Notify the conversation window that we've left the chat */
938 serv_got_chat_left(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(cc->conv)));
939
940 /* Destroy the chat_connection */
941 od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
942 flap_connection_schedule_destroy(cc->conn, OSCAR_DISCONNECT_DONE, NULL);
943 oscar_chat_destroy(cc);
944 }
945
946 /**
947 * This is the callback function anytime gaim_proxy_connect()
948 * establishes a new TCP connection with an oscar host. Depending
949 * on the type of host, we do a few different things here.
950 */
951 static void
952 connection_established_cb(gpointer data, gint source, const gchar *error_message)
953 {
954 GaimConnection *gc;
955 OscarData *od;
956 GaimAccount *account;
957 FlapConnection *conn;
958
959 conn = data;
960 od = conn->od;
961 gc = od->gc;
962 account = gaim_connection_get_account(gc);
963
964 conn->connect_data = NULL;
965 conn->fd = source;
966
967 if (source < 0)
968 {
969 gaim_debug_error("oscar", "unable to connect FLAP server "
970 "of type 0x%04hx\n", conn->type);
971 if (conn->type == SNAC_FAMILY_AUTH)
972 {
973 gchar *msg;
974 msg = g_strdup_printf(_("Could not connect to authentication server:\n%s"),
975 error_message);
976 gaim_connection_error(gc, msg);
977 g_free(msg);
978 }
979 else if (conn->type == SNAC_FAMILY_LOCATE)
980 {
981 gchar *msg;
982 msg = g_strdup_printf(_("Could not connect to BOS server:\n%s"),
983 error_message);
984 gaim_connection_error(gc, msg);
985 g_free(msg);
986 }
987 else
988 {
989 /* Maybe we should call this for BOS connections, too? */
990 flap_connection_schedule_destroy(conn,
991 OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message);
992 }
993 return;
994 }
995
996 gaim_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n",
997 conn->type);
998 conn->watcher_incoming = gaim_input_add(conn->fd,
999 GAIM_INPUT_READ, flap_connection_recv_cb, conn);
1000 if (conn->cookie == NULL)
1001 {
1002 if (!aim_sn_is_icq(gaim_account_get_username(account)))
1003 /*
1004 * We don't send this when authenticating an ICQ account
1005 * because for some reason ICQ is still using the
1006 * assy/insecure authentication procedure.
1007 */
1008 flap_connection_send_version(od, conn);
1009 }
1010 else
1011 {
1012 flap_connection_send_version_with_cookie(od, conn,
1013 conn->cookielen, conn->cookie);
1014 g_free(conn->cookie);
1015 conn->cookie = NULL;
1016 }
1017
1018 if (conn->type == SNAC_FAMILY_AUTH)
1019 {
1020 aim_request_login(od, conn, gaim_account_get_username(account));
1021 gaim_debug_info("oscar", "Screen name sent, waiting for response\n");
1022 gaim_connection_update_progress(gc, _("Screen name sent"), 1, OSCAR_CONNECT_STEPS);
1023 ck[1] = 0x65;
1024 }
1025 else if (conn->type == SNAC_FAMILY_LOCATE)
1026 {
1027 gaim_connection_update_progress(gc, _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS);
1028 ck[4] = 0x61;
1029 }
1030 else if (conn->type == SNAC_FAMILY_CHAT)
1031 {
1032 od->oscar_chats = g_slist_prepend(od->oscar_chats, conn->new_conn_data);
1033 conn->new_conn_data = NULL;
1034 }
1035 }
1036
1037 static void
1038 flap_connection_established_bos(OscarData *od, FlapConnection *conn)
1039 {
1040 GaimConnection *gc = od->gc;
1041
1042 aim_srv_reqpersonalinfo(od, conn);
1043
1044 gaim_debug_info("oscar", "ssi: requesting rights and list\n");
1045 aim_ssi_reqrights(od);
1046 aim_ssi_reqdata(od);
1047 if (od->getblisttimer > 0)
1048 gaim_timeout_remove(od->getblisttimer);
1049 od->getblisttimer = gaim_timeout_add(30000, gaim_ssi_rerequestdata, od);
1050
1051 aim_locate_reqrights(od);
1052 aim_buddylist_reqrights(od, conn);
1053 aim_im_reqparams(od);
1054 aim_bos_reqrights(od, conn); /* TODO: Don't call this with ssi */
1055
1056 gaim_connection_update_progress(gc, _("Finalizing connection"), 5, OSCAR_CONNECT_STEPS);
1057 }
1058
1059 static void
1060 flap_connection_established_admin(OscarData *od, FlapConnection *conn)
1061 {
1062 aim_clientready(od, conn);
1063 gaim_debug_info("oscar", "connected to admin\n");
1064
1065 if (od->chpass) {
1066 gaim_debug_info("oscar", "changing password\n");
1067 aim_admin_changepasswd(od, conn, od->newp, od->oldp);
1068 g_free(od->oldp);
1069 od->oldp = NULL;
1070 g_free(od->newp);
1071 od->newp = NULL;
1072 od->chpass = FALSE;
1073 }
1074 if (od->setnick) {
1075 gaim_debug_info("oscar", "formatting screen name\n");
1076 aim_admin_setnick(od, conn, od->newsn);
1077 g_free(od->newsn);
1078 od->newsn = NULL;
1079 od->setnick = FALSE;
1080 }
1081 if (od->conf) {
1082 gaim_debug_info("oscar", "confirming account\n");
1083 aim_admin_reqconfirm(od, conn);
1084 od->conf = FALSE;
1085 }
1086 if (od->reqemail) {
1087 gaim_debug_info("oscar", "requesting e-mail address\n");
1088 aim_admin_getinfo(od, conn, 0x0011);
1089 od->reqemail = FALSE;
1090 }
1091 if (od->setemail) {
1092 gaim_debug_info("oscar", "setting e-mail address\n");
1093 aim_admin_setemail(od, conn, od->email);
1094 g_free(od->email);
1095 od->email = NULL;
1096 od->setemail = FALSE;
1097 }
1098 }
1099
1100 static void
1101 flap_connection_established_chat(OscarData *od, FlapConnection *conn)
1102 {
1103 GaimConnection *gc = od->gc;
1104 struct chat_connection *chatcon;
1105 static int id = 1;
1106
1107 aim_clientready(od, conn);
1108
1109 chatcon = find_oscar_chat_by_conn(gc, conn);
1110 chatcon->id = id;
1111 chatcon->conv = serv_got_joined_chat(gc, id++, chatcon->show);
1112 }
1113
1114 static void
1115 flap_connection_established_chatnav(OscarData *od, FlapConnection *conn)
1116 {
1117 aim_clientready(od, conn);
1118 aim_chatnav_reqrights(od, conn);
1119 }
1120
1121 static void
1122 flap_connection_established_alert(OscarData *od, FlapConnection *conn)
1123 {
1124 aim_email_sendcookies(od);
1125 aim_email_activate(od);
1126 aim_clientready(od, conn);
1127 }
1128
1129 static void
1130 flap_connection_established_bart(OscarData *od, FlapConnection *conn)
1131 {
1132 GaimConnection *gc = od->gc;
1133
1134 aim_clientready(od, conn);
1135
1136 od->iconconnecting = FALSE;
1137
1138 if (od->icontimer == 0)
1139 od->icontimer = gaim_timeout_add(100, gaim_icon_timerfunc, gc);
1140 }
1141
1142 static int
1143 flap_connection_established(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
1144 {
1145 gaim_debug_info("oscar", "FLAP connection of type 0x%04hx is "
1146 "now fully connected\n", conn->type);
1147 if (conn->type == SNAC_FAMILY_LOCATE)
1148 flap_connection_established_bos(od, conn);
1149 else if (conn->type == SNAC_FAMILY_ADMIN)
1150 flap_connection_established_admin(od, conn);
1151 else if (conn->type == SNAC_FAMILY_CHAT)
1152 flap_connection_established_chat(od, conn);
1153 else if (conn->type == SNAC_FAMILY_CHATNAV)
1154 flap_connection_established_chatnav(od, conn);
1155 else if (conn->type == SNAC_FAMILY_ALERT)
1156 flap_connection_established_alert(od, conn);
1157 else if (conn->type == SNAC_FAMILY_BART)
1158 flap_connection_established_bart(od, conn);
1159
1160 return 1;
1161 }
1162
1163 void
1164 oscar_login(GaimAccount *account)
1165 {
1166 GaimConnection *gc;
1167 OscarData *od;
1168 FlapConnection *newconn;
1169
1170 gc = gaim_account_get_connection(account);
1171 od = gc->proto_data = oscar_data_new();
1172 od->gc = gc;
1173
1174 oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_connerr, 0);
1175 oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, flap_connection_established, 0);
1176
1177 oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0003, gaim_info_change, 0);
1178 oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0005, gaim_info_change, 0);
1179 oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0007, gaim_account_confirm, 0);
1180 oscar_data_addhandler(od, SNAC_FAMILY_ALERT, 0x0001, gaim_parse_genericerr, 0);
1181 oscar_data_addhandler(od, SNAC_FAMILY_ALERT, SNAC_SUBTYPE_ALERT_MAILSTATUS, gaim_email_parseupdate, 0);
1182 oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0003, gaim_parse_auth_resp, 0);
1183 oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0007, gaim_parse_login, 0);
1184 oscar_data_addhandler(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_REQUEST, gaim_parse_auth_securid_request, 0);
1185 oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_ERROR, gaim_icon_error, 0);
1186 oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_RESPONSE, gaim_icon_parseicon, 0);
1187 oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0001, gaim_parse_genericerr, 0);
1188 oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0003, gaim_bosrights, 0);
1189 oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, 0x0001, gaim_parse_genericerr, 0);
1190 oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_RIGHTSINFO, gaim_parse_buddyrights, 0);
1191 oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_ONCOMING, gaim_parse_oncoming, 0);
1192 oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_OFFGOING, gaim_parse_offgoing, 0);
1193 oscar_data_addhandler(od, SNAC_FAMILY_CHAT, 0x0001, gaim_parse_genericerr, 0);
1194 oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERJOIN, gaim_conv_chat_join, 0);
1195 oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERLEAVE, gaim_conv_chat_leave, 0);
1196 oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE, gaim_conv_chat_info_update, 0);
1197 oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_INCOMINGMSG, gaim_conv_chat_incoming_msg, 0);
1198 oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, 0x0001, gaim_parse_genericerr, 0);
1199 oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, SNAC_SUBTYPE_CHATNAV_INFO, gaim_chatnav_info, 0);
1200 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ERROR, gaim_ssi_parseerr, 0);
1201 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO, gaim_ssi_parserights, 0);
1202 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_LIST, gaim_ssi_parselist, 0);
1203 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SRVACK, gaim_ssi_parseack, 0);
1204 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADD, gaim_ssi_parseadd, 0);
1205 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTH, gaim_ssi_authgiven, 0);
1206 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, gaim_ssi_authrequest, 0);
1207 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, gaim_ssi_authreply, 0);
1208 oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADDED, gaim_ssi_gotadded, 0);
1209 oscar_data_addhandler(od, SNAC_FAMILY_ICBM, 0x0005, gaim_icbm_param_info, 0);
1210 oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, gaim_parse_incoming_im, 0);
1211 oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, gaim_parse_misses, 0);
1212 oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, gaim_parse_clientauto, 0);
1213 oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_ERROR, gaim_parse_msgerr, 0);
1214 oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MTN, gaim_parse_mtn, 0);
1215 oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_ACK, gaim_parse_msgack, 0);
1216 oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
1217 oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
1218 oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_ALIAS, gaim_icqalias, 0);
1219 oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_INFO, gaim_icqinfo, 0);
1220 oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, gaim_parse_locaterights, 0);
1221 oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_USERINFO, gaim_parse_userinfo, 0);
1222 oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_ERROR, gaim_parse_locerr, 0);
1223 oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_GOTINFOBLOCK, gaim_got_infoblock, 0);
1224 oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0001, gaim_parse_genericerr, 0);
1225 oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x000f, gaim_selfinfo, 0);
1226 oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x001f, gaim_memrequest, 0);
1227 oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0021, oscar_icon_req,0);
1228 oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_RATECHANGE, gaim_parse_ratechange, 0);
1229 oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_REDIRECT, gaim_handle_redirect, 0);
1230 oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_MOTD, gaim_parse_motd, 0);
1231 oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_EVIL, gaim_parse_evilnotify, 0);
1232 oscar_data_addhandler(od, SNAC_FAMILY_POPUP, 0x0002, gaim_popup, 0);
1233 oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, SNAC_SUBTYPE_USERLOOKUP_ERROR, gaim_parse_searcherror, 0);
1234 oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, gaim_parse_searchreply, 0);
1235
1236 gaim_debug_misc("oscar", "oscar_login: gc = %p\n", gc);
1237
1238 if (!aim_snvalid(gaim_account_get_username(account))) {
1239 gchar *buf;
1240 buf = g_strdup_printf(_("Unable to login: Could not sign on as %s because the screen name is invalid. Screen names must either start with a letter and contain only letters, numbers and spaces, or contain only numbers."), gaim_account_get_username(account));
1241 gc->wants_to_die = TRUE;
1242 gaim_connection_error(gc, buf);
1243 g_free(buf);
1244 }
1245
1246 if (aim_sn_is_icq((gaim_account_get_username(account)))) {
1247 od->icq = TRUE;
1248 } else {
1249 gc->flags |= GAIM_CONNECTION_HTML;
1250 gc->flags |= GAIM_CONNECTION_AUTO_RESP;
1251 }
1252
1253 /* Connect to core Gaim signals */
1254 gaim_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_cb, gc);
1255
1256 newconn = flap_connection_new(od, SNAC_FAMILY_AUTH);
1257 newconn->connect_data = gaim_proxy_connect(NULL, account,
1258 gaim_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER),
1259 gaim_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
1260 connection_established_cb, newconn);
1261 if (newconn->connect_data == NULL)
1262 {
1263 gaim_connection_error(gc, _("Couldn't connect to host"));
1264 return;
1265 }
1266
1267 gaim_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS);
1268 ck[0] = 0x5a;
1269 }
1270
1271 void
1272 oscar_close(GaimConnection *gc)
1273 {
1274 OscarData *od;
1275
1276 od = (OscarData *)gc->proto_data;
1277
1278 while (od->oscar_chats)
1279 {
1280 struct chat_connection *cc = od->oscar_chats->data;
1281 od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
1282 oscar_chat_destroy(cc);
1283 }
1284 while (od->create_rooms)
1285 {
1286 struct create_room *cr = od->create_rooms->data;
1287 g_free(cr->name);
1288 od->create_rooms = g_slist_remove(od->create_rooms, cr);
1289 g_free(cr);
1290 }
1291 oscar_data_destroy(od);
1292 gc->proto_data = NULL;
1293
1294 gaim_prefs_disconnect_by_handle(gc);
1295
1296 gaim_debug_info("oscar", "Signed off.\n");
1297 }
1298
1299 static int
1300 gaim_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
1301 {
1302 GaimConnection *gc = od->gc;
1303 GaimAccount *account = gc->account;
1304 char *host; int port;
1305 int i;
1306 FlapConnection *newconn;
1307 va_list ap;
1308 struct aim_authresp_info *info;
1309
1310 port = gaim_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT);
1311
1312 va_start(ap, fr);
1313 info = va_arg(ap, struct aim_authresp_info *);
1314 va_end(ap);
1315
1316 gaim_debug_info("oscar",
1317 "inside auth_resp (Screen name: %s)\n", info->sn);
1318
1319 if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) {
1320 char buf[256];
1321 switch (info->errorcode) {
1322 case 0x01:
1323 /* Unregistered screen name */
1324 gc->wants_to_die = TRUE;
1325 gaim_connection_error(gc, _("Invalid screen name."));
1326 break;
1327 case 0x05:
1328 /* Incorrect password */
1329 gc->wants_to_die = TRUE;
1330 if (!gaim_account_get_remember_password(account))
1331 gaim_account_set_password(account, NULL);
1332 gaim_connection_error(gc, _("Incorrect password."));
1333 break;
1334 case 0x11:
1335 /* Suspended account */
1336 gc->wants_to_die = TRUE;
1337 gaim_connection_error(gc, _("Your account is currently suspended."));
1338 break;
1339 case 0x14:
1340 /* service temporarily unavailable */
1341 gaim_connection_error(gc, _("The AOL Instant Messenger service is temporarily unavailable."));
1342 break;
1343 case 0x18:
1344 /* connecting too frequently */
1345 gc->wants_to_die = TRUE;
1346 gaim_connection_error(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."));
1347 break;
1348 case 0x1c:
1349 /* client too old */
1350 gc->wants_to_die = TRUE;
1351 g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), GAIM_WEBSITE);
1352 gaim_connection_error(gc, buf);
1353 break;
1354 default:
1355 gaim_connection_error(gc, _("Authentication failed"));
1356 break;
1357 }
1358 gaim_debug_error("oscar", "Login Error Code 0x%04hx\n", info->errorcode);
1359 gaim_debug_error("oscar", "Error URL: %s\n", info->errorurl);
1360 od->killme = TRUE;
1361 return 1;
1362 }
1363
1364 gaim_debug_misc("oscar", "Reg status: %hu\n", info->regstatus);
1365 gaim_debug_misc("oscar", "E-mail: %s\n",
1366 (info->email != NULL) ? info->email : "null");
1367 gaim_debug_misc("oscar", "BOSIP: %s\n", info->bosip);
1368 gaim_debug_info("oscar", "Closing auth connection...\n");
1369 flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL);
1370
1371 for (i = 0; i < strlen(info->bosip); i++) {
1372 if (info->bosip[i] == ':') {
1373 port = atoi(&(info->bosip[i+1]));
1374 break;
1375 }
1376 }
1377 host = g_strndup(info->bosip, i);
1378 newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE);
1379 newconn->cookielen = info->cookielen;
1380 newconn->cookie = g_memdup(info->cookie, info->cookielen);
1381 newconn->connect_data = gaim_proxy_connect(NULL, account, host, port,
1382 connection_established_cb, newconn);
1383 g_free(host);
1384 if (newconn->connect_data == NULL)
1385 {
1386 gaim_connection_error(gc, _("Could Not Connect"));
1387 od->killme = TRUE;
1388 return 0;
1389 }
1390
1391 gaim_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
1392 ck[3] = 0x64;
1393
1394 return 1;
1395 }
1396
1397 static void
1398 gaim_parse_auth_securid_request_yes_cb(gpointer user_data, const char *msg)
1399 {
1400 GaimConnection *gc = user_data;
1401 OscarData *od = gc->proto_data;
1402
1403 aim_auth_securid_send(od, msg);
1404 }
1405
1406 static void
1407 gaim_parse_auth_securid_request_no_cb(gpointer user_data, const char *value)
1408 {
1409 GaimConnection *gc = user_data;
1410 OscarData *od = gc->proto_data;
1411
1412 /* Disconnect */
1413 gc->wants_to_die = TRUE;
1414 gaim_connection_error(gc, _("The SecurID key entered is invalid."));
1415 od->killme = TRUE;
1416 }
1417
1418 static int
1419 gaim_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
1420 {
1421 GaimConnection *gc = od->gc;
1422 GaimAccount *account = gaim_connection_get_account(gc);
1423 gchar *primary;
1424
1425 gaim_debug_info("oscar", "Got SecurID request\n");
1426
1427 primary = g_strdup_printf("Enter the SecurID key for %s.", gaim_account_get_username(account));
1428 gaim_request_input(gc, NULL, _("Enter SecurID"), primary,
1429 _("Enter the 6 digit number from the digital display."),
1430 FALSE, FALSE, NULL,
1431 _("_OK"), G_CALLBACK(gaim_parse_auth_securid_request_yes_cb),
1432 _("_Cancel"), G_CALLBACK(gaim_parse_auth_securid_request_no_cb),
1433 gc);
1434 g_free(primary);
1435
1436 return 1;
1437 }
1438
1439 /* XXX - Should use gaim_util_fetch_url for the below stuff */
1440 struct pieceofcrap {
1441 GaimConnection *gc;
1442 unsigned long offset;
1443 unsigned long len;
1444 char *modname;
1445 int fd;
1446 FlapConnection *conn;
1447 unsigned int inpa;
1448 };
1449
1450 static void damn_you(gpointer data, gint source, GaimInputCondition c)
1451 {
1452 struct pieceofcrap *pos = data;
1453 OscarData *od = pos->gc->proto_data;
1454 char in = '\0';
1455 int x = 0;
1456 unsigned char m[17];
1457
1458 while (read(pos->fd, &in, 1) == 1) {
1459 if (in == '\n')
1460 x++;
1461 else if (in != '\r')
1462 x = 0;
1463 if (x == 2)
1464 break;
1465 in = '\0';
1466 }
1467 if (in != '\n') {
1468 char buf[256];
1469 g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. You may want to use TOC until "
1470 "this is fixed. Check %s for updates."), GAIM_WEBSITE);
1471 gaim_notify_warning(pos->gc, NULL,
1472 _("Gaim was unable to get a valid AIM login hash."),
1473 buf);
1474 gaim_input_remove(pos->inpa);
1475 close(pos->fd);
1476 g_free(pos);
1477 return;
1478 }
1479 if (read(pos->fd, m, 16) != 16)
1480 {
1481 gaim_debug_warning("oscar", "Could not read full AIM login hash "
1482 "from " AIMHASHDATA "--that's bad.\n");
1483 }
1484 m[16] = '\0';
1485 gaim_debug_misc("oscar", "Sending hash: ");
1486 for (x = 0; x < 16; x++)
1487 gaim_debug_misc(NULL, "%02hhx ", (unsigned char)m[x]);
1488
1489 gaim_debug_misc(NULL, "\n");
1490 gaim_input_remove(pos->inpa);
1491 close(pos->fd);
1492 aim_sendmemblock(od, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
1493 g_free(pos);
1494 }
1495
1496 static void
1497 straight_to_hell(gpointer data, gint source, const gchar *error_message)
1498 {
1499 struct pieceofcrap *pos = data;
1500 gchar *buf;
1501
1502 if (!GAIM_CONNECTION_IS_VALID(pos->gc))
1503 {
1504 g_free(pos->modname);
1505 g_free(pos);
1506 return;
1507 }
1508
1509 pos->fd = source;
1510
1511 if (source < 0) {
1512 buf = g_strdup_printf(_("You may be disconnected shortly. You may want to use TOC until "
1513 "this is fixed. Check %s for updates."), GAIM_WEBSITE);
1514 gaim_notify_warning(pos->gc, NULL,
1515 _("Gaim was unable to get a valid AIM login hash."),
1516 buf);
1517 g_free(buf);
1518 g_free(pos->modname);
1519 g_free(pos);
1520 return;
1521 }
1522
1523 buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
1524 pos->offset, pos->len, pos->modname ? pos->modname : "");
1525 write(pos->fd, buf, strlen(buf));
1526 g_free(buf);
1527 g_free(pos->modname);
1528 pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos);
1529 return;
1530 }
1531
1532 /* size of icbmui.ocm, the largest module in AIM 3.5 */
1533 #define AIM_MAX_FILE_SIZE 98304
1534
1535 int gaim_memrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
1536 va_list ap;
1537 struct pieceofcrap *pos;
1538 guint32 offset, len;
1539 char *modname;
1540
1541 va_start(ap, fr);
1542 offset = va_arg(ap, guint32);
1543 len = va_arg(ap, guint32);
1544 modname = va_arg(ap, char *);
1545 va_end(ap);
1546
1547 gaim_debug_misc("oscar", "offset: %u, len: %u, file: %s\n",
1548 offset, len, (modname ? modname : "aim.exe"));
1549
1550 if (len == 0) {
1551 gaim_debug_misc("oscar", "len is 0, hashing NULL\n");
1552 aim_sendmemblock(od, conn, offset, len, NULL,
1553 AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
1554 return 1;
1555 }
1556 /* uncomment this when you're convinced it's right. remember, it's been wrong before. */
1557 #if 0
1558 if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
1559 char *buf;
1560 int i = 8;
1561 if (modname)
1562 i += strlen(modname);
1563 buf = g_malloc(i);
1564 i = 0;
1565 if (modname) {
1566 memcpy(buf, modname, strlen(modname));
1567 i += strlen(modname);
1568 }
1569 buf[i++] = offset & 0xff;
1570 buf[i++] = (offset >> 8) & 0xff;
1571 buf[i++] = (offset >> 16) & 0xff;
1572 buf[i++] = (offset >> 24) & 0xff;
1573 buf[i++] = len & 0xff;
1574 buf[i++] = (len >> 8) & 0xff;
1575 buf[i++] = (len >> 16) & 0xff;
1576 buf[i++] = (len >> 24) & 0xff;
1577 gaim_debug_misc("oscar", "len + offset is invalid, "
1578 "hashing request\n");
1579 aim_sendmemblock(od, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
1580 g_free(buf);
1581 return 1;
1582 }
1583 #endif
1584
1585 pos = g_new0(struct pieceofcrap, 1);
1586 pos->gc = od->gc;
1587 pos->conn = conn;
1588
1589 pos->offset = offset;
1590 pos->len = len;
1591 pos->modname = g_strdup(modname);
1592
1593 /* TODO: Keep track of this return value. */
1594 if (gaim_proxy_connect(NULL, pos->gc->account, "gaim.sourceforge.net", 80,
1595 straight_to_hell, pos) == NULL)
1596 {
1597 char buf[256];
1598 if (pos->modname)
1599 g_free(pos->modname);
1600 g_free(pos);
1601 g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. "
1602 "Check %s for updates."), GAIM_WEBSITE);
1603 gaim_notify_warning(pos->gc, NULL,
1604 _("Gaim was unable to get a valid login hash."),
1605 buf);
1606 }
1607
1608 return 1;
1609 }
1610
1611 static int
1612 gaim_parse_login(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
1613 {
1614 GaimConnection *gc;
1615 GaimAccount *account;
1616 ClientInfo info = CLIENTINFO_GAIM;
1617 va_list ap;
1618 char *key;
1619
1620 gc = od->gc;
1621 account = gaim_connection_get_account(gc);
1622
1623 va_start(ap, fr);
1624 key = va_arg(ap, char *);
1625 va_end(ap);
1626
1627 aim_send_login(od, conn, gaim_account_get_username(account),
1628 gaim_connection_get_password(gc), &info, key);
1629
1630 gaim_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS);
1631 ck[2] = 0x6c;
1632
1633 return 1;
1634 }
1635
1636 static int
1637 gaim_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
1638 {
1639 GaimConnection *gc = od->gc;
1640 GaimAccount *account = gaim_connection_get_account(gc);
1641 char *host, *separator;
1642 int port;
1643 FlapConnection *newconn;
1644 va_list ap;
1645 struct aim_redirect_data *redir;
1646
1647 va_start(ap, fr);
1648 redir = va_arg(ap, struct aim_redirect_data *);
1649 va_end(ap);
1650
1651 port = gaim_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT);
1652 separator = strchr(redir->ip, ':');
1653 if (separator != NULL)
1654 {
1655 host = g_strndup(redir->ip, separator - redir->ip);
1656 port = atoi(separator + 1);
1657 }
1658 else
1659 host = g_strdup(redir->ip);
1660
1661 gaim_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx\n",
1662 host, port, redir->group);
1663 newconn = flap_connection_new(od, redir->group);
1664 newconn->cookielen = redir->cookielen;
1665 newconn->cookie = g_memdup(redir->cookie, redir->cookielen);
1666 if (newconn->type == SNAC_FAMILY_CHAT)
1667 {
1668 struct chat_connection *cc;
1669 cc = g_new0(struct chat_connection, 1);
1670 cc->conn = newconn;
1671 cc->gc = gc;
1672 cc->name = g_strdup(redir->chat.room);
1673 cc->exchange = redir->chat.exchange;
1674 cc->instance = redir->chat.instance;
1675 cc->show = extract_name(redir->chat.room);
1676 newconn->new_conn_data = cc;
1677 gaim_debug_info("oscar", "Connecting to chat room %s exchange %hu\n", cc->name, cc->exchange);
1678 }
1679
1680 newconn->connect_data = gaim_proxy_connect(NULL, account, host, port,
1681 connection_established_cb, newconn);
1682 if (newconn->connect_data == NULL)
1683 {
1684 flap_connection_schedule_destroy(newconn,
1685 OSCAR_DISCONNECT_COULD_NOT_CONNECT,
1686 _("Unable to initialize connection"));
1687 gaim_debug_error("oscar", "Unable to connect to FLAP server "
1688 "of type 0x%04hx\n", redir->group);
1689 }
1690 g_free(host);
1691
1692 return 1;
1693 }
1694
1695 static int gaim_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
1696 {
1697 GaimConnection *gc;
1698 GaimAccount *account;
1699 GaimPresence *presence;
1700 struct buddyinfo *bi;
1701 time_t time_idle = 0, signon = 0;
1702 int type = 0;
1703 gboolean buddy_is_away = FALSE;
1704 const char *status_id;
1705 gboolean have_status_message = FALSE;
1706 char *message = NULL;
1707 va_list ap;
1708 aim_userinfo_t *info;
1709
1710 gc = od->gc;
1711 account = gaim_connection_get_account(gc);
1712 presence = gaim_account_get_presence(account);
1713
1714 va_start(ap, fr);
1715 info = va_arg(ap, aim_userinfo_t *);
1716 va_end(ap);
1717
1718 g_return_val_if_fail(info != NULL, 1);
1719 g_return_val_if_fail(info->sn != NULL, 1);
1720
1721 if (info->present & AIM_USERINFO_PRESENT_FLAGS) {
1722 if (info->flags & AIM_FLAG_AWAY)
1723 buddy_is_away = TRUE;
1724 }
1725 if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
1726 type = info->icqinfo.status;
1727 if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
1728 (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
1729 buddy_is_away = TRUE;
1730 }
1731 }
1732
1733 if (aim_sn_is_icq(info->sn)) {
1734 if (type & AIM_ICQ_STATE_CHAT)
1735 status_id = OSCAR_STATUS_ID_FREE4CHAT;
1736 else if (type & AIM_ICQ_STATE_DND)
1737 status_id = OSCAR_STATUS_ID_DND;
1738 else if (type & AIM_ICQ_STATE_OUT)
1739 status_id = OSCAR_STATUS_ID_NA;
1740 else if (type & AIM_ICQ_STATE_BUSY)
1741 status_id = OSCAR_STATUS_ID_OCCUPIED;
1742 else if (type & AIM_ICQ_STATE_AWAY)
1743 status_id = OSCAR_STATUS_ID_AWAY;
1744 else if (type & AIM_ICQ_STATE_INVISIBLE)
1745 status_id = OSCAR_STATUS_ID_INVISIBLE;
1746 else
1747 status_id = OSCAR_STATUS_ID_AVAILABLE;
1748 } else {
1749 if (buddy_is_away)
1750 status_id = OSCAR_STATUS_ID_AWAY;
1751 else
1752 status_id = OSCAR_STATUS_ID_AVAILABLE;
1753 }
1754
1755 /*
1756 * Handle the available message. If info->status is NULL then the user
1757 * may or may not have an available message, so don't do anything. If
1758 * info->status is set to the empty string, then the user's client DOES
1759 * support available messages and the user DOES NOT have one set.
1760 * Otherwise info->status contains the available message.
1761 */
1762 if (info->status != NULL)
1763 {
1764 have_status_message = TRUE;
1765 if (info->status[0] != '\0')
1766 message = oscar_encoding_to_utf8(info->status_encoding,
1767 info->status, info->status_len);
1768 }
1769
1770 if (have_status_message)
1771 {
1772 gaim_prpl_got_user_status(account, info->sn, status_id,
1773 "message", message, NULL);
1774 g_free(message);
1775 }
1776 else
1777 {
1778 GaimBuddy *b = gaim_find_buddy(account, info->sn);
1779 GaimStatus *status;
1780 const char *active_status_id;
1781
1782 status = gaim_presence_get_active_status(gaim_buddy_get_presence(b));
1783 active_status_id = gaim_status_get_id(status);
1784
1785 if (!active_status_id || strcmp(active_status_id, status_id))
1786 gaim_prpl_got_user_status(account, info->sn, status_id, NULL);
1787 }
1788
1789 /* Login time stuff */
1790 if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
1791 signon = info->onlinesince;
1792 else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
1793 signon = time(NULL) - info->sessionlen;
1794 if (!aim_sncmp(gaim_account_get_username(account), info->sn)) {
1795 gaim_connection_set_display_name(gc, info->sn);
1796 od->timeoffset = signon - gaim_presence_get_login_time(presence);
1797 }
1798 gaim_prpl_got_user_login_time(account, info->sn, signon - od->timeoffset);
1799
1800 /* Idle time stuff */
1801 /* info->idletime is the number of minutes that this user has been idle */
1802 if (info->present & AIM_USERINFO_PRESENT_IDLE)
1803 time_idle = time(NULL) - info->idletime * 60;
1804
1805 if (time_idle > 0)
1806 gaim_prpl_got_user_idle(account, info->sn, TRUE, time_idle);
1807 else
1808 gaim_prpl_got_user_idle(account, info->sn, FALSE, 0);
1809
1810 /* Server stored icon stuff */
1811 bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(account, info->sn));
1812 if (!bi) {
1813 bi = g_new0(struct buddyinfo, 1);
1814 g_hash_table_insert(od->buddyinfo, g_strdup(gaim_normalize(account, info->sn)), bi);
1815 }
1816 bi->typingnot = FALSE;
1817 bi->ico_informed = FALSE;
1818 bi->ipaddr = info->icqinfo.ipaddr;
1819
1820 if (info->iconcsumlen) {
1821 const char *filename, *saved_b16 = NULL;
1822 char *b16 = NULL, *filepath = NULL;
1823 GaimBuddy *b = NULL;
1824
1825 b16 = gaim_base16_encode(info->iconcsum, info->iconcsumlen);
1826 b = gaim_find_buddy(account, info->sn);
1827 /*
1828 * If for some reason the checksum is valid, but cached file is not..
1829 * we want to know.
1830 */
1831 if (b != NULL)
1832 filename = gaim_blist_node_get_string((GaimBlistNode*)b, "buddy_icon");
1833 else
1834 filename = NULL;
1835 if (filename != NULL) {
1836 if (g_file_test(filename, G_FILE_TEST_EXISTS))
1837 saved_b16 = gaim_blist_node_get_string((GaimBlistNode*)b,
1838 "icon_checksum");
1839 else {
1840 filepath = g_build_filename(gaim_buddy_icons_get_cache_dir(),
1841 filename, NULL);
1842 if (g_file_test(filepath, G_FILE_TEST_EXISTS))
1843 saved_b16 = gaim_blist_node_get_string((GaimBlistNode*)b,
1844 "icon_checksum");
1845 g_free(filepath);
1846 }
1847 } else
1848 saved_b16 = NULL;
1849
1850 if (!b16 || !saved_b16 || strcmp(b16, saved_b16)) {
1851 GSList *cur = od->requesticon;
1852 while (cur && aim_sncmp((char *)cur->data, info->sn))
1853 cur = cur->next;
1854 if (!cur) {
1855 od->requesticon = g_slist_append(od->requesticon, g_strdup(gaim_normalize(account, info->sn)));
1856 if (od->icontimer == 0)
1857 od->icontimer = gaim_timeout_add(500, gaim_icon_timerfunc, gc);
1858 }
1859 }
1860 g_free(b16);
1861 }
1862
1863 return 1;
1864 }
1865
1866 static void gaim_check_comment(OscarData *od, const char *str) {
1867 if ((str == NULL) || strcmp(str, (const char *)ck))
1868 aim_locate_setcaps(od, gaim_caps);
1869 else
1870 aim_locate_setcaps(od, gaim_caps | OSCAR_CAPABILITY_SECUREIM);
1871 }
1872
1873 static int gaim_parse_offgoing(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
1874 GaimConnection *gc = od->gc;
1875 GaimAccount *account = gaim_connection_get_account(gc);
1876 va_list ap;
1877 aim_userinfo_t *info;
1878
1879 va_start(ap, fr);
1880 info = va_arg(ap, aim_userinfo_t *);
1881 va_end(ap);
1882
1883 gaim_prpl_got_user_status(account, info->sn, OSCAR_STATUS_ID_OFFLINE, NULL);
1884
1885 g_hash_table_remove(od->buddyinfo, gaim_normalize(gc->account, info->sn));
1886
1887 return 1;
1888 }
1889
1890 static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
1891 GaimConnection *gc = od->gc;
1892 GaimAccount *account = gaim_connection_get_account(gc);
1893 GaimMessageFlags flags = 0;
1894 struct buddyinfo *bi;
1895 char *iconfile;
1896 GString *message;
1897 gchar *tmp;
1898 aim_mpmsg_section_t *curpart;
1899 const char *start, *end;
1900 GData *attribs;
1901
1902 gaim_debug_misc("oscar", "Received IM from %s with %d parts\n",
1903 userinfo->sn, args->mpmsg.numparts);
1904
1905 if (args->mpmsg.numparts == 0)
1906 return 1;
1907
1908 bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(account, userinfo->sn));
1909 if (!bi) {
1910 bi = g_new0(struct buddyinfo, 1);
1911 g_hash_table_insert(od->buddyinfo, g_strdup(gaim_normalize(account, userinfo->sn)), bi);
1912 }
1913
1914 if (args->icbmflags & AIM_IMFLAGS_AWAY)
1915 flags |= GAIM_MESSAGE_AUTO_RESP;
1916
1917 if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT)
1918 bi->typingnot = TRUE;
1919 else
1920 bi->typingnot = FALSE;
1921
1922 if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) {
1923 gaim_debug_misc("oscar", "%s has an icon\n", userinfo->sn);
1924 if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) {
1925 bi->ico_need = TRUE;
1926 bi->ico_len = args->iconlen;
1927 bi->ico_csum = args->iconsum;
1928 bi->ico_time = args->iconstamp;
1929 }
1930 }
1931
1932 iconfile = gaim_buddy_icons_get_full_path(gaim_account_get_buddy_icon(account));
1933 if ((iconfile != NULL) &&
1934 (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) {
1935 FILE *file;
1936 struct stat st;
1937
1938 if (!g_stat(iconfile, &st)) {
1939 guchar *buf = g_malloc(st.st_size);
1940 file = g_fopen(iconfile, "rb");
1941 if (file) {
1942 /* XXX - Use g_file_get_contents() */
1943 /* g_file_get_contents(iconfile, &data, &len, NULL); */
1944 int len = fread(buf, 1, st.st_size, file);
1945 gaim_debug_info("oscar",
1946 "Sending buddy icon to %s (%d bytes, "
1947 "%lu reported)\n",
1948 userinfo->sn, len, st.st_size);
1949 aim_im_sendch2_icon(od, userinfo->sn, buf, st.st_size,
1950 st.st_mtime, aimutil_iconsum(buf, st.st_size));
1951 fclose(file);
1952 } else
1953 gaim_debug_error("oscar", "Can't open buddy icon file!\n");
1954 g_free(buf);
1955 } else
1956 gaim_debug_error("oscar", "Can't stat buddy icon file!\n");
1957 }
1958 g_free(iconfile);
1959
1960 message = g_string_new("");
1961 curpart = args->mpmsg.parts;
1962 while (curpart != NULL) {
1963 tmp = gaim_plugin_oscar_decode_im_part(account, userinfo->sn, curpart->charset,
1964 curpart->charsubset, curpart->data, curpart->datalen);
1965 if (tmp != NULL) {
1966 g_string_append(message, tmp);
1967 g_free(tmp);
1968 }
1969
1970 curpart = curpart->next;
1971 }
1972 tmp = g_string_free(message, FALSE);
1973
1974 /*
1975 * If the message is from an ICQ user and to an ICQ user then escape any HTML,
1976 * because HTML is not sent over ICQ as a means to format a message.
1977 * So any HTML we receive is intended to be displayed. Also, \r\n must be
1978 * replaced with <br>
1979 *
1980 * Note: There *may* be some clients which send messages as HTML formatted -
1981 * they need to be special-cased somehow.
1982 */
1983 if (aim_sn_is_icq(gaim_account_get_username(account)) && aim_sn_is_icq(userinfo->sn)) {
1984 /* being recevied by ICQ from ICQ - escape HTML so it is displayed as sent */
1985 gchar *tmp2 = g_markup_escape_text(tmp, -1);
1986 g_free(tmp);
1987 tmp = tmp2;
1988 tmp2 = gaim_strreplace(tmp, "\r\n", "<br>");
1989 g_free(tmp);
1990 tmp = tmp2;
1991 }
1992
1993 /*
1994 * Convert iChat color tags to normal font tags.
1995 */
1996 if (gaim_markup_find_tag("body", tmp, &start, &end, &attribs))
1997 {
1998 const char *ichattextcolor, *ichatballooncolor;
1999
2000 ichattextcolor = g_datalist_get_data(&attribs, "ichattextcolor");
2001 if (ichattextcolor != NULL)
2002 {
2003 gchar *tmp2;
2004 tmp2 = g_strdup_printf("<font color=\"%s\">%s</font>", ichattextcolor, tmp);
2005 g_free(tmp);
2006 tmp = tmp2;
2007 }
2008
2009 ichatballooncolor = g_datalist_get_data(&attribs, "ichatballooncolor");
2010 if (ichatballooncolor != NULL)
2011 {
2012 gchar *tmp2;
2013 tmp2 = g_strdup_printf("<font back=\"%s\">%s</font>", ichatballooncolor, tmp);
2014 g_free(tmp);
2015 tmp = tmp2;
2016 }
2017
2018 g_datalist_clear(&attribs);
2019 }
2020
2021 serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL));
2022 g_free(tmp);
2023
2024 return 1;
2025 }
2026
2027 static int
2028 incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, IcbmArgsCh2 *args)
2029 {
2030 GaimConnection *gc;
2031 GaimAccount *account;
2032 char *message = NULL;
2033
2034 g_return_val_if_fail(od != NULL, 0);
2035 g_return_val_if_fail(od->gc != NULL, 0);
2036
2037 gc = od->gc;
2038 account = gaim_connection_get_account(gc);
2039 od = gc->proto_data;
2040
2041 if (args == NULL)
2042 return 0;
2043
2044 gaim_debug_misc("oscar", "Incoming rendezvous message of type %u, "
2045 "user %s, status %hu\n", args->type, userinfo->sn, args->status);
2046
2047 if (args->msg != NULL)
2048 {
2049 if (args->encoding != NULL)
2050 {
2051 char *encoding = NULL;
2052 encoding = oscar_encoding_extract(args->encoding);
2053 message = oscar_encoding_to_utf8(encoding, args->msg, args->msglen);
2054 g_free(encoding);
2055 } else {
2056 if (g_utf8_validate(args->msg, args->msglen, NULL))
2057 message = g_strdup(args->msg);
2058 }
2059 }
2060
2061 if (args->type & OSCAR_CAPABILITY_CHAT)
2062 {
2063 char *encoding, *utf8name, *tmp;
2064 GHashTable *components;
2065
2066 if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) {
2067 g_free(message);
2068 return 1;
2069 }
2070 encoding = args->encoding ? oscar_encoding_extract(args->encoding) : NULL;
2071 utf8name = oscar_encoding_to_utf8(encoding,
2072 args->info.chat.roominfo.name,
2073 args->info.chat.roominfo.namelen);
2074 g_free(encoding);
2075
2076 tmp = extract_name(utf8name);
2077 if (tmp != NULL)
2078 {
2079 g_free(utf8name);
2080 utf8name = tmp;
2081 }
2082
2083 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
2084 g_free);
2085 g_hash_table_replace(components, g_strdup("room"), utf8name);
2086 g_hash_table_replace(components, g_strdup("exchange"),
2087 g_strdup_printf("%d", args->info.chat.roominfo.exchange));
2088 serv_got_chat_invite(gc,
2089 utf8name,
2090 userinfo->sn,
2091 message,
2092 components);
2093 }
2094
2095 else if ((args->type & OSCAR_CAPABILITY_SENDFILE) ||
2096 (args->type & OSCAR_CAPABILITY_DIRECTIM))
2097 {
2098 if (args->status == AIM_RENDEZVOUS_PROPOSE)
2099 {
2100 peer_connection_got_proposition(od, userinfo->sn, message, args);
2101 }
2102 else if (args->status == AIM_RENDEZVOUS_CANCEL)
2103 {
2104 /* The other user canceled a peer request */
2105 PeerConnection *conn;
2106
2107 conn = peer_connection_find_by_cookie(od, userinfo->sn, args->cookie);
2108 /*
2109 * If conn is NULL it means we haven't tried to create
2110 * a connection with that user. They may be trying to
2111 * do something malicious.
2112 */
2113 if (conn != NULL)
2114 {
2115 peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
2116 }
2117 }
2118 else if (args->status == AIM_RENDEZVOUS_CONNECTED)
2119 {
2120 /* Remote user has accepted our peer request */
2121 PeerConnection *conn;
2122
2123 conn = peer_connection_find_by_cookie(od, userinfo->sn, args->cookie);
2124 /*
2125 * If conn is NULL it means we haven't tried to create
2126 * a connection with that user. They may be trying to
2127 * do something malicious.
2128 */
2129 if (conn != NULL)
2130 {
2131 if (conn->listenerfd != -1)
2132 {
2133 /*
2134 * If they are connecting directly to us then
2135 * continue the peer negotiation by
2136 * accepting connections on our listener port.
2137 */
2138 conn->watcher_incoming = gaim_input_add(conn->listenerfd,
2139 GAIM_INPUT_READ, peer_connection_listen_cb, conn);
2140 }
2141 }
2142 }
2143 }
2144
2145 else if (args->type & OSCAR_CAPABILITY_GETFILE)
2146 {
2147 }
2148
2149 else if (args->type & OSCAR_CAPABILITY_TALK)
2150 {
2151 }
2152
2153 else if (args->type & OSCAR_CAPABILITY_BUDDYICON)
2154 {
2155 gaim_buddy_icons_set_for_user(account, userinfo->sn,
2156 args->info.icon.icon,
2157 args->info.icon.length);
2158 }
2159
2160 else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY)
2161 {
2162 gaim_debug_error("oscar", "Got an ICQ Server Relay message of "
2163 "type %d\n", args->info.rtfmsg.msgtype);
2164 }
2165
2166 else
2167 {
2168 gaim_debug_error("oscar", "Unknown request class %hu\n",
2169 args->type);
2170 }
2171
2172 g_free(message);
2173
2174 return 1;
2175 }
2176
2177 /*
2178 * Authorization Functions
2179 * Most of these are callbacks from dialogs. They're used by both
2180 * methods of authorization (SSI and old-school channel 4 ICBM)
2181 */
2182 /* When you ask other people for authorization */
2183 static void
2184 gaim_auth_request(struct name_data *data, char *msg)
2185 {
2186 GaimConnection *gc;
2187 OscarData *od;
2188 GaimBuddy *buddy;
2189 GaimGroup *group;
2190
2191 gc = data->gc;
2192 od = gc->proto_data;
2193 buddy = gaim_find_buddy(gaim_connection_get_account(gc), data->name);
2194 if (buddy != NULL)
2195 group = gaim_buddy_get_group(buddy);
2196 else
2197 group = NULL;
2198
2199 if (group != NULL)
2200 {
2201 gaim_debug_info("oscar", "ssi: adding buddy %s to group %s\n",
2202 buddy->name, group->name);
2203 aim_ssi_sendauthrequest(od, data->name, msg ? msg : _("Please authorize me so I can add you to my buddy list."));
2204 if (!aim_ssi_itemlist_finditem(od->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY))
2205 aim_ssi_addbuddy(od, buddy->name, group->name, gaim_buddy_get_alias_only(buddy), NULL, NULL, 1);
2206 }
2207 }
2208
2209 static void
2210 gaim_auth_dontrequest(struct name_data *data)
2211 {
2212 GaimConnection *gc = data->gc;
2213 GaimBuddy *b = gaim_find_buddy(gaim_connection_get_account(gc), data->name);
2214
2215 /* Remove from local list */
2216 gaim_blist_remove_buddy(b);
2217
2218 oscar_free_name_data(data);
2219 }
2220
2221
2222 static void
2223 gaim_auth_sendrequest(GaimConnection *gc, const char *name)
2224 {
2225 struct name_data *data;
2226
2227 data = g_new0(struct name_data, 1);
2228 data->gc = gc;
2229 data->name = g_strdup(name);
2230
2231 gaim_request_input(data->gc, NULL, _("Authorization Request Message:"),
2232 NULL, _("Please authorize me!"), TRUE, FALSE, NULL,
2233 _("_OK"), G_CALLBACK(gaim_auth_request),
2234 _("_Cancel"), G_CALLBACK(gaim_auth_dontrequest),
2235 data);
2236 }
2237
2238
2239 static void
2240 gaim_auth_sendrequest_menu(GaimBlistNode *node, gpointer ignored)
2241 {
2242 GaimBuddy *buddy;
2243 GaimConnection *gc;
2244
2245 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
2246
2247 buddy = (GaimBuddy *) node;
2248 gc = gaim_account_get_connection(buddy->account);
2249 gaim_auth_sendrequest(gc, buddy->name);
2250 }
2251
2252 /* When other people ask you for authorization */
2253 static void
2254 gaim_auth_grant(struct name_data *data)
2255 {
2256 GaimConnection *gc = data->gc;
2257 OscarData *od = gc->proto_data;
2258
2259 aim_ssi_sendauthreply(od, data->name, 0x01, NULL);
2260
2261 oscar_free_name_data(data);
2262 }
2263
2264 /* When other people ask you for authorization */
2265 static void
2266 gaim_auth_dontgrant(struct name_data *data, char *msg)
2267 {
2268 GaimConnection *gc = data->gc;
2269 OscarData *od = gc->proto_data;
2270
2271 aim_ssi_sendauthreply(od, data->name, 0x00, msg ? msg : _("No reason given."));
2272 }
2273
2274 static void
2275 gaim_auth_dontgrant_msgprompt(struct name_data *data)
2276 {
2277 gaim_request_input(data->gc, NULL, _("Authorization Denied Message:"),
2278 NULL, _("No reason given."), TRUE, FALSE, NULL,
2279 _("_OK"), G_CALLBACK(gaim_auth_dontgrant),
2280 _("_Cancel"), G_CALLBACK(oscar_free_name_data),
2281 data);
2282 }
2283
2284 /* When someone sends you buddies */
2285 static void
2286 gaim_icq_buddyadd(struct name_data *data)
2287 {
2288 GaimConnection *gc = data->gc;
2289
2290 gaim_blist_request_add_buddy(gaim_connection_get_account(gc), data->name, NULL, data->nick);
2291
2292 oscar_free_name_data(data);
2293 }
2294
2295 static int
2296 incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args, time_t t)
2297 {
2298 GaimConnection *gc = od->gc;
2299 GaimAccount *account = gaim_connection_get_account(gc);
2300 gchar **msg1, **msg2;
2301 int i, numtoks;
2302
2303 if (!args->type || !args->msg || !args->uin)
2304 return 1;
2305
2306 gaim_debug_info("oscar",
2307 "Received a channel 4 message of type 0x%02hx.\n",
2308 args->type);
2309
2310 /*
2311 * Split up the message at the delimeter character, then convert each
2312 * string to UTF-8. Unless, of course, this is a type 1 message. If
2313 * this is a type 1 message, then the delimiter 0xfe could be a valid
2314 * character in whatever encoding the message was sent in. Type 1
2315 * messages are always made up of only one part, so we can easily account
2316 * for this suck-ass part of the protocol by splitting the string into at
2317 * most 1 baby string.
2318 */
2319 msg1 = g_strsplit(args->msg, "\376", (args->type == 0x01 ? 1 : 0));
2320 for (numtoks=0; msg1[numtoks]; numtoks++);
2321 msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *));
2322 for (i=0; msg1[i]; i++) {
2323 gchar *uin = g_strdup_printf("%u", args->uin);
2324
2325 gaim_str_strip_char(msg1[i], '\r');
2326 /* TODO: Should use an encoding other than ASCII? */
2327 msg2[i] = gaim_plugin_oscar_decode_im_part(account, uin, AIM_CHARSET_ASCII, 0x0000, msg1[i], strlen(msg1[i]));
2328 g_free(uin);
2329 }
2330 msg2[i] = NULL;
2331
2332 switch (args->type) {
2333 case 0x01: { /* MacICQ message or basic offline message */
2334 if (i >= 1) {
2335 gchar *uin = g_strdup_printf("%u", args->uin);
2336 gchar *tmp;
2337
2338 /* If the message came from an ICQ user then escape any HTML */
2339 tmp = g_markup_escape_text(msg2[0], -1);
2340
2341 if (t) { /* This is an offline message */
2342 /* The timestamp is UTC-ish, so we need to get the offset */
2343 #ifdef HAVE_TM_GMTOFF
2344 time_t now;
2345 struct tm *tm;
2346 now = time(NULL);
2347 tm = localtime(&now);
2348 t += tm->tm_gmtoff;
2349 #else
2350 # ifdef HAVE_TIMEZONE
2351 tzset();
2352 t -= timezone;
2353 # endif
2354 #endif
2355 serv_got_im(gc, uin, tmp, 0, t);
2356 } else { /* This is a message from MacICQ/Miranda */
2357 serv_got_im(gc, uin, tmp, 0, time(NULL));
2358 }
2359 g_free(uin);
2360 g_free(tmp);
2361 }
2362 } break;
2363
2364 case 0x04: { /* Someone sent you a URL */
2365 if (i >= 2) {
2366 if (msg2[1] != NULL) {
2367 gchar *uin = g_strdup_printf("%u", args->uin);
2368 gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>",
2369 msg2[1],
2370 (msg2[0] && msg2[0][0]) ? msg2[0] : msg2[1]);
2371 serv_got_im(gc, uin, message, 0, time(NULL));
2372 g_free(uin);
2373 g_free(message);
2374 }
2375 }
2376 } break;
2377
2378 case 0x06: { /* Someone requested authorization */
2379 if (i >= 6) {
2380 struct name_data *data = g_new(struct name_data, 1);
2381 gchar *sn = g_strdup_printf("%u", args->uin);
2382 gchar *reason = NULL;
2383
2384 if (msg2[5] != NULL)
2385 reason = gaim_plugin_oscar_decode_im_part(account, sn, AIM_CHARSET_CUSTOM, 0x0000, msg2[5], strlen(msg2[5]));
2386
2387 gaim_debug_info("oscar",
2388 "Received an authorization request from UIN %u\n",
2389 args->uin);
2390 data->gc = gc;
2391 data->name = sn;
2392 data->nick = NULL;
2393
2394 gaim_account_request_authorization(account, sn, NULL, NULL,
2395 reason, gaim_find_buddy(account, sn) != NULL,
2396 G_CALLBACK(gaim_auth_grant),
2397 G_CALLBACK(gaim_auth_dontgrant_msgprompt), data);
2398 g_free(reason);
2399 }
2400 } break;
2401
2402 case 0x07: { /* Someone has denied you authorization */
2403 if (i >= 1) {
2404 gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your buddy list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given."));
2405 gaim_notify_info(gc, NULL, _("ICQ authorization denied."),
2406 dialog_msg);
2407 g_free(dialog_msg);
2408 }
2409 } break;
2410
2411 case 0x08: { /* Someone has granted you authorization */
2412 gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args->uin);
2413 gaim_notify_info(gc, NULL, "ICQ authorization accepted.",
2414 dialog_msg);
2415 g_free(dialog_msg);
2416 } break;
2417
2418 case 0x09: { /* Message from the Godly ICQ server itself, I think */
2419 if (i >= 5) {
2420 gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
2421 gaim_notify_info(gc, NULL, "ICQ Server Message", dialog_msg);
2422 g_free(dialog_msg);
2423 }
2424 } break;
2425
2426 case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
2427 if (i >= 6) {
2428 gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
2429 gaim_notify_info(gc, NULL, "ICQ Page", dialog_msg);
2430 g_free(dialog_msg);
2431 }
2432 } break;
2433
2434 case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
2435 if (i >= 6) {
2436 gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ e-mail from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]);
2437 gaim_notify_info(gc, NULL, "ICQ E-Mail", dialog_msg);
2438 g_free(dialog_msg);
2439 }
2440 } break;
2441
2442 case 0x12: {
2443 /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */
2444 /* Someone added you to their buddy list? */
2445 } break;
2446
2447 case 0x13: { /* Someone has sent you some ICQ buddies */
2448 guint i, num;
2449 gchar **text;
2450 text = g_strsplit(args->msg, "\376", 0);
2451 if (text) {
2452 num = 0;
2453 for (i=0; i<strlen(text[0]); i++)
2454 num = num*10 + text[0][i]-48;
2455 for (i=0; i<num; i++) {
2456 struct name_data *data = g_new(struct name_data, 1);
2457 gchar *message = g_strdup_printf(_("ICQ user %u has sent you a buddy: %s (%s)"), args->uin, text[i*2+2], text[i*2+1]);
2458 data->gc = gc;
2459 data->name = g_strdup(text[i*2+1]);
2460 data->nick = g_strdup(text[i*2+2]);
2461
2462 gaim_request_action(gc, NULL, message,
2463 _("Do you want to add this buddy "
2464 "to your buddy list?"),
2465 GAIM_DEFAULT_ACTION_NONE, data, 2,
2466 _("_Add"), G_CALLBACK(gaim_icq_buddyadd),
2467 _("_Decline"), G_CALLBACK(oscar_free_name_data));
2468 g_free(message);
2469 }
2470 g_strfreev(text);
2471 }
2472 } break;
2473
2474 case 0x1a: { /* Someone has sent you a greeting card or requested buddies? */
2475 /* This is boring and silly. */
2476 } break;
2477
2478 default: {
2479 gaim_debug_info("oscar",
2480 "Received a channel 4 message of unknown type "
2481 "(type 0x%02hhx).\n", args->type);
2482 } break;
2483 }
2484
2485 g_strfreev(msg1);
2486 g_strfreev(msg2);
2487
2488 return 1;
2489 }
2490
2491 static int gaim_parse_incoming_im(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2492 guint16 channel;
2493 int ret = 0;
2494 aim_userinfo_t *userinfo;
2495 va_list ap;
2496
2497 va_start(ap, fr);
2498 channel = (guint16)va_arg(ap, unsigned int);
2499 userinfo = va_arg(ap, aim_userinfo_t *);
2500
2501 switch (channel) {
2502 case 1: { /* standard message */
2503 struct aim_incomingim_ch1_args *args;
2504 args = va_arg(ap, struct aim_incomingim_ch1_args *);
2505 ret = incomingim_chan1(od, conn, userinfo, args);
2506 } break;
2507
2508 case 2: { /* rendezvous */
2509 IcbmArgsCh2 *args;
2510 args = va_arg(ap, IcbmArgsCh2 *);
2511 ret = incomingim_chan2(od, conn, userinfo, args);
2512 } break;
2513
2514 case 4: { /* ICQ */
2515 struct aim_incomingim_ch4_args *args;
2516 args = va_arg(ap, struct aim_incomingim_ch4_args *);
2517 ret = incomingim_chan4(od, conn, userinfo, args, 0);
2518 } break;
2519
2520 default: {
2521 gaim_debug_warning("oscar",
2522 "ICBM received on unsupported channel (channel "
2523 "0x%04hx).", channel);
2524 } break;
2525 }
2526
2527 va_end(ap);
2528
2529 return ret;
2530 }
2531
2532 static int gaim_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2533 GaimConnection *gc = od->gc;
2534 GaimAccount *account = gaim_connection_get_account(gc);
2535 char *buf;
2536 va_list ap;
2537 guint16 chan, nummissed, reason;
2538 aim_userinfo_t *userinfo;
2539
2540 va_start(ap, fr);
2541 chan = (guint16)va_arg(ap, unsigned int);
2542 userinfo = va_arg(ap, aim_userinfo_t *);
2543 nummissed = (guint16)va_arg(ap, unsigned int);
2544 reason = (guint16)va_arg(ap, unsigned int);
2545 va_end(ap);
2546
2547 switch(reason) {
2548 case 0: /* Invalid (0) */
2549 buf = g_strdup_printf(
2550 ngettext(
2551 "You missed %hu message from %s because it was invalid.",
2552 "You missed %hu messages from %s because they were invalid.",
2553 nummissed),
2554 nummissed,
2555 userinfo->sn);
2556 break;
2557 case 1: /* Message too large */
2558 buf = g_strdup_printf(
2559 ngettext(
2560 "You missed %hu message from %s because it was too large.",
2561 "You missed %hu messages from %s because they were too large.",
2562 nummissed),
2563 nummissed,
2564 userinfo->sn);
2565 break;
2566 case 2: /* Rate exceeded */
2567 buf = g_strdup_printf(
2568 ngettext(
2569 "You missed %hu message from %s because the rate limit has been exceeded.",
2570 "You missed %hu messages from %s because the rate limit has been exceeded.",
2571 nummissed),
2572 nummissed,
2573 userinfo->sn);
2574 break;
2575 case 3: /* Evil Sender */
2576 buf = g_strdup_printf(
2577 ngettext(
2578 "You missed %hu message from %s because he/she was too evil.",
2579 "You missed %hu messages from %s because he/she was too evil.",
2580 nummissed),
2581 nummissed,
2582 userinfo->sn);
2583 break;
2584 case 4: /* Evil Receiver */
2585 buf = g_strdup_printf(
2586 ngettext(
2587 "You missed %hu message from %s because you are too evil.",
2588 "You missed %hu messages from %s because you are too evil.",
2589 nummissed),
2590 nummissed,
2591 userinfo->sn);
2592 break;
2593 default:
2594 buf = g_strdup_printf(
2595 ngettext(
2596 "You missed %hu message from %s for an unknown reason.",
2597 "You missed %hu messages from %s for an unknown reason.",
2598 nummissed),
2599 nummissed,
2600 userinfo->sn);
2601 break;
2602 }
2603
2604 if (!gaim_conv_present_error(userinfo->sn, account, buf))
2605 gaim_notify_error(od->gc, NULL, buf, NULL);
2606 g_free(buf);
2607
2608 return 1;
2609 }
2610
2611 static int
2612 gaim_parse_clientauto_ch2(OscarData *od, const char *who, guint16 reason, const guchar *cookie)
2613 {
2614 if (reason == 0x0003)
2615 {
2616 /* Rendezvous was refused. */
2617 PeerConnection *conn;
2618
2619 conn = peer_connection_find_by_cookie(od, who, cookie);
2620
2621 if (conn == NULL)
2622 {
2623 gaim_debug_info("oscar", "Received a rendezvous cancel message "
2624 "for a nonexistant connection from %s.\n", who);
2625 }
2626 else
2627 {
2628 peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_REFUSED, NULL);
2629 }
2630 }
2631 else
2632 {
2633 gaim_debug_warning("oscar", "Received an unknown rendezvous "
2634 "message from %s. Type 0x%04hx\n", who, reason);
2635 }
2636
2637 return 0;
2638 }
2639
2640 static int gaim_parse_clientauto_ch4(OscarData *od, char *who, guint16 reason, guint32 state, char *msg) {
2641 GaimConnection *gc = od->gc;
2642
2643 switch(reason) {
2644 case 0x0003: { /* Reply from an ICQ status message request */
2645 char *statusmsg, **splitmsg;
2646 GaimNotifyUserInfo *user_info;
2647
2648 /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
2649 statusmsg = oscar_icqstatus(state);
2650 splitmsg = g_strsplit(msg, "\r\n", 0);
2651
2652 user_info = gaim_notify_user_info_new();
2653
2654 gaim_notify_user_info_add_pair(user_info, _("UIN"), who);
2655 gaim_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
2656 gaim_notify_user_info_add_section_break(user_info);
2657 gaim_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg));
2658
2659 g_free(statusmsg);
2660 g_strfreev(splitmsg);
2661
2662 gaim_notify_userinfo(gc, who, user_info, NULL, NULL);
2663 gaim_notify_user_info_destroy(user_info);
2664
2665 } break;
2666
2667 default: {
2668 gaim_debug_warning("oscar",
2669 "Received an unknown client auto-response from %s. "
2670 "Type 0x%04hx\n", who, reason);
2671 } break;
2672 } /* end of switch */
2673
2674 return 0;
2675 }
2676
2677 static int gaim_parse_clientauto(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2678 va_list ap;
2679 guint16 chan, reason;
2680 char *who;
2681
2682 va_start(ap, fr);
2683 chan = (guint16)va_arg(ap, unsigned int);
2684 who = va_arg(ap, char *);
2685 reason = (guint16)va_arg(ap, unsigned int);
2686
2687 if (chan == 0x0002) { /* File transfer declined */
2688 guchar *cookie = va_arg(ap, guchar *);
2689 return gaim_parse_clientauto_ch2(od, who, reason, cookie);
2690 } else if (chan == 0x0004) { /* ICQ message */
2691 guint32 state = 0;
2692 char *msg = NULL;
2693 if (reason == 0x0003) {
2694 state = va_arg(ap, guint32);
2695 msg = va_arg(ap, char *);
2696 }
2697 return gaim_parse_clientauto_ch4(od, who, reason, state, msg);
2698 }
2699
2700 va_end(ap);
2701
2702 return 1;
2703 }
2704
2705 static int gaim_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2706 va_list ap;
2707 guint16 reason;
2708 char *m;
2709
2710 va_start(ap, fr);
2711 reason = (guint16) va_arg(ap, unsigned int);
2712 va_end(ap);
2713
2714 gaim_debug_error("oscar",
2715 "snac threw error (reason 0x%04hx: %s)\n", reason,
2716 (reason < msgerrreasonlen) ? msgerrreason[reason] : "unknown");
2717
2718 m = g_strdup_printf(_("SNAC threw error: %s\n"),
2719 reason < msgerrreasonlen ? _(msgerrreason[reason]) : _("Unknown error"));
2720 gaim_notify_error(od->gc, NULL, m, NULL);
2721 g_free(m);
2722
2723 return 1;
2724 }
2725
2726 static int gaim_parse_msgerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2727 GaimConnection *gc = od->gc;
2728 #ifdef TODOFT
2729 OscarData *od = gc->proto_data;
2730 GaimXfer *xfer;
2731 #endif
2732 va_list ap;
2733 guint16 reason;
2734 char *data, *buf;
2735
2736 va_start(ap, fr);
2737 reason = (guint16)va_arg(ap, unsigned int);
2738 data = va_arg(ap, char *);
2739 va_end(ap);
2740
2741 gaim_debug_error("oscar",
2742 "Message error with data %s and reason %hu\n",
2743 (data != NULL ? data : ""), reason);
2744
2745 if ((data == NULL) || (*data == '\0'))
2746 /* We can't do anything if data is empty */
2747 return 1;
2748
2749 #ifdef TODOFT
2750 /* If this was a file transfer request, data is a cookie */
2751 if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, data))) {
2752 gaim_xfer_cancel_remote(xfer);
2753 return 1;
2754 }
2755 #endif
2756
2757 /* Data is assumed to be the destination sn */
2758 buf = g_strdup_printf(_("Unable to send message: %s"), (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Unknown reason."));
2759 if (!gaim_conv_present_error(data, gaim_connection_get_account(gc), buf)) {
2760 g_free(buf);
2761 buf = g_strdup_printf(_("Unable to send message to %s:"), data ? data : "(unknown)");
2762 gaim_notify_error(od->gc, NULL, buf,
2763 (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason."));
2764 }
2765 g_free(buf);
2766
2767 return 1;
2768 }
2769
2770 static int gaim_parse_mtn(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2771 GaimConnection *gc = od->gc;
2772 va_list ap;
2773 guint16 type1, type2;
2774 char *sn;
2775
2776 va_start(ap, fr);
2777 type1 = (guint16) va_arg(ap, unsigned int);
2778 sn = va_arg(ap, char *);
2779 type2 = (guint16) va_arg(ap, unsigned int);
2780 va_end(ap);
2781
2782 switch (type2) {
2783 case 0x0000: { /* Text has been cleared */
2784 serv_got_typing_stopped(gc, sn);
2785 } break;
2786
2787 case 0x0001: { /* Paused typing */
2788 serv_got_typing(gc, sn, 0, GAIM_TYPED);
2789 } break;
2790
2791 case 0x0002: { /* Typing */
2792 serv_got_typing(gc, sn, 0, GAIM_TYPING);
2793 } break;
2794
2795 default: {
2796 /*
2797 * It looks like iChat sometimes sends typing notification
2798 * with type1=0x0001 and type2=0x000f. Not sure why.
2799 */
2800 gaim_debug_info("oscar", "Received unknown typing notification message from %s. Type1 is 0x%04x and type2 is 0x%04hx.\n", sn, type1, type2);
2801 } break;
2802 }
2803
2804 return 1;
2805 }
2806
2807 /*
2808 * We get this error when there was an error in the locate family. This
2809 * happens when you request info of someone who is offline.
2810 */
2811 static int gaim_parse_locerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2812 gchar *buf;
2813 va_list ap;
2814 guint16 reason;
2815 char *destn;
2816
2817 va_start(ap, fr);
2818 reason = (guint16) va_arg(ap, unsigned int);
2819 destn = va_arg(ap, char *);
2820 va_end(ap);
2821
2822 if (destn == NULL)
2823 return 1;
2824
2825 buf = g_strdup_printf(_("User information not available: %s"), (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason."));
2826 if (!gaim_conv_present_error(destn, gaim_connection_get_account((GaimConnection*)od->gc), buf)) {
2827 g_free(buf);
2828 buf = g_strdup_printf(_("User information for %s unavailable:"), destn);
2829 gaim_notify_error(od->gc, NULL, buf, (reason < msgerrreasonlen) ? _(msgerrreason[reason]) : _("Unknown reason."));
2830 }
2831 g_free(buf);
2832
2833 return 1;
2834 }
2835
2836 static int gaim_parse_userinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2837 GaimConnection *gc = od->gc;
2838 GaimAccount *account = gaim_connection_get_account(gc);
2839 GaimNotifyUserInfo *user_info;
2840 gchar *tmp = NULL, *info_utf8 = NULL, *away_utf8 = NULL;
2841 va_list ap;
2842 aim_userinfo_t *userinfo;
2843
2844 va_start(ap, fr);
2845 userinfo = va_arg(ap, aim_userinfo_t *);
2846 va_end(ap);
2847
2848 user_info = gaim_notify_user_info_new();
2849 gaim_notify_user_info_add_pair(user_info, _("Screen Name"), userinfo->sn);
2850
2851 tmp = g_strdup_printf("%d", (int)((userinfo->warnlevel/10.0) + 0.5));
2852 gaim_notify_user_info_add_pair(user_info, _("Warning Level"), tmp);
2853 g_free(tmp);
2854
2855 if (userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) {
2856 time_t t = userinfo->onlinesince - od->timeoffset;
2857 oscar_user_info_add_pair(user_info, _("Online Since"), gaim_date_format_full(localtime(&t)));
2858 }
2859
2860 if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
2861 time_t t = userinfo->membersince - od->timeoffset;
2862 oscar_user_info_add_pair(user_info, _("Member Since"), gaim_date_format_full(localtime(&t)));
2863 }
2864
2865 if (userinfo->capabilities != 0) {
2866 tmp = oscar_caps_to_string(userinfo->capabilities);
2867 oscar_user_info_add_pair(user_info, _("Capabilities"), tmp);
2868 g_free(tmp);
2869 }
2870
2871 if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) {
2872 tmp = gaim_str_seconds_to_string(userinfo->idletime*60);
2873 oscar_user_info_add_pair(user_info, _("Idle"), tmp);
2874 g_free(tmp);
2875 }
2876
2877 oscar_string_append_info(gc, user_info, NULL, userinfo);
2878
2879 /* Available message */
2880 if ((userinfo->status != NULL) && !(userinfo->flags & AIM_FLAG_AWAY))
2881 {
2882 if (userinfo->status[0] != '\0')
2883 tmp = oscar_encoding_to_utf8(userinfo->status_encoding,
2884 userinfo->status, userinfo->status_len);
2885 oscar_user_info_add_pair(user_info, _("Available Message"), tmp);
2886 g_free(tmp);
2887 }
2888
2889 /* Away message */
2890 if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
2891 tmp = oscar_encoding_extract(userinfo->away_encoding);
2892 away_utf8 = oscar_encoding_to_utf8(tmp, userinfo->away, userinfo->away_len);
2893 g_free(tmp);
2894 if (away_utf8 != NULL) {
2895 tmp = gaim_str_sub_away_formatters(away_utf8, gaim_account_get_username(account));
2896 gaim_notify_user_info_add_section_break(user_info);
2897 oscar_user_info_add_pair(user_info, NULL, tmp);
2898 g_free(tmp);
2899 g_free(away_utf8);
2900 }
2901 }
2902
2903 /* Info */
2904 if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
2905 tmp = oscar_encoding_extract(userinfo->info_encoding);
2906 info_utf8 = oscar_encoding_to_utf8(tmp, userinfo->info, userinfo->info_len);
2907 g_free(tmp);
2908 if (info_utf8 != NULL) {
2909 tmp = gaim_str_sub_away_formatters(info_utf8, gaim_account_get_username(account));
2910 gaim_notify_user_info_add_section_break(user_info);
2911 oscar_user_info_add_pair(user_info, _("Profile"), tmp);
2912 g_free(tmp);
2913 g_free(info_utf8);
2914 }
2915 }
2916
2917 gaim_notify_userinfo(gc, userinfo->sn, user_info, NULL, NULL);
2918 gaim_notify_user_info_destroy(user_info);
2919
2920 return 1;
2921 }
2922
2923 static int gaim_got_infoblock(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
2924 {
2925 GaimConnection *gc = od->gc;
2926 GaimBuddy *b;
2927 GaimPresence *presence;
2928 GaimStatus *status;
2929 gchar *message = NULL;
2930
2931 va_list ap;
2932 aim_userinfo_t *userinfo;
2933
2934 va_start(ap, fr);
2935 userinfo = va_arg(ap, aim_userinfo_t *);
2936 va_end(ap);
2937
2938 b = gaim_find_buddy(gaim_connection_get_account(gc), userinfo->sn);
2939 if (b == NULL)
2940 return 1;
2941
2942 if (!aim_sn_is_icq(userinfo->sn))
2943 {
2944 if (strcmp(gaim_buddy_get_name(b), userinfo->sn))
2945 serv_got_alias(gc, gaim_buddy_get_name(b), userinfo->sn);
2946 else
2947 serv_got_alias(gc, gaim_buddy_get_name(b), NULL);
2948 }
2949
2950 presence = gaim_buddy_get_presence(b);
2951 status = gaim_presence_get_active_status(presence);
2952
2953 if (!gaim_status_is_available(status) && gaim_status_is_online(status))
2954 {
2955 if ((userinfo->flags & AIM_FLAG_AWAY) &&
2956 (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
2957 gchar *charset = oscar_encoding_extract(userinfo->away_encoding);
2958 message = oscar_encoding_to_utf8(charset, userinfo->away, userinfo->away_len);
2959 g_free(charset);
2960 gaim_status_set_attr_string(status, "message", message);
2961 g_free(message);
2962 }
2963 else
2964 /* Set an empty message so that we know not to show "pending" */
2965 gaim_status_set_attr_string(status, "message", "");
2966
2967 gaim_blist_update_buddy_status(b, status);
2968 }
2969
2970 return 1;
2971 }
2972
2973 static int gaim_parse_motd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
2974 {
2975 char *msg;
2976 guint16 id;
2977 va_list ap;
2978
2979 va_start(ap, fr);
2980 id = (guint16) va_arg(ap, unsigned int);
2981 msg = va_arg(ap, char *);
2982 va_end(ap);
2983
2984 gaim_debug_misc("oscar",
2985 "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id);
2986 if (id < 4)
2987 gaim_notify_warning(od->gc, NULL,
2988 _("Your AIM connection may be lost."), NULL);
2989
2990 return 1;
2991 }
2992
2993 static int gaim_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
2994 va_list ap;
2995 guint16 type;
2996
2997 va_start(ap, fr);
2998 type = (guint16) va_arg(ap, unsigned int);
2999
3000 switch(type) {
3001 case 0x0002: {
3002 guint8 maxrooms;
3003 struct aim_chat_exchangeinfo *exchanges;
3004 int exchangecount, i;
3005
3006 maxrooms = (guint8) va_arg(ap, unsigned int);
3007 exchangecount = va_arg(ap, int);
3008 exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
3009
3010 gaim_debug_misc("oscar", "chat info: Chat Rights:\n");
3011 gaim_debug_misc("oscar",
3012 "chat info: \tMax Concurrent Rooms: %hhd\n", maxrooms);
3013 gaim_debug_misc("oscar",
3014 "chat info: \tExchange List: (%d total)\n", exchangecount);
3015 for (i = 0; i < exchangecount; i++)
3016 gaim_debug_misc("oscar",
3017 "chat info: \t\t%hu %s\n",
3018 exchanges[i].number, exchanges[i].name ? exchanges[i].name : "");
3019 while (od->create_rooms) {
3020 struct create_room *cr = od->create_rooms->data;
3021 gaim_debug_info("oscar",
3022 "creating room %s\n", cr->name);
3023 aim_chatnav_createroom(od, conn, cr->name, cr->exchange);
3024 g_free(cr->name);
3025 od->create_rooms = g_slist_remove(od->create_rooms, cr);
3026 g_free(cr);
3027 }
3028 }
3029 break;
3030 case 0x0008: {
3031 char *fqcn, *name, *ck;
3032 guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
3033 guint8 createperms;
3034 guint32 createtime;
3035
3036 fqcn = va_arg(ap, char *);
3037 instance = (guint16)va_arg(ap, unsigned int);
3038 exchange = (guint16)va_arg(ap, unsigned int);
3039 flags = (guint16)va_arg(ap, unsigned int);
3040 createtime = va_arg(ap, guint32);
3041 maxmsglen = (guint16)va_arg(ap, unsigned int);
3042 maxoccupancy = (guint16)va_arg(ap, unsigned int);
3043 createperms = (guint8)va_arg(ap, unsigned int);
3044 unknown = (guint16)va_arg(ap, unsigned int);
3045 name = va_arg(ap, char *);
3046 ck = va_arg(ap, char *);
3047
3048 gaim_debug_misc("oscar",
3049 "created room: %s %hu %hu %hu %u %hu %hu %hhu %hu %s %s\n",
3050 fqcn, exchange, instance, flags, createtime,
3051 maxmsglen, maxoccupancy, createperms, unknown,
3052 name, ck);
3053 aim_chat_join(od, exchange, ck, instance);
3054 }
3055 break;
3056 default:
3057 gaim_debug_warning("oscar",
3058 "chatnav info: unknown type (%04hx)\n", type);
3059 break;
3060 }
3061
3062 va_end(ap);
3063
3064 return 1;
3065 }
3066
3067 static int gaim_conv_chat_join(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3068 va_list ap;
3069 int count, i;
3070 aim_userinfo_t *info;
3071 GaimConnection *gc = od->gc;
3072
3073 struct chat_connection *c = NULL;
3074
3075 va_start(ap, fr);
3076 count = va_arg(ap, int);
3077 info = va_arg(ap, aim_userinfo_t *);
3078 va_end(ap);
3079
3080 c = find_oscar_chat_by_conn(gc, conn);
3081 if (!c)
3082 return 1;
3083
3084 for (i = 0; i < count; i++)
3085 gaim_conv_chat_add_user(GAIM_CONV_CHAT(c->conv), info[i].sn, NULL, GAIM_CBFLAGS_NONE, TRUE);
3086
3087 return 1;
3088 }
3089
3090 static int gaim_conv_chat_leave(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3091 va_list ap;
3092 int count, i;
3093 aim_userinfo_t *info;
3094 GaimConnection *gc = od->gc;
3095
3096 struct chat_connection *c = NULL;
3097
3098 va_start(ap, fr);
3099 count = va_arg(ap, int);
3100 info = va_arg(ap, aim_userinfo_t *);
3101 va_end(ap);
3102
3103 c = find_oscar_chat_by_conn(gc, conn);
3104 if (!c)
3105 return 1;
3106
3107 for (i = 0; i < count; i++)
3108 gaim_conv_chat_remove_user(GAIM_CONV_CHAT(c->conv), info[i].sn, NULL);
3109
3110 return 1;
3111 }
3112
3113 static int gaim_conv_chat_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3114 va_list ap;
3115 aim_userinfo_t *userinfo;
3116 struct aim_chat_roominfo *roominfo;
3117 char *roomname;
3118 int usercount;
3119 char *roomdesc;
3120 guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
3121 guint32 creationtime;
3122 GaimConnection *gc = od->gc;
3123 struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
3124
3125 if (!ccon)
3126 return 1;
3127
3128 va_start(ap, fr);
3129 roominfo = va_arg(ap, struct aim_chat_roominfo *);
3130 roomname = va_arg(ap, char *);
3131 usercount= va_arg(ap, int);
3132 userinfo = va_arg(ap, aim_userinfo_t *);
3133 roomdesc = va_arg(ap, char *);
3134 unknown_c9 = (guint16)va_arg(ap, unsigned int);
3135 creationtime = va_arg(ap, guint32);
3136 maxmsglen = (guint16)va_arg(ap, unsigned int);
3137 unknown_d2 = (guint16)va_arg(ap, unsigned int);
3138 unknown_d5 = (guint16)va_arg(ap, unsigned int);
3139 maxvisiblemsglen = (guint16)va_arg(ap, unsigned int);
3140 va_end(ap);
3141
3142 gaim_debug_misc("oscar",
3143 "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n",
3144 maxmsglen, maxvisiblemsglen);
3145
3146 ccon->maxlen = maxmsglen;
3147 ccon->maxvis = maxvisiblemsglen;
3148
3149 return 1;
3150 }
3151
3152 static int gaim_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3153 GaimConnection *gc = od->gc;
3154 struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
3155 gchar *utf8;
3156 va_list ap;
3157 aim_userinfo_t *info;
3158 int len;
3159 char *msg;
3160 char *charset;
3161
3162 if (!ccon)
3163 return 1;
3164
3165 va_start(ap, fr);
3166 info = va_arg(ap, aim_userinfo_t *);
3167 len = va_arg(ap, int);
3168 msg = va_arg(ap, char *);
3169 charset = va_arg(ap, char *);
3170 va_end(ap);
3171
3172 utf8 = oscar_encoding_to_utf8(charset, msg, len);
3173 if (utf8 == NULL)
3174 /* The conversion failed! */
3175 utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]"));
3176 serv_got_chat_in(gc, ccon->id, info->sn, 0, utf8, time((time_t)NULL));
3177 g_free(utf8);
3178
3179 return 1;
3180 }
3181
3182 static int gaim_email_parseupdate(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3183 va_list ap;
3184 GaimConnection *gc = od->gc;
3185 struct aim_emailinfo *emailinfo;
3186 int havenewmail;
3187 char *alertitle, *alerturl;
3188
3189 va_start(ap, fr);
3190 emailinfo = va_arg(ap, struct aim_emailinfo *);
3191 havenewmail = va_arg(ap, int);
3192 alertitle = va_arg(ap, char *);
3193 alerturl = va_arg(ap, char *);
3194 va_end(ap);
3195
3196 if ((emailinfo != NULL) && gaim_account_get_check_mail(gc->account)) {
3197 gchar *to = g_strdup_printf("%s%s%s", gaim_account_get_username(gaim_connection_get_account(gc)),
3198 emailinfo->domain ? "@" : "",
3199 emailinfo->domain ? emailinfo->domain : "");
3200 if (emailinfo->unread && havenewmail)
3201 gaim_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL, (const char **)&to, (const char **)&emailinfo->url, NULL, NULL);
3202 g_free(to);
3203 }
3204
3205 if (alertitle)
3206 gaim_debug_misc("oscar", "Got an alert '%s' %s\n", alertitle, alerturl ? alerturl : "");
3207
3208 return 1;
3209 }
3210
3211 static int gaim_icon_error(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3212 GaimConnection *gc = od->gc;
3213 char *sn;
3214
3215 sn = od->requesticon->data;
3216 gaim_debug_misc("oscar", "removing %s from hash table\n", sn);
3217 od->requesticon = g_slist_remove(od->requesticon, sn);
3218 g_free(sn);
3219
3220 if (od->icontimer == 0)
3221 od->icontimer = gaim_timeout_add(500, gaim_icon_timerfunc, gc);
3222
3223 return 1;
3224 }
3225
3226 static int gaim_icon_parseicon(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3227 GaimConnection *gc = od->gc;
3228 GSList *cur;
3229 va_list ap;
3230 char *sn;
3231 guint8 iconcsumtype, *iconcsum, *icon;
3232 guint16 iconcsumlen, iconlen;
3233
3234 va_start(ap, fr);
3235 sn = va_arg(ap, char *);
3236 iconcsumtype = va_arg(ap, int);
3237 iconcsum = va_arg(ap, guint8 *);
3238 iconcsumlen = va_arg(ap, int);
3239 icon = va_arg(ap, guint8 *);
3240 iconlen = va_arg(ap, int);
3241 va_end(ap);
3242
3243 /*
3244 * Some AIM clients will send a blank GIF image with iconlen 90 when
3245 * no icon is set. Ignore these.
3246 */
3247 if ((iconlen > 0) && (iconlen != 90)) {
3248 char *b16;
3249 GaimBuddy *b;
3250 gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc),
3251 sn, icon, iconlen);
3252 b16 = gaim_base16_encode(iconcsum, iconcsumlen);
3253 b = gaim_find_buddy(gc->account, sn);
3254 if ((b16 != NULL) && (b != NULL)) {
3255 gaim_blist_node_set_string((GaimBlistNode*)b, "icon_checksum", b16);
3256 g_free(b16);
3257 }
3258 }
3259
3260 cur = od->requesticon;
3261 while (cur) {
3262 char *cursn = cur->data;
3263 if (!aim_sncmp(cursn, sn)) {
3264 od->requesticon = g_slist_remove(od->requesticon, cursn);
3265 g_free(cursn);
3266 cur = od->requesticon;
3267 } else
3268 cur = cur->next;
3269 }
3270
3271 if (od->icontimer == 0)
3272 od->icontimer = gaim_timeout_add(250, gaim_icon_timerfunc, gc);
3273
3274 return 1;
3275 }
3276
3277 static gboolean gaim_icon_timerfunc(gpointer data) {
3278 GaimConnection *gc = data;
3279 OscarData *od = gc->proto_data;
3280 aim_userinfo_t *userinfo;
3281 FlapConnection *conn;
3282
3283 od->icontimer = 0;
3284
3285 conn = flap_connection_getbytype(od, SNAC_FAMILY_BART);
3286 if (!conn) {
3287 if (!od->iconconnecting) {
3288 aim_srv_requestnew(od, SNAC_FAMILY_BART);
3289 od->iconconnecting = TRUE;
3290 }
3291 return FALSE;
3292 }
3293
3294 if (od->set_icon) {
3295 struct stat st;
3296 char *iconfile = gaim_buddy_icons_get_full_path(gaim_account_get_buddy_icon(gaim_connection_get_account(gc)));
3297 if (iconfile == NULL) {
3298 aim_ssi_delicon(od);
3299 } else if (!g_stat(iconfile, &st)) {
3300 guchar *buf = g_malloc(st.st_size);
3301 FILE *file = g_fopen(iconfile, "rb");
3302 if (file) {
3303 /* XXX - Use g_file_get_contents()? */
3304 fread(buf, 1, st.st_size, file);
3305 fclose(file);
3306 gaim_debug_info("oscar",
3307 "Uploading icon to icon server\n");
3308 aim_bart_upload(od, buf, st.st_size);
3309 } else
3310 gaim_debug_error("oscar",
3311 "Can't open buddy icon file!\n");
3312 g_free(buf);
3313 } else {
3314 gaim_debug_error("oscar",
3315 "Can't stat buddy icon file!\n");
3316 }
3317 g_free(iconfile);
3318 od->set_icon = FALSE;
3319 }
3320
3321 if (!od->requesticon) {
3322 gaim_debug_misc("oscar",
3323 "no more icons to request\n");
3324 return FALSE;
3325 }
3326
3327 userinfo = aim_locate_finduserinfo(od, (char *)od->requesticon->data);
3328 if ((userinfo != NULL) && (userinfo->iconcsumlen > 0)) {
3329 aim_bart_request(od, od->requesticon->data, userinfo->iconcsumtype, userinfo->iconcsum, userinfo->iconcsumlen);
3330 return FALSE;
3331 } else {
3332 gchar *sn = od->requesticon->data;
3333 od->requesticon = g_slist_remove(od->requesticon, sn);
3334 g_free(sn);
3335 }
3336
3337 od->icontimer = gaim_timeout_add(100, gaim_icon_timerfunc, gc);
3338
3339 return FALSE;
3340 }
3341
3342 /*
3343 * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
3344 */
3345 static int gaim_parse_msgack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3346 va_list ap;
3347 guint16 type;
3348 char *sn;
3349
3350 va_start(ap, fr);
3351 type = (guint16) va_arg(ap, unsigned int);
3352 sn = va_arg(ap, char *);
3353 va_end(ap);
3354
3355 gaim_debug_info("oscar", "Sent message to %s.\n", sn);
3356
3357 return 1;
3358 }
3359
3360 static int gaim_parse_ratechange(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3361 static const char *codes[5] = {
3362 "invalid",
3363 "change",
3364 "warning",
3365 "limit",
3366 "limit cleared",
3367 };
3368 va_list ap;
3369 guint16 code, rateclass;
3370 guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
3371
3372 va_start(ap, fr);
3373 code = (guint16)va_arg(ap, unsigned int);
3374 rateclass= (guint16)va_arg(ap, unsigned int);
3375 windowsize = va_arg(ap, guint32);
3376 clear = va_arg(ap, guint32);
3377 alert = va_arg(ap, guint32);
3378 limit = va_arg(ap, guint32);
3379 disconnect = va_arg(ap, guint32);
3380 currentavg = va_arg(ap, guint32);
3381 maxavg = va_arg(ap, guint32);
3382 va_end(ap);
3383
3384 gaim_debug_misc("oscar",
3385 "rate %s (param ID 0x%04hx): curavg = %u, maxavg = %u, alert at %u, "
3386 "clear warning at %u, limit at %u, disconnect at %u (window size = %u)\n",
3387 (code < 5) ? codes[code] : codes[0],
3388 rateclass,
3389 currentavg, maxavg,
3390 alert, clear,
3391 limit, disconnect,
3392 windowsize);
3393
3394 if (code == AIM_RATE_CODE_LIMIT)
3395 {
3396 gaim_notify_error(od->gc, NULL, _("Rate limiting error."),
3397 _("The last action you attempted could not be "
3398 "performed because you are over the rate limit. "
3399 "Please wait 10 seconds and try again."));
3400 }
3401
3402 return 1;
3403 }
3404
3405 static int gaim_parse_evilnotify(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3406 #ifdef CRAZY_WARNING
3407 va_list ap;
3408 guint16 newevil;
3409 aim_userinfo_t *userinfo;
3410
3411 va_start(ap, fr);
3412 newevil = (guint16) va_arg(ap, unsigned int);
3413 userinfo = va_arg(ap, aim_userinfo_t *);
3414 va_end(ap);
3415
3416 gaim_prpl_got_account_warning_level(account, (userinfo && userinfo->sn) ? userinfo->sn : NULL, (newevil/10.0) + 0.5);
3417 #endif
3418
3419 return 1;
3420 }
3421
3422 static int gaim_selfinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3423 int warning_level;
3424 va_list ap;
3425 aim_userinfo_t *info;
3426
3427 va_start(ap, fr);
3428 info = va_arg(ap, aim_userinfo_t *);
3429 va_end(ap);
3430
3431 /*
3432 * What's with the + 0.5?
3433 * The 0.5 is basically poor-man's rounding. Normally
3434 * casting "13.7" to an int will truncate to "13," but
3435 * with 13.7 + 0.5 = 14.2, which becomes "14" when
3436 * truncated.
3437 */
3438 warning_level = info->warnlevel/10.0 + 0.5;
3439
3440 #ifdef CRAZY_WARNING
3441 gaim_presence_set_warning_level(presence, warning_level);
3442 #endif
3443
3444 return 1;
3445 }
3446
3447 static int gaim_connerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3448 GaimConnection *gc = od->gc;
3449 va_list ap;
3450 guint16 code;
3451 char *msg;
3452
3453 va_start(ap, fr);
3454 code = (guint16)va_arg(ap, int);
3455 msg = va_arg(ap, char *);
3456 va_end(ap);
3457
3458 gaim_debug_info("oscar", "Disconnected. Code is 0x%04x and msg is %s\n",
3459 code, (msg != NULL ? msg : ""));
3460
3461 g_return_val_if_fail(fr != NULL, 1);
3462 g_return_val_if_fail(conn != NULL, 1);
3463
3464 if (conn->type == SNAC_FAMILY_LOCATE) {
3465 if (code == 0x0001) {
3466 gc->wants_to_die = TRUE;
3467 gaim_connection_error(gc, _("You have signed on from another location."));
3468 } else {
3469 gaim_connection_error(gc, _("You have been signed off for an unknown reason."));
3470 }
3471 od->killme = TRUE;
3472 } else if (conn->type == SNAC_FAMILY_CHAT) {
3473 struct chat_connection *cc;
3474 GaimConversation *conv;
3475
3476 cc = find_oscar_chat_by_conn(gc, conn);
3477 conv = gaim_find_chat(gc, cc->id);
3478
3479 if (conv != NULL)
3480 {
3481 gchar *buf;
3482 buf = g_strdup_printf(_("You have been disconnected from chat "
3483 "room %s."), cc->name);
3484 gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_ERROR, time(NULL));
3485 g_free(buf);
3486 }
3487 oscar_chat_kill(gc, cc);
3488 }
3489
3490 return 1;
3491 }
3492
3493 static int gaim_icbm_param_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3494 struct aim_icbmparameters *params;
3495 va_list ap;
3496
3497 va_start(ap, fr);
3498 params = va_arg(ap, struct aim_icbmparameters *);
3499 va_end(ap);
3500
3501 /* XXX - evidently this crashes on solaris. i have no clue why
3502 gaim_debug_misc("oscar", "ICBM Parameters: maxchannel = %hu, default flags = 0x%08lx, max msg len = %hu, "
3503 "max sender evil = %f, max receiver evil = %f, min msg interval = %u\n",
3504 params->maxchan, params->flags, params->maxmsglen,
3505 ((float)params->maxsenderwarn)/10.0, ((float)params->maxrecverwarn)/10.0,
3506 params->minmsginterval);
3507 */
3508
3509 /* Maybe senderwarn and recverwarn should be user preferences... */
3510 params->flags = 0x0000000b;
3511 params->maxmsglen = 8000;
3512 params->minmsginterval = 0;
3513
3514 aim_im_setparams(od, params);
3515
3516 return 1;
3517 }
3518
3519 static int gaim_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
3520 {
3521 GaimConnection *gc = od->gc;
3522 GaimAccount *account = gaim_connection_get_account(gc);
3523 va_list ap;
3524 guint16 maxsiglen;
3525
3526 va_start(ap, fr);
3527 maxsiglen = (guint16) va_arg(ap, int);
3528 va_end(ap);
3529
3530 gaim_debug_misc("oscar",
3531 "locate rights: max sig len = %d\n", maxsiglen);
3532
3533 od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen;
3534
3535 aim_locate_setcaps(od, gaim_caps);
3536 oscar_set_info_and_status(account, TRUE, account->user_info, TRUE,
3537 gaim_account_get_active_status(account));
3538
3539 return 1;
3540 }
3541
3542 static int gaim_parse_buddyrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3543 va_list ap;
3544 guint16 maxbuddies, maxwatchers;
3545
3546 va_start(ap, fr);
3547 maxbuddies = (guint16) va_arg(ap, unsigned int);
3548 maxwatchers = (guint16) va_arg(ap, unsigned int);
3549 va_end(ap);
3550
3551 gaim_debug_misc("oscar",
3552 "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers);
3553
3554 od->rights.maxbuddies = (guint)maxbuddies;
3555 od->rights.maxwatchers = (guint)maxwatchers;
3556
3557 return 1;
3558 }
3559
3560 static int gaim_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3561 GaimConnection *gc;
3562 GaimAccount *account;
3563 GaimStatus *status;
3564 const char *message;
3565 char *tmp;
3566 va_list ap;
3567 guint16 maxpermits, maxdenies;
3568
3569 gc = od->gc;
3570 od = (OscarData *)gc->proto_data;
3571 account = gaim_connection_get_account(gc);
3572
3573 va_start(ap, fr);
3574 maxpermits = (guint16) va_arg(ap, unsigned int);
3575 maxdenies = (guint16) va_arg(ap, unsigned int);
3576 va_end(ap);
3577
3578 gaim_debug_misc("oscar",
3579 "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies);
3580
3581 od->rights.maxpermits = (guint)maxpermits;
3582 od->rights.maxdenies = (guint)maxdenies;
3583
3584 gaim_connection_set_state(gc, GAIM_CONNECTED);
3585
3586 gaim_debug_info("oscar", "buddy list loaded\n");
3587
3588 aim_clientready(od, conn);
3589
3590 if (gaim_account_get_user_info(account) != NULL)
3591 serv_set_info(gc, gaim_account_get_user_info(account));
3592
3593 /* Set our available message based on the current status */
3594 status = gaim_account_get_active_status(account);
3595 if (gaim_status_is_available(status))
3596 message = gaim_status_get_attr_string(status, "message");
3597 else
3598 message = NULL;
3599 tmp = gaim_markup_strip_html(message);
3600 aim_srv_setstatusmsg(od, tmp);
3601 g_free(tmp);
3602
3603 aim_srv_setidle(od, 0);
3604
3605 if (od->icq) {
3606 aim_icq_reqofflinemsgs(od);
3607 oscar_set_extendedstatus(gc);
3608 aim_icq_setsecurity(od,
3609 gaim_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION),
3610 gaim_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE));
3611 }
3612
3613 aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
3614
3615 /*
3616 * The "if" statement here is a pathetic attempt to not attempt to
3617 * connect to the alerts servce (aka email notification) if this
3618 * screen name does not support it. I think mail notification
3619 * works for @mac.com accounts but does not work for the newer
3620 * @anythingelse.com accounts. If that's true then this change
3621 * breaks mail notification for @mac.com accounts, but it gets rid
3622 * of an annoying error at signon for @anythingelse.com accounts.
3623 */
3624 if ((od->authinfo->email != NULL) && ((strchr(gc->account->username, '@') == NULL)))
3625 aim_srv_requestnew(od, SNAC_FAMILY_ALERT);
3626
3627 return 1;
3628 }
3629
3630 static int gaim_offlinemsg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3631 va_list ap;
3632 struct aim_icq_offlinemsg *msg;
3633 struct aim_incomingim_ch4_args args;
3634 time_t t;
3635
3636 va_start(ap, fr);
3637 msg = va_arg(ap, struct aim_icq_offlinemsg *);
3638 va_end(ap);
3639
3640 gaim_debug_info("oscar",
3641 "Received offline message. Converting to channel 4 ICBM...\n");
3642 args.uin = msg->sender;
3643 args.type = msg->type;
3644 args.flags = msg->flags;
3645 args.msglen = msg->msglen;
3646 args.msg = msg->msg;
3647 t = gaim_time_build(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
3648 incomingim_chan4(od, conn, NULL, &args, t);
3649
3650 return 1;
3651 }
3652
3653 static int gaim_offlinemsgdone(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
3654 {
3655 aim_icq_ackofflinemsgs(od);
3656 return 1;
3657 }
3658
3659 static int gaim_icqinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
3660 {
3661 GaimConnection *gc;
3662 GaimAccount *account;
3663 GaimBuddy *buddy;
3664 struct buddyinfo *bi;
3665 gchar who[16];
3666 GaimNotifyUserInfo *user_info;
3667 GString *tmp;
3668 gchar *utf8;
3669 gchar *buf;
3670 const gchar *alias;
3671 va_list ap;
3672 struct aim_icq_info *info;
3673
3674 gc = od->gc;
3675 account = gaim_connection_get_account(gc);
3676
3677 va_start(ap, fr);
3678 info = va_arg(ap, struct aim_icq_info *);
3679 va_end(ap);
3680
3681 if (!info->uin)
3682 return 0;
3683
3684 user_info = gaim_notify_user_info_new();
3685
3686 g_snprintf(who, sizeof(who), "%u", info->uin);
3687 buddy = gaim_find_buddy(gaim_connection_get_account(gc), who);
3688 if (buddy != NULL)
3689 bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(buddy->account, buddy->name));
3690 else
3691 bi = NULL;
3692
3693 gaim_notify_user_info_add_pair(user_info, _("UIN"), who);
3694 oscar_user_info_convert_and_add(account, user_info, _("Nick"), info->nick);
3695 if ((bi != NULL) && (bi->ipaddr != 0)) {
3696 char *tstr = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
3697 (bi->ipaddr & 0xff000000) >> 24,
3698 (bi->ipaddr & 0x00ff0000) >> 16,
3699 (bi->ipaddr & 0x0000ff00) >> 8,
3700 (bi->ipaddr & 0x000000ff));
3701 gaim_notify_user_info_add_pair(user_info, _("IP Address"), tstr);
3702 g_free(tstr);
3703 }
3704 oscar_user_info_convert_and_add(account, user_info, _("First Name"), info->first);
3705 oscar_user_info_convert_and_add(account, user_info, _("Last Name"), info->last);
3706 if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->email))) {
3707 buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
3708 gaim_notify_user_info_add_pair(user_info, _("E-Mail Address"), buf);
3709 g_free(buf);
3710 g_free(utf8);
3711 }
3712 if (info->numaddresses && info->email2) {
3713 int i;
3714 for (i = 0; i < info->numaddresses; i++) {
3715 if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(gc->account, info->email2[i]))) {
3716 buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
3717 gaim_notify_user_info_add_pair(user_info, _("E-Mail Address"), buf);
3718 g_free(buf);
3719 g_free(utf8);
3720 }
3721 }
3722 }
3723 oscar_user_info_convert_and_add(account, user_info, _("Mobile Phone"), info->mobile);
3724
3725 if (info->gender != 0)
3726 gaim_notify_user_info_add_pair(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
3727
3728 if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
3729 /* Initialize the struct properly or strftime() will crash
3730 * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
3731 time_t t = time(NULL);
3732 struct tm *tm = localtime(&t);
3733
3734 tm->tm_mday = (int)info->birthday;
3735 tm->tm_mon = (int)info->birthmonth - 1;
3736 tm->tm_year = (int)info->birthyear - 1900;
3737
3738 /* To be 100% sure that the fields are re-normalized.
3739 * If you're sure strftime() ALWAYS does this EVERYWHERE,
3740 * feel free to remove it. --rlaager */
3741 mktime(tm);
3742
3743 oscar_user_info_convert_and_add(account, user_info, _("Birthday"), gaim_date_format_short(tm));
3744 }
3745 if ((info->age > 0) && (info->age < 255)) {
3746 char age[5];
3747 snprintf(age, sizeof(age), "%hhd", info->age);
3748 gaim_notify_user_info_add_pair(user_info,
3749 _("Age"), age);
3750 }
3751 if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->personalwebpage))) {
3752 buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
3753 gaim_notify_user_info_add_pair(user_info, _("Personal Web Page"), buf);
3754 g_free(buf);
3755 g_free(utf8);
3756 }
3757
3758 oscar_user_info_convert_and_add(account, user_info, _("Additional Information"), info->info);
3759 gaim_notify_user_info_add_section_break(user_info);
3760
3761 if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
3762 tmp = g_string_sized_new(100);
3763 oscar_string_convert_and_append(account, tmp, "\n<br>", _("Address"), info->homeaddr);
3764 oscar_string_convert_and_append(account, tmp, "\n<br>", _("City"), info->homecity);
3765 oscar_string_convert_and_append(account, tmp, "\n<br>", _("State"), info->homestate);
3766 oscar_string_convert_and_append(account, tmp, "\n<br>", _("Zip Code"), info->homezip);
3767
3768 gaim_notify_user_info_add_pair(user_info, _("Home Address"), tmp->str);
3769 gaim_notify_user_info_add_section_break(user_info);
3770
3771 g_string_free(tmp, TRUE);
3772 }
3773 if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
3774 tmp = g_string_sized_new(100);
3775
3776 oscar_string_convert_and_append(account, tmp, "\n<br>", _("Address"), info->workaddr);
3777 oscar_string_convert_and_append(account, tmp, "\n<br>", _("City"), info->workcity);
3778 oscar_string_convert_and_append(account, tmp, "\n<br>", _("State"), info->workstate);
3779 oscar_string_convert_and_append(account, tmp, "\n<br>", _("Zip Code"), info->workzip);
3780
3781 gaim_notify_user_info_add_pair(user_info, _("Work Address"), tmp->str);
3782 gaim_notify_user_info_add_section_break(user_info);
3783
3784 g_string_free(tmp, TRUE);
3785 }
3786 if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
3787 tmp = g_string_sized_new(100);
3788
3789 oscar_string_convert_and_append(account, tmp, "\n<br>", _("Company"), info->workcompany);
3790 oscar_string_convert_and_append(account, tmp, "\n<br>", _("Division"), info->workdivision);
3791 oscar_string_convert_and_append(account, tmp, "\n<br>", _("Position"), info->workposition);
3792 if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->workwebpage))) {
3793 g_string_append_printf(tmp, "\n<br><b>%s:</b> <a href=\"%s\">%s</a>", _("Web Page"), utf8, utf8);
3794 g_free(utf8);
3795 }
3796 gaim_notify_user_info_add_pair(user_info, _("Work Information"), tmp->str);
3797 g_string_free(tmp, TRUE);
3798 }
3799
3800 if (buddy != NULL)
3801 alias = gaim_buddy_get_alias(buddy);
3802 else
3803 alias = who;
3804 gaim_notify_userinfo(gc, who, user_info, NULL, NULL);
3805 gaim_notify_user_info_destroy(user_info);
3806
3807 return 1;
3808 }
3809
3810 static int gaim_icqalias(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
3811 {
3812 GaimConnection *gc = od->gc;
3813 GaimAccount *account = gaim_connection_get_account(gc);
3814 gchar who[16], *utf8;
3815 GaimBuddy *b;
3816 va_list ap;
3817 struct aim_icq_info *info;
3818
3819 va_start(ap, fr);
3820 info = va_arg(ap, struct aim_icq_info *);
3821 va_end(ap);
3822
3823 if (info->uin && info->nick && info->nick[0] && (utf8 = oscar_utf8_try_convert(account, info->nick))) {
3824 g_snprintf(who, sizeof(who), "%u", info->uin);
3825 serv_got_alias(gc, who, utf8);
3826 if ((b = gaim_find_buddy(gc->account, who))) {
3827 gaim_blist_node_set_string((GaimBlistNode*)b, "servernick", utf8);
3828 }
3829 g_free(utf8);
3830 }
3831
3832 return 1;
3833 }
3834
3835 static int gaim_popup(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
3836 {
3837 GaimConnection *gc = od->gc;
3838 gchar *text;
3839 va_list ap;
3840 char *msg, *url;
3841 guint16 wid, hei, delay;
3842
3843 va_start(ap, fr);
3844 msg = va_arg(ap, char *);
3845 url = va_arg(ap, char *);
3846 wid = (guint16) va_arg(ap, int);
3847 hei = (guint16) va_arg(ap, int);
3848 delay = (guint16) va_arg(ap, int);
3849 va_end(ap);
3850
3851 text = g_strdup_printf("%s<br><a href=\"%s\">%s</a>", msg, url, url);
3852 gaim_notify_formatted(gc, NULL, _("Pop-Up Message"), NULL, text, NULL, NULL);
3853 g_free(text);
3854
3855 return 1;
3856 }
3857
3858 static void oscar_searchresults_add_buddy_cb(GaimConnection *gc, GList *row, void *user_data)
3859 {
3860 gaim_blist_request_add_buddy(gaim_connection_get_account(gc),
3861 g_list_nth_data(row, 0), NULL, NULL);
3862 }
3863
3864 static int gaim_parse_searchreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
3865 {
3866 GaimConnection *gc = od->gc;
3867 GaimNotifySearchResults *results;
3868 GaimNotifySearchColumn *column;
3869 gchar *secondary;
3870 int i, num;
3871 va_list ap;
3872 char *email, *SNs;
3873
3874 va_start(ap, fr);
3875 email = va_arg(ap, char *);
3876 num = va_arg(ap, int);
3877 SNs = va_arg(ap, char *);
3878 va_end(ap);
3879
3880 results = gaim_notify_searchresults_new();
3881
3882 if (results == NULL) {
3883 gaim_debug_error("oscar", "gaim_parse_searchreply: "
3884 "Unable to display the search results.\n");
3885 gaim_notify_error(gc, NULL,
3886 _("Unable to display the search results."),
3887 NULL);
3888 return 1;
3889 }
3890
3891 secondary = g_strdup_printf(
3892 ngettext("The following screen name is associated with %s",
3893 "The following screen names are associated with %s",
3894 num),
3895 email);
3896
3897 column = gaim_notify_searchresults_column_new(_("Screen name"));
3898 gaim_notify_searchresults_column_add(results, column);
3899
3900 for (i = 0; i < num; i++) {
3901 GList *row = NULL;
3902 row = g_list_append(row, g_strdup(&SNs[i * (MAXSNLEN + 1)]));
3903 gaim_notify_searchresults_row_add(results, row);
3904 }
3905 gaim_notify_searchresults_button_add(results, GAIM_NOTIFY_BUTTON_ADD,
3906 oscar_searchresults_add_buddy_cb);
3907 gaim_notify_searchresults(gc, NULL, NULL, secondary, results, NULL, NULL);
3908
3909 g_free(secondary);
3910
3911 return 1;
3912 }
3913
3914 static int gaim_parse_searcherror(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3915 va_list ap;
3916 char *email;
3917 char *buf;
3918
3919 va_start(ap, fr);
3920 email = va_arg(ap, char *);
3921 va_end(ap);
3922
3923 buf = g_strdup_printf(_("No results found for e-mail address %s"), email);
3924 gaim_notify_error(od->gc, NULL, buf, NULL);
3925 g_free(buf);
3926
3927 return 1;
3928 }
3929
3930 static int gaim_account_confirm(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3931 GaimConnection *gc = od->gc;
3932 guint16 status;
3933 va_list ap;
3934 char msg[256];
3935
3936 va_start(ap, fr);
3937 status = (guint16) va_arg(ap, unsigned int); /* status code of confirmation request */
3938 va_end(ap);
3939
3940 gaim_debug_info("oscar",
3941 "account confirmation returned status 0x%04x (%s)\n", status,
3942 status ? "unknown" : "e-mail sent");
3943 if (!status) {
3944 g_snprintf(msg, sizeof(msg), _("You should receive an e-mail asking to confirm %s."),
3945 gaim_account_get_username(gaim_connection_get_account(gc)));
3946 gaim_notify_info(gc, NULL, _("Account Confirmation Requested"), msg);
3947 }
3948
3949 return 1;
3950 }
3951
3952 static int gaim_info_change(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
3953 GaimConnection *gc = od->gc;
3954 va_list ap;
3955 guint16 perms, err;
3956 char *url, *sn, *email;
3957 int change;
3958
3959 va_start(ap, fr);
3960 change = va_arg(ap, int);
3961 perms = (guint16) va_arg(ap, unsigned int);
3962 err = (guint16) va_arg(ap, unsigned int);
3963 url = va_arg(ap, char *);
3964 sn = va_arg(ap, char *);
3965 email = va_arg(ap, char *);
3966 va_end(ap);
3967
3968 gaim_debug_misc("oscar",
3969 "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, sn=%s, email=%s\n",
3970 change ? "change" : "request", perms, err,
3971 (url != NULL) ? url : "(null)",
3972 (sn != NULL) ? sn : "(null)",
3973 (email != NULL) ? email : "(null)");
3974
3975 if ((err > 0) && (url != NULL)) {
3976 char *dialog_msg;
3977 char *dialog_top = g_strdup_printf(_("Error Changing Account Info"));
3978 switch (err) {
3979 case 0x0001: {
3980 dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name differs from the original."), err);
3981 } break;
3982 case 0x0006: {
3983 dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because it is invalid."), err);
3984 } break;
3985 case 0x000b: {
3986 dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format screen name because the requested screen name is too long."), err);
3987 } break;
3988 case 0x001d: {
3989 dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change e-mail address because there is already a request pending for this screen name."), err);
3990 } break;
3991 case 0x0021: {
3992 dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change e-mail address because the given address has too many screen names associated with it."), err);
3993 } break;
3994 case 0x0023: {
3995 dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change e-mail address because the given address is invalid."), err);
3996 } break;
3997 default: {
3998 dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err);
3999 } break;
4000 }
4001 gaim_notify_error(gc, NULL, dialog_top, dialog_msg);
4002 g_free(dialog_top);
4003 g_free(dialog_msg);
4004 return 1;
4005 }
4006
4007 if (sn != NULL) {
4008 char *dialog_msg = g_strdup_printf(_("Your screen name is currently formatted as follows:\n%s"), sn);
4009 gaim_notify_info(gc, NULL, _("Account Info"), dialog_msg);
4010 g_free(dialog_msg);
4011 }
4012
4013 if (email != NULL) {
4014 char *dialog_msg = g_strdup_printf(_("The e-mail address for %s is %s"),
4015 gaim_account_get_username(gaim_connection_get_account(gc)), email);
4016 gaim_notify_info(gc, NULL, _("Account Info"), dialog_msg);
4017 g_free(dialog_msg);
4018 }
4019
4020 return 1;
4021 }
4022
4023 void
4024 oscar_keepalive(GaimConnection *gc)
4025 {
4026 OscarData *od;
4027 FlapConnection *conn;
4028
4029 od = (OscarData *)gc->proto_data;
4030 conn = flap_connection_getbytype(od, SNAC_FAMILY_LOCATE);
4031 if (conn != NULL)
4032 flap_connection_send_keepalive(od, conn);
4033 }
4034
4035 unsigned int
4036 oscar_send_typing(GaimConnection *gc, const char *name, GaimTypingState state)
4037 {
4038 OscarData *od;
4039 PeerConnection *conn;
4040
4041 od = (OscarData *)gc->proto_data;
4042 conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
4043
4044 if ((conn != NULL) && (conn->ready))
4045 {
4046 peer_odc_send_typing(conn, state);
4047 }
4048 else {
4049 /* Don't send if this turkey is in our deny list */
4050 GSList *list;
4051 for (list=gc->account->deny; (list && aim_sncmp(name, list->data)); list=list->next);
4052 if (!list) {
4053 struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(gc->account, name));
4054 if (bi && bi->typingnot) {
4055 if (state == GAIM_TYPING)
4056 aim_im_sendmtn(od, 0x0001, name, 0x0002);
4057 else if (state == GAIM_TYPED)
4058 aim_im_sendmtn(od, 0x0001, name, 0x0001);
4059 else
4060 aim_im_sendmtn(od, 0x0001, name, 0x0000);
4061 }
4062 }
4063 }
4064 return 0;
4065 }
4066
4067 /* TODO: Move this into odc.c! */
4068 static void
4069 gaim_odc_send_im(PeerConnection *conn, const char *message, GaimMessageFlags imflags)
4070 {
4071 GString *msg;
4072 GString *data;
4073 gchar *tmp;
4074 int tmplen;
4075 guint16 charset, charsubset;
4076 GData *attribs;
4077 const char *start, *end, *last;
4078 int oscar_id = 0;
4079
4080 msg = g_string_new("<HTML><BODY>");
4081 data = g_string_new("<BINARY>");
4082 last = message;
4083
4084 /* for each valid IMG tag... */
4085 while (last && *last && gaim_markup_find_tag("img", last, &start, &end, &attribs))
4086 {
4087 GaimStoredImage *image = NULL;
4088 const char *id;
4089
4090 if (start - last) {
4091 g_string_append_len(msg, last, start - last);
4092 }
4093
4094 id = g_datalist_get_data(&attribs, "id");
4095
4096 /* ... if it refers to a valid gaim image ... */
4097 if (id && (image = gaim_imgstore_get(atoi(id)))) {
4098 /* ... append the message from start to the tag ... */
4099 unsigned long size = gaim_imgstore_get_size(image);
4100 const char *filename = gaim_imgstore_get_filename(image);
4101 gpointer imgdata = gaim_imgstore_get_data(image);
4102
4103 oscar_id++;
4104
4105 /* ... insert a new img tag with the oscar id ... */
4106 if (filename)
4107 g_string_append_printf(msg,
4108 "<IMG SRC=\"%s\" ID=\"%d\" DATASIZE=\"%lu\">",
4109 filename, oscar_id, size);
4110 else
4111 g_string_append_printf(msg,
4112 "<IMG ID=\"%d\" DATASIZE=\"%lu\">",
4113 oscar_id, size);
4114
4115 /* ... and append the data to the binary section ... */
4116 g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%lu\">",
4117 oscar_id, size);
4118 g_string_append_len(data, imgdata, size);
4119 g_string_append(data, "</DATA>");
4120 }
4121 /* If the tag is invalid, skip it, thus no else here */
4122
4123 g_datalist_clear(&attribs);
4124
4125 /* continue from the end of the tag */
4126 last = end + 1;
4127 }
4128
4129 /* append any remaining message data */
4130 if (last && *last)
4131 g_string_append(msg, last);
4132
4133 g_string_append(msg, "</BODY></HTML>");
4134
4135 /* Convert the message to a good encoding */
4136 gaim_plugin_oscar_convert_to_best_encoding(conn->od->gc,
4137 conn->sn, msg->str, &tmp, &tmplen, &charset, &charsubset);
4138 g_string_free(msg, TRUE);
4139 msg = g_string_new_len(tmp, tmplen);
4140
4141 /* Append any binary data that we may have */
4142 if (oscar_id) {
4143 msg = g_string_append_len(msg, data->str, data->len);
4144 msg = g_string_append(msg, "</BINARY>");
4145 }
4146 g_string_free(data, TRUE);
4147
4148 peer_odc_send_im(conn, msg->str, msg->len, charset,
4149 imflags & GAIM_MESSAGE_AUTO_RESP);
4150 g_string_free(msg, TRUE);
4151 }
4152
4153 int
4154 oscar_send_im(GaimConnection *gc, const char *name, const char *message, GaimMessageFlags imflags)
4155 {
4156 OscarData *od;
4157 GaimAccount *account;
4158 PeerConnection *conn;
4159 int ret;
4160 char *iconfile;
4161 char *tmp1, *tmp2;
4162
4163 od = (OscarData *)gc->proto_data;
4164 account = gaim_connection_get_account(gc);
4165 ret = 0;
4166 iconfile = gaim_buddy_icons_get_full_path(gaim_account_get_buddy_icon(account));
4167
4168 if (imflags & GAIM_MESSAGE_AUTO_RESP)
4169 tmp1 = gaim_str_sub_away_formatters(message, name);
4170 else
4171 tmp1 = g_strdup(message);
4172
4173 conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
4174 if ((conn != NULL) && (conn->ready))
4175 {
4176 /* If we're directly connected, send a direct IM */
4177 gaim_odc_send_im(conn, tmp1, imflags);
4178 } else {
4179 struct buddyinfo *bi;
4180 struct aim_sendimext_args args;
4181 struct stat st;
4182 gsize len;
4183 GaimConversation *conv;
4184
4185 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, account);
4186
4187 if (strstr(tmp1, "<IMG "))
4188 gaim_conversation_write(conv, "",
4189 _("Your IM Image was not sent. "
4190 "You must be Direct Connected to send IM Images."),
4191 GAIM_MESSAGE_ERROR, time(NULL));
4192
4193 bi = g_hash_table_lookup(od->buddyinfo, gaim_normalize(account, name));
4194 if (!bi) {
4195 bi = g_new0(struct buddyinfo, 1);
4196 g_hash_table_insert(od->buddyinfo, g_strdup(gaim_normalize(account, name)), bi);
4197 }
4198
4199 args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES;
4200 if (od->icq) {
4201 /* We have to present different "features" (whose meaning
4202 is unclear and are merely a result of protocol inspection)
4203 to offline ICQ buddies. Otherwise, the official
4204 ICQ client doesn't treat those messages as being "ANSI-
4205 encoded" (and instead, assumes them to be UTF-8).
4206 For more details, see SF issue 1179452.
4207 */
4208 GaimBuddy *buddy = gaim_find_buddy(gc->account, name);
4209 if (buddy && GAIM_BUDDY_IS_ONLINE(buddy)) {
4210 args.features = features_icq;
4211 args.featureslen = sizeof(features_icq);
4212 } else {
4213 args.features = features_icq_offline;
4214 args.featureslen = sizeof(features_icq_offline);
4215 }
4216 args.flags |= AIM_IMFLAGS_OFFLINE;
4217 } else {
4218 args.features = features_aim;
4219 args.featureslen = sizeof(features_aim);
4220
4221 if (imflags & GAIM_MESSAGE_AUTO_RESP)
4222 args.flags |= AIM_IMFLAGS_AWAY;
4223 }
4224
4225 if (bi->ico_need) {
4226 gaim_debug_info("oscar",
4227 "Sending buddy icon request with message\n");
4228 args.flags |= AIM_IMFLAGS_BUDDYREQ;
4229 bi->ico_need = FALSE;
4230 }
4231
4232 if (iconfile && !g_stat(iconfile, &st)) {
4233 FILE *file = g_fopen(iconfile, "rb");
4234 if (file) {
4235 guchar *buf = g_malloc(st.st_size);
4236 /* TODO: Use g_file_get_contents()? */
4237 fread(buf, 1, st.st_size, file);
4238 fclose(file);
4239
4240 args.iconlen = st.st_size;
4241 args.iconsum = aimutil_iconsum(buf, st.st_size);
4242 args.iconstamp = st.st_mtime;
4243
4244 if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time)) {
4245 bi->ico_informed = FALSE;
4246 bi->ico_sent = FALSE;
4247 }
4248
4249 /*
4250 * TODO:
4251 * For some reason sending our icon to people only works
4252 * when we're the ones who initiated the conversation. If
4253 * the other person sends the first IM then they never get
4254 * the icon. We should fix that.
4255 */
4256 if (!bi->ico_informed) {
4257 gaim_debug_info("oscar",
4258 "Claiming to have a buddy icon\n");
4259 args.flags |= AIM_IMFLAGS_HASICON;
4260 bi->ico_me_len = args.iconlen;
4261 bi->ico_me_csum = args.iconsum;
4262 bi->ico_me_time = args.iconstamp;
4263 bi->ico_informed = TRUE;
4264 }
4265
4266 g_free(buf);
4267 }
4268 }
4269 g_free(iconfile);
4270
4271 args.destsn = name;
4272
4273 /*
4274 * If we're IMing an SMS user or an ICQ user from an ICQ account, then strip HTML.
4275 */
4276 if (aim_sn_is_sms(name)) {
4277 /* Messaging an SMS (mobile) user */
4278 tmp2 = gaim_unescape_html(tmp1);
4279 } else if (aim_sn_is_icq(gaim_account_get_username(account))) {
4280 if (aim_sn_is_icq(name))
4281 /* From ICQ to ICQ */
4282 tmp2 = gaim_unescape_html(tmp1);
4283 else
4284 /* From ICQ to AIM */
4285 tmp2 = g_strdup(tmp1);
4286 } else {
4287 /* From AIM to AIM and AIM to ICQ */
4288 tmp2 = g_strdup(tmp1);
4289 }
4290 g_free(tmp1);
4291 tmp1 = tmp2;
4292 len = strlen(tmp1);
4293
4294 gaim_plugin_oscar_convert_to_best_encoding(gc, name, tmp1, (char **)&args.msg, &args.msglen, &args.charset, &args.charsubset);
4295 gaim_debug_info("oscar", "Sending IM, charset=0x%04hx, charsubset=0x%04hx, length=%d\n",
4296 args.charset, args.charsubset, args.msglen);
4297 ret = aim_im_sendch1_ext(od, &args);
4298 g_free((char *)args.msg);
4299 }
4300
4301 g_free(tmp1);
4302
4303 if (ret >= 0)
4304 return 1;
4305
4306 return ret;
4307 }
4308
4309 /*
4310 * As of 26 June 2006, ICQ users can request AIM info from
4311 * everyone, and can request ICQ info from ICQ users, and
4312 * AIM users can only request AIM info.
4313 */
4314 void oscar_get_info(GaimConnection *gc, const char *name) {
4315 OscarData *od = (OscarData *)gc->proto_data;
4316
4317 if (od->icq && aim_sn_is_icq(name))
4318 aim_icq_getallinfo(od, name);
4319 else
4320 aim_locate_getinfoshort(od, name, 0x00000003);
4321 }
4322
4323 #if 0
4324 static void oscar_set_dir(GaimConnection *gc, const char *first, const char *middle, const char *last,
4325 const char *maiden, const char *city, const char *state, const char *country, int web) {
4326 /* XXX - some of these things are wrong, but i'm lazy */
4327 OscarData *od = (OscarData *)gc->proto_data;
4328 aim_locate_setdirinfo(od, first, middle, last,
4329 maiden, NULL, NULL, city, state, NULL, 0, web);
4330 }
4331 #endif
4332
4333 void oscar_set_idle(GaimConnection *gc, int time) {
4334 OscarData *od = (OscarData *)gc->proto_data;
4335 aim_srv_setidle(od, time);
4336 }
4337
4338 static
4339 gchar *gaim_prpl_oscar_convert_to_infotext(const gchar *str, gsize *ret_len, char **encoding)
4340 {
4341 int charset = 0;
4342 char *encoded = NULL;
4343
4344 charset = oscar_charset_check(str);
4345 if (charset == AIM_CHARSET_UNICODE) {
4346 encoded = g_convert(str, strlen(str), "UCS-2BE", "UTF-8", NULL, ret_len, NULL);
4347 *encoding = "unicode-2-0";
4348 } else if (charset == AIM_CHARSET_CUSTOM) {
4349 encoded = g_convert(str, strlen(str), "ISO-8859-1", "UTF-8", NULL, ret_len, NULL);
4350 *encoding = "iso-8859-1";
4351 } else {
4352 encoded = g_strdup(str);
4353 *ret_len = strlen(str);
4354 *encoding = "us-ascii";
4355 }
4356
4357 return encoded;
4358 }
4359
4360 void
4361 oscar_set_info(GaimConnection *gc, const char *rawinfo)
4362 {
4363 GaimAccount *account;
4364 GaimStatus *status;
4365
4366 account = gaim_connection_get_account(gc);
4367 status = gaim_account_get_active_status(account);
4368 oscar_set_info_and_status(account, TRUE, rawinfo, FALSE, status);
4369 }
4370
4371 static void
4372 oscar_set_extendedstatus(GaimConnection *gc)
4373 {
4374 OscarData *od;
4375 GaimAccount *account;
4376 GaimStatus *status;
4377 const gchar *status_id;
4378 guint32 data = 0x00000000;
4379
4380 od = gc->proto_data;
4381 account = gaim_connection_get_account(gc);
4382 status = gaim_account_get_active_status(account);
4383 status_id = gaim_status_get_id(status);
4384
4385 data |= AIM_ICQ_STATE_HIDEIP;
4386 if (gaim_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE))
4387 data |= AIM_ICQ_STATE_WEBAWARE;
4388
4389 if (!strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) || !strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE))
4390 data |= AIM_ICQ_STATE_NORMAL;
4391 else if (!strcmp(status_id, OSCAR_STATUS_ID_AWAY))
4392 data |= AIM_ICQ_STATE_AWAY;
4393 else if (!strcmp(status_id, OSCAR_STATUS_ID_DND))
4394 data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
4395 else if (!strcmp(status_id, OSCAR_STATUS_ID_NA))
4396 data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
4397 else if (!strcmp(status_id, OSCAR_STATUS_ID_OCCUPIED))
4398 data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
4399 else if (!strcmp(status_id, OSCAR_STATUS_ID_FREE4CHAT))
4400 data |= AIM_ICQ_STATE_CHAT;
4401 else if (!strcmp(status_id, OSCAR_STATUS_ID_INVISIBLE))
4402 data |= AIM_ICQ_STATE_INVISIBLE;
4403 else if (!strcmp(status_id, OSCAR_STATUS_ID_CUSTOM))
4404 data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
4405
4406 aim_srv_setextstatus(od, data);
4407 }
4408
4409 static void
4410 oscar_set_info_and_status(GaimAccount *account, gboolean setinfo, const char *rawinfo,
4411 gboolean setstatus, GaimStatus *status)
4412 {
4413 GaimConnection *gc = gaim_account_get_connection(account);
4414 OscarData *od = gc->proto_data;
4415 GaimPresence *presence;
4416 GaimStatusType *status_type;
4417 GaimStatusPrimitive primitive;
4418 gboolean invisible;
4419
4420 char *htmlinfo;
4421 char *info_encoding = NULL;
4422 char *info = NULL;
4423 gsize infolen = 0;
4424
4425 const char *htmlaway;
4426 char *away_encoding = NULL;
4427 char *away = NULL;
4428 gsize awaylen = 0;
4429
4430 status_type = gaim_status_get_type(status);
4431 primitive = gaim_status_type_get_primitive(status_type);
4432 presence = gaim_account_get_presence(account);
4433 invisible = gaim_presence_is_status_primitive_active(presence, GAIM_STATUS_INVISIBLE);
4434
4435 if (!setinfo)
4436 {
4437 /* Do nothing! */
4438 }
4439 else if (od->rights.maxsiglen == 0)
4440 {
4441 gaim_notify_warning(gc, NULL, _("Unable to set AIM profile."),
4442 _("You have probably requested to set your "
4443 "profile before the login procedure completed. "
4444 "Your profile remains unset; try setting it "
4445 "again when you are fully connected."));
4446 }
4447 else if (rawinfo != NULL)
4448 {
4449 htmlinfo = gaim_strdup_withhtml(rawinfo);
4450 info = gaim_prpl_oscar_convert_to_infotext(htmlinfo, &infolen, &info_encoding);
4451 g_free(htmlinfo);
4452
4453 if (infolen > od->rights.maxsiglen)
4454 {
4455 gchar *errstr;
4456 errstr = g_strdup_printf(ngettext("The maximum profile length of %d byte "
4457 "has been exceeded. Gaim has truncated it for you.",
4458 "The maximum profile length of %d bytes "
4459 "has been exceeded. Gaim has truncated it for you.",
4460 od->rights.maxsiglen), od->rights.maxsiglen);
4461 gaim_notify_warning(gc, NULL, _("Profile too long."), errstr);
4462 g_free(errstr);
4463 }
4464 }
4465
4466 if (!setstatus)
4467 {
4468 /* Do nothing! */
4469 }
4470 else if (primitive == GAIM_STATUS_AVAILABLE)
4471 {
4472 const char *status_html;
4473 char *status_text = NULL;
4474
4475 status_html = gaim_status_get_attr_string(status, "message");
4476 if (status_html != NULL)
4477 {
4478 status_text = gaim_markup_strip_html(status_html);
4479 /* If the status_text is longer than 60 character then truncate it */
4480 if (strlen(status_text) > 60)
4481 {
4482 char *tmp = g_utf8_find_prev_char(status_text, &status_text[58]);
4483 strcpy(tmp, "...");
4484 }
4485 }
4486
4487 aim_srv_setstatusmsg(od, status_text);
4488 g_free(status_text);
4489
4490 /* This is needed for us to un-set any previous away message. */
4491 away = g_strdup("");
4492 }
4493 else if ((primitive == GAIM_STATUS_AWAY) ||
4494 (primitive == GAIM_STATUS_EXTENDED_AWAY))
4495 {
4496 htmlaway = gaim_status_get_attr_string(status, "message");
4497 if ((htmlaway == NULL) || (*htmlaway == '\0'))
4498 htmlaway = _("Away");
4499 away = gaim_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding);
4500
4501 if (awaylen > od->rights.maxawaymsglen)
4502 {
4503 gchar *errstr;
4504
4505 errstr = g_strdup_printf(ngettext("The maximum away message length of %d byte "
4506 "has been exceeded. Gaim has truncated it for you.",
4507 "The maximum away message length of %d bytes "
4508 "has been exceeded. Gaim has truncated it for you.",
4509 od->rights.maxawaymsglen), od->rights.maxawaymsglen);
4510 gaim_notify_warning(gc, NULL, _("Away message too long."), errstr);
4511 g_free(errstr);
4512 }
4513 }
4514
4515 if (setstatus)
4516 oscar_set_extendedstatus(gc);
4517
4518 aim_locate_setprofile(od, info_encoding, info, MIN(infolen, od->rights.maxsiglen),
4519 away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
4520 g_free(info);
4521 g_free(away);
4522 }
4523
4524 static void
4525 oscar_set_status_icq(GaimAccount *account, GaimStatus *status)
4526 {
4527 GaimConnection *gc = gaim_account_get_connection(account);
4528 OscarData *od = NULL;
4529
4530 if (gc)
4531 od = (OscarData *)gc->proto_data;
4532 if (!od)
4533 return;
4534
4535 if (gaim_status_type_get_primitive(gaim_status_get_type(status)) == GAIM_STATUS_INVISIBLE)
4536 account->perm_deny = GAIM_PRIVACY_ALLOW_USERS;
4537 else
4538 account->perm_deny = GAIM_PRIVACY_DENY_USERS;
4539
4540 if ((od->ssi.received_data) && (aim_ssi_getpermdeny(od->ssi.local) != account->perm_deny))
4541 aim_ssi_setpermdeny(od, account->perm_deny, 0xffffffff);
4542
4543 oscar_set_extendedstatus(gc);
4544 }
4545
4546 void
4547 oscar_set_status(GaimAccount *account, GaimStatus *status)
4548 {
4549 gaim_debug_info("oscar", "Set status to %s\n", gaim_status_get_name(status));
4550
4551 if (!gaim_status_is_active(status))
4552 return;
4553
4554 if (!gaim_account_is_connected(account))
4555 return;
4556
4557 /* Set the AIM-style away message for both AIM and ICQ accounts */
4558 oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
4559
4560 /* Set the ICQ status for ICQ accounts only */
4561 if (aim_sn_is_icq(gaim_account_get_username(account)))
4562 oscar_set_status_icq(account, status);
4563 }
4564
4565 #ifdef CRAZY_WARN
4566 void
4567 oscar_warn(GaimConnection *gc, const char *name, gboolean anonymous) {
4568 OscarData *od = (OscarData *)gc->proto_data;
4569 aim_im_warn(od, od->conn, name, anonymous ? AIM_WARN_ANON : 0);
4570 }
4571 #endif
4572
4573 void
4574 oscar_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group) {
4575 OscarData *od = (OscarData *)gc->proto_data;
4576
4577 if (!aim_snvalid(buddy->name)) {
4578 gchar *buf;
4579 buf = g_strdup_printf(_("Could not add the buddy %s because the screen name is invalid. Screen names must either start with a letter and contain only letters, numbers and spaces, or contain only numbers."), buddy->name);
4580 if (!gaim_conv_present_error(buddy->name, gaim_connection_get_account(gc), buf))
4581 gaim_notify_error(gc, NULL, _("Unable To Add"), buf);
4582 g_free(buf);
4583
4584 /* Remove from local list */
4585 gaim_blist_remove_buddy(buddy);
4586
4587 return;
4588 }
4589
4590 if ((od->ssi.received_data) && !(aim_ssi_itemlist_finditem(od->ssi.local, group->name, buddy->name, AIM_SSI_TYPE_BUDDY))) {
4591 gaim_debug_info("oscar",
4592 "ssi: adding buddy %s to group %s\n", buddy->name, group->name);
4593 aim_ssi_addbuddy(od, buddy->name, group->name, gaim_buddy_get_alias_only(buddy), NULL, NULL, 0);
4594 }
4595
4596 /* XXX - Should this be done from AIM accounts, as well? */
4597 if (od->icq)
4598 aim_icq_getalias(od, buddy->name);
4599 }
4600
4601 void oscar_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group) {
4602 OscarData *od = (OscarData *)gc->proto_data;
4603
4604 if (od->ssi.received_data) {
4605 gaim_debug_info("oscar",
4606 "ssi: deleting buddy %s from group %s\n", buddy->name, group->name);
4607 aim_ssi_delbuddy(od, buddy->name, group->name);
4608 }
4609 }
4610
4611 void oscar_move_buddy(GaimConnection *gc, const char *name, const char *old_group, const char *new_group) {
4612 OscarData *od = (OscarData *)gc->proto_data;
4613 if (od->ssi.received_data && strcmp(old_group, new_group)) {
4614 gaim_debug_info("oscar",
4615 "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group);
4616 aim_ssi_movebuddy(od, old_group, new_group, name);
4617 }
4618 }
4619
4620 void oscar_alias_buddy(GaimConnection *gc, const char *name, const char *alias) {
4621 OscarData *od = (OscarData *)gc->proto_data;
4622 if (od->ssi.received_data) {
4623 char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
4624 if (gname) {
4625 gaim_debug_info("oscar",
4626 "ssi: changing the alias for buddy %s to %s\n", name, alias ? alias : "(none)");
4627 aim_ssi_aliasbuddy(od, gname, name, alias);
4628 }
4629 }
4630 }
4631
4632 /*
4633 * FYI, the OSCAR SSI code removes empty groups automatically.
4634 */
4635 void oscar_rename_group(GaimConnection *gc, const char *old_name, GaimGroup *group, GList *moved_buddies) {
4636 OscarData *od = (OscarData *)gc->proto_data;
4637
4638 if (od->ssi.received_data) {
4639 if (aim_ssi_itemlist_finditem(od->ssi.local, group->name, NULL, AIM_SSI_TYPE_GROUP)) {
4640 GList *cur, *groups = NULL;
4641 GaimAccount *account = gaim_connection_get_account(gc);
4642
4643 /* Make a list of what the groups each buddy is in */
4644 for (cur = moved_buddies; cur != NULL; cur = cur->next) {
4645 GaimBlistNode *node = cur->data;
4646 /* node is GaimBuddy, parent is a GaimContact.
4647 * We must go two levels up to get the Group */
4648 groups = g_list_append(groups,
4649 node->parent->parent);
4650 }
4651
4652 gaim_account_remove_buddies(account, moved_buddies, groups);
4653 gaim_account_add_buddies(account, moved_buddies);
4654 g_list_free(groups);
4655 gaim_debug_info("oscar",
4656 "ssi: moved all buddies from group %s to %s\n", old_name, group->name);
4657 } else {
4658 aim_ssi_rename_group(od, old_name, group->name);
4659 gaim_debug_info("oscar",
4660 "ssi: renamed group %s to %s\n", old_name, group->name);
4661 }
4662 }
4663 }
4664
4665 static gboolean gaim_ssi_rerequestdata(gpointer data) {
4666 OscarData *od = data;
4667
4668 aim_ssi_reqdata(od);
4669
4670 return TRUE;
4671 }
4672
4673 static int gaim_ssi_parseerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
4674 GaimConnection *gc = od->gc;
4675 va_list ap;
4676 guint16 reason;
4677
4678 va_start(ap, fr);
4679 reason = (guint16)va_arg(ap, unsigned int);
4680 va_end(ap);
4681
4682 gaim_debug_error("oscar", "ssi: SNAC error %hu\n", reason);
4683
4684 if (reason == 0x0005) {
4685 gaim_notify_error(gc, NULL, _("Unable To Retrieve Buddy List"),
4686 _("Gaim was temporarily unable to retrieve your buddy list from the AIM servers. Your buddy list is not lost, and will probably become available in a few hours."));
4687 if (od->getblisttimer > 0)
4688 gaim_timeout_remove(od->getblisttimer);
4689 od->getblisttimer = gaim_timeout_add(30000, gaim_ssi_rerequestdata, od);
4690 }
4691
4692 oscar_set_extendedstatus(gc);
4693
4694 /* Activate SSI */
4695 /* Sending the enable causes other people to be able to see you, and you to see them */
4696 /* Make sure your privacy setting/invisibility is set how you want it before this! */
4697 gaim_debug_info("oscar", "ssi: activating server-stored buddy list\n");
4698 aim_ssi_enable(od);
4699
4700 return 1;
4701 }
4702
4703 static int gaim_ssi_parserights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
4704 int i;
4705 va_list ap;
4706 int numtypes;
4707 guint16 *maxitems;
4708
4709 va_start(ap, fr);
4710 numtypes = va_arg(ap, int);
4711 maxitems = va_arg(ap, guint16 *);
4712 va_end(ap);
4713
4714 gaim_debug_misc("oscar", "ssi rights:");
4715
4716 for (i=0; i<numtypes; i++)
4717 gaim_debug_misc(NULL, " max type 0x%04x=%hd,",
4718 i, maxitems[i]);
4719
4720 gaim_debug_misc(NULL, "\n");
4721
4722 if (numtypes >= 0)
4723 od->rights.maxbuddies = maxitems[0];
4724 if (numtypes >= 1)
4725 od->rights.maxgroups = maxitems[1];
4726 if (numtypes >= 2)
4727 od->rights.maxpermits = maxitems[2];
4728 if (numtypes >= 3)
4729 od->rights.maxdenies = maxitems[3];
4730
4731 return 1;
4732 }
4733
4734 static int gaim_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
4735 {
4736 GaimConnection *gc;
4737 GaimAccount *account;
4738 GaimGroup *g;
4739 GaimBuddy *b;
4740 struct aim_ssi_item *curitem;
4741 guint32 tmp;
4742 const char *icon_path, *cached_icon_path;
4743 va_list ap;
4744 guint16 fmtver, numitems;
4745 guint32 timestamp;
4746
4747 gc = od->gc;
4748 od = gc->proto_data;
4749 account = gaim_connection_get_account(gc);
4750
4751 va_start(ap, fr);
4752 fmtver = (guint16)va_arg(ap, int);
4753 numitems = (guint16)va_arg(ap, int);
4754 timestamp = va_arg(ap, guint32);
4755 va_end(ap);
4756
4757 /* Don't attempt to re-request our buddy list later */
4758 if (od->getblisttimer != 0)
4759 gaim_timeout_remove(od->getblisttimer);
4760 od->getblisttimer = 0;
4761
4762 gaim_debug_info("oscar",
4763 "ssi: syncing local list and server list\n");
4764
4765 if ((timestamp == 0) || (numitems == 0)) {
4766 gaim_debug_info("oscar", "Got AIM SSI with a 0 timestamp or 0 numitems--not syncing. This probably means your buddy list is empty.", NULL);
4767 return 1;
4768 }
4769
4770 /* Clean the buddy list */
4771 aim_ssi_cleanlist(od);
4772
4773 { /* If not in server list then prune from local list */
4774 GaimBlistNode *gnode, *cnode, *bnode;
4775 GaimBuddyList *blist;
4776 GSList *cur, *next;
4777
4778 /* Buddies */
4779 cur = NULL;
4780 if ((blist = gaim_get_blist()) != NULL) {
4781 for (gnode = blist->root; gnode; gnode = gnode->next) {
4782 if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
4783 continue;
4784 g = (GaimGroup *)gnode;
4785 for (cnode = gnode->child; cnode; cnode = cnode->next) {
4786 if(!GAIM_BLIST_NODE_IS_CONTACT(cnode))
4787 continue;
4788 for (bnode = cnode->child; bnode; bnode = bnode->next) {
4789 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
4790 continue;
4791 b = (GaimBuddy *)bnode;
4792 if (b->account == gc->account) {
4793 if (aim_ssi_itemlist_exists(od->ssi.local, b->name)) {
4794 /* If the buddy is an ICQ user then load his nickname */
4795 const char *servernick = gaim_blist_node_get_string((GaimBlistNode*)b, "servernick");
4796 char *alias;
4797 if (servernick)
4798 serv_got_alias(gc, b->name, servernick);
4799
4800 /* Store local alias on server */
4801 alias = aim_ssi_getalias(od->ssi.local, g->name, b->name);
4802 if (!alias && b->alias && strlen(b->alias))
4803 aim_ssi_aliasbuddy(od, g->name, b->name, b->alias);
4804 g_free(alias);
4805 } else {
4806 gaim_debug_info("oscar",
4807 "ssi: removing buddy %s from local list\n", b->name);
4808 /* We can't actually remove now because it will screw up our looping */
4809 cur = g_slist_prepend(cur, b);
4810 }
4811 }
4812 }
4813 }
4814 }
4815 }
4816
4817 while (cur != NULL) {
4818 b = cur->data;
4819 cur = g_slist_remove(cur, b);
4820 gaim_blist_remove_buddy(b);
4821 }
4822
4823 /* Permit list */
4824 if (gc->account->permit) {
4825 next = gc->account->permit;
4826 while (next != NULL) {
4827 cur = next;
4828 next = next->next;
4829 if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) {
4830 gaim_debug_info("oscar",
4831 "ssi: removing permit %s from local list\n", (const char *)cur->data);
4832 gaim_privacy_permit_remove(account, cur->data, TRUE);
4833 }
4834 }
4835 }
4836
4837 /* Deny list */
4838 if (gc->account->deny) {
4839 next = gc->account->deny;
4840 while (next != NULL) {
4841 cur = next;
4842 next = next->next;
4843 if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_DENY)) {
4844 gaim_debug_info("oscar",
4845 "ssi: removing deny %s from local list\n", (const char *)cur->data);
4846 gaim_privacy_deny_remove(account, cur->data, TRUE);
4847 }
4848 }
4849 }
4850 /* Presence settings (idle time visibility) */
4851 if ((tmp = aim_ssi_getpresence(od->ssi.local)) != 0xFFFFFFFF)
4852 if (!(tmp & 0x400))
4853 aim_ssi_setpresence(od, tmp | 0x400);
4854 } /* end pruning buddies from local list */
4855
4856 /* Add from server list to local list */
4857 for (curitem=od->ssi.local; curitem; curitem=curitem->next) {
4858 if ((curitem->name == NULL) || (g_utf8_validate(curitem->name, -1, NULL)))
4859 switch (curitem->type) {
4860 case 0x0000: { /* Buddy */
4861 if (curitem->name) {
4862 char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, curitem->name);
4863 char *gname_utf8 = gname ? oscar_utf8_try_convert(gc->account, gname) : NULL;
4864 char *alias = aim_ssi_getalias(od->ssi.local, gname, curitem->name);
4865 char *alias_utf8;
4866
4867 if (alias != NULL)
4868 {
4869 if (g_utf8_validate(alias, -1, NULL))
4870 alias_utf8 = g_strdup(alias);
4871 else
4872 alias_utf8 = oscar_utf8_try_convert(account, alias);
4873 }
4874 else
4875 alias_utf8 = NULL;
4876
4877 b = gaim_find_buddy(gc->account, curitem->name);
4878 /* Should gname be freed here? -- elb */
4879 /* Not with the current code, but that might be cleaner -- med */
4880 g_free(alias);
4881 if (b) {
4882 /* Get server stored alias */
4883 if (alias_utf8) {
4884 g_free(b->alias);
4885 b->alias = g_strdup(alias_utf8);
4886 }
4887 } else {
4888 b = gaim_buddy_new(gc->account, curitem->name, alias_utf8);
4889
4890 if (!(g = gaim_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) {
4891 g = gaim_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
4892 gaim_blist_add_group(g, NULL);
4893 }
4894
4895 gaim_debug_info("oscar",
4896 "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname_utf8 ? gname_utf8 : _("Orphans"));
4897 gaim_blist_add_buddy(b, NULL, g, NULL);
4898 }
4899 if (!aim_sncmp(curitem->name, account->username)) {
4900 char *comment = aim_ssi_getcomment(od->ssi.local, gname, curitem->name);
4901 if (comment != NULL)
4902 {
4903 gaim_check_comment(od, comment);
4904 g_free(comment);
4905 }
4906 }
4907 g_free(gname_utf8);
4908 g_free(alias_utf8);
4909 }
4910 } break;
4911
4912 case 0x0001: { /* Group */
4913 /* Shouldn't add empty groups */
4914 } break;
4915
4916 case 0x0002: { /* Permit buddy */
4917 if (curitem->name) {
4918 /* if (!find_permdeny_by_name(gc->permit, curitem->name)) { AAA */
4919 GSList *list;
4920 for (list=account->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
4921 if (!list) {
4922 gaim_debug_info("oscar",
4923 "ssi: adding permit buddy %s to local list\n", curitem->name);
4924 gaim_privacy_permit_add(account, curitem->name, TRUE);
4925 }
4926 }
4927 } break;
4928
4929 case 0x0003: { /* Deny buddy */
4930 if (curitem->name) {
4931 GSList *list;
4932 for (list=account->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
4933 if (!list) {
4934 gaim_debug_info("oscar",
4935 "ssi: adding deny buddy %s to local list\n", curitem->name);
4936 gaim_privacy_deny_add(account, curitem->name, TRUE);
4937 }
4938 }
4939 } break;
4940
4941 case 0x0004: { /* Permit/deny setting */
4942 if (curitem->data) {
4943 guint8 permdeny;
4944 if ((permdeny = aim_ssi_getpermdeny(od->ssi.local)) && (permdeny != account->perm_deny)) {
4945 gaim_debug_info("oscar",
4946 "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny);
4947 account->perm_deny = permdeny;
4948 if (od->icq && account->perm_deny == GAIM_PRIVACY_ALLOW_USERS) {
4949 gaim_presence_set_status_active(account->presence, OSCAR_STATUS_ID_INVISIBLE, TRUE);
4950 }
4951 }
4952 }
4953 } break;
4954
4955 case 0x0005: { /* Presence setting */
4956 /* We don't want to change Gaim's setting because it applies to all accounts */
4957 } break;
4958 } /* End of switch on curitem->type */
4959 } /* End of for loop */
4960
4961 oscar_set_extendedstatus(gc);
4962
4963 /* Activate SSI */
4964 /* Sending the enable causes other people to be able to see you, and you to see them */
4965 /* Make sure your privacy setting/invisibility is set how you want it before this! */
4966 gaim_debug_info("oscar",
4967 "ssi: activating server-stored buddy list\n");
4968 aim_ssi_enable(od);
4969
4970 /*
4971 * Make sure our server-stored icon is updated correctly in
4972 * the event that the local user set a new icon while this
4973 * account was offline.
4974 */
4975 icon_path = gaim_account_get_buddy_icon(account);
4976 cached_icon_path = gaim_buddy_icons_get_full_path(icon_path);
4977 oscar_set_icon(gc, cached_icon_path);
4978
4979 return 1;
4980 }
4981
4982 static int gaim_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
4983 GaimConnection *gc = od->gc;
4984 va_list ap;
4985 struct aim_ssi_tmp *retval;
4986
4987 va_start(ap, fr);
4988 retval = va_arg(ap, struct aim_ssi_tmp *);
4989 va_end(ap);
4990
4991 while (retval) {
4992 gaim_debug_misc("oscar",
4993 "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack, retval->action, retval->item ? (retval->item->name ? retval->item->name : "no name") : "no item");
4994
4995 if (retval->ack != 0xffff)
4996 switch (retval->ack) {
4997 case 0x0000: { /* added successfully */
4998 } break;
4999
5000 case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */
5001 gchar *buf;
5002 buf = g_strdup_printf(_("Could not add the buddy %s because you have too many buddies in your buddy list. Please remove one and try again."), (retval->name ? retval->name : _("(no name)")));
5003 if ((retval->name != NULL) && !gaim_conv_present_error(retval->name, gaim_connection_get_account(gc), buf))
5004 gaim_notify_error(gc, NULL, _("Unable To Add"), buf);
5005 g_free(buf);
5006 }
5007
5008 case 0x000e: { /* buddy requires authorization */
5009 if ((retval->action == SNAC_SUBTYPE_FEEDBAG_ADD) && (retval->name))
5010 gaim_auth_sendrequest(gc, retval->name);
5011 } break;
5012
5013 default: { /* La la la */
5014 gchar *buf;
5015 gaim_debug_error("oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack);
5016 buf = g_strdup_printf(_("Could not add the buddy %s for an unknown reason. The most common reason for this is that you have the maximum number of allowed buddies in your buddy list."), (retval->name ? retval->name : _("(no name)")));
5017 if ((retval->name != NULL) && !gaim_conv_present_error(retval->name, gaim_connection_get_account(gc), buf))
5018 gaim_notify_error(gc, NULL, _("Unable To Add"), buf);
5019 g_free(buf);
5020 } break;
5021 }
5022
5023 retval = retval->next;
5024 }
5025
5026 return 1;
5027 }
5028
5029 static int gaim_ssi_parseadd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
5030 GaimConnection *gc = od->gc;
5031 char *gname, *gname_utf8, *alias, *alias_utf8;
5032 GaimBuddy *b;
5033 GaimGroup *g;
5034 va_list ap;
5035 guint16 type;
5036 const char *name;
5037
5038 va_start(ap, fr);
5039 type = (guint16)va_arg(ap, int);
5040 name = va_arg(ap, char *);
5041 va_end(ap);
5042
5043 if ((type != 0x0000) || (name == NULL))
5044 return 1;
5045
5046 gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
5047 gname_utf8 = gname ? oscar_utf8_try_convert(gc->account, gname) : NULL;
5048
5049 alias = aim_ssi_getalias(od->ssi.local, gname, name);
5050 if (alias != NULL)
5051 {
5052 if (g_utf8_validate(alias, -1, NULL))
5053 alias_utf8 = g_strdup(alias);
5054 else
5055 alias_utf8 = oscar_utf8_try_convert(gaim_connection_get_account(gc), alias);
5056 }
5057 else
5058 alias_utf8 = NULL;
5059
5060 b = gaim_find_buddy(gc->account, name);
5061 g_free(alias);
5062
5063 if (b) {
5064 /* Get server stored alias */
5065 if (alias_utf8) {
5066 g_free(b->alias);
5067 b->alias = g_strdup(alias_utf8);
5068 }
5069 } else {
5070 b = gaim_buddy_new(gc->account, name, alias_utf8);
5071
5072 if (!(g = gaim_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) {
5073 g = gaim_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
5074 gaim_blist_add_group(g, NULL);
5075 }
5076
5077 gaim_debug_info("oscar",
5078 "ssi: adding buddy %s to group %s to local list\n", name, gname_utf8 ? gname_utf8 : _("Orphans"));
5079 gaim_blist_add_buddy(b, NULL, g, NULL);
5080 }
5081 g_free(gname_utf8);
5082 g_free(alias_utf8);
5083
5084 return 1;
5085 }
5086
5087 static int gaim_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
5088 GaimConnection *gc = od->gc;
5089 va_list ap;
5090 char *sn, *msg;
5091 gchar *dialog_msg, *nombre;
5092 struct name_data *data;
5093 GaimBuddy *buddy;
5094
5095 va_start(ap, fr);
5096 sn = va_arg(ap, char *);
5097 msg = va_arg(ap, char *);
5098 va_end(ap);
5099
5100 gaim_debug_info("oscar",
5101 "ssi: %s has given you permission to add him to your buddy list\n", sn);
5102
5103 buddy = gaim_find_buddy(gc->account, sn);
5104 if (buddy && (gaim_buddy_get_alias_only(buddy)))
5105 nombre = g_strdup_printf("%s (%s)", sn, gaim_buddy_get_alias_only(buddy));
5106 else
5107 nombre = g_strdup(sn);
5108
5109 dialog_msg = g_strdup_printf(_("The user %s has given you permission to add you to their buddy list. Do you want to add them?"), nombre);
5110 data = g_new(struct name_data, 1);
5111 data->gc = gc;
5112 data->name = g_strdup(sn);
5113 data->nick = NULL;
5114
5115 gaim_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg,
5116 GAIM_DEFAULT_ACTION_NONE, data,
5117 G_CALLBACK(gaim_icq_buddyadd),
5118 G_CALLBACK(oscar_free_name_data));
5119
5120 g_free(dialog_msg);
5121 g_free(nombre);
5122
5123 return 1;
5124 }
5125
5126 static int gaim_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
5127 GaimConnection *gc = od->gc;
5128 va_list ap;
5129 char *sn;
5130 char *msg;
5131 GaimAccount *account = gaim_connection_get_account(gc);
5132 gchar *nombre;
5133 gchar *reason = NULL;
5134 struct name_data *data;
5135 GaimBuddy *buddy;
5136
5137 va_start(ap, fr);
5138 sn = va_arg(ap, char *);
5139 msg = va_arg(ap, char *);
5140 va_end(ap);
5141
5142 gaim_debug_info("oscar",
5143 "ssi: received authorization request from %s\n", sn);
5144
5145 buddy = gaim_find_buddy(account, sn);
5146 if (buddy && (gaim_buddy_get_alias_only(buddy)))
5147 nombre = g_strdup_printf("%s (%s)", sn, gaim_buddy_get_alias_only(buddy));
5148 else
5149 nombre = g_strdup(sn);
5150
5151 if (msg != NULL)
5152 reason = gaim_plugin_oscar_decode_im_part(account, sn, AIM_CHARSET_CUSTOM, 0x0000, msg, strlen(msg));
5153
5154 data = g_new(struct name_data, 1);
5155 data->gc = gc;
5156 data->name = g_strdup(sn);
5157 data->nick = NULL;
5158
5159 gaim_account_request_authorization(account, nombre, NULL, NULL,
5160 reason, buddy != NULL, G_CALLBACK(gaim_auth_grant),
5161 G_CALLBACK(gaim_auth_dontgrant_msgprompt), data);
5162 g_free(nombre);
5163 g_free(reason);
5164
5165 return 1;
5166 }
5167
5168 static int gaim_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
5169 GaimConnection *gc = od->gc;
5170 va_list ap;
5171 char *sn, *msg;
5172 gchar *dialog_msg, *nombre;
5173 guint8 reply;
5174 GaimBuddy *buddy;
5175
5176 va_start(ap, fr);
5177 sn = va_arg(ap, char *);
5178 reply = (guint8)va_arg(ap, int);
5179 msg = va_arg(ap, char *);
5180 va_end(ap);
5181
5182 gaim_debug_info("oscar",
5183 "ssi: received authorization reply from %s. Reply is 0x%04hhx\n", sn, reply);
5184
5185 buddy = gaim_find_buddy(gc->account, sn);
5186 if (buddy && (gaim_buddy_get_alias_only(buddy)))
5187 nombre = g_strdup_printf("%s (%s)", sn, gaim_buddy_get_alias_only(buddy));
5188 else
5189 nombre = g_strdup(sn);
5190
5191 if (reply) {
5192 /* Granted */
5193 dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre);
5194 gaim_notify_info(gc, NULL, _("Authorization Granted"), dialog_msg);
5195 } else {
5196 /* Denied */
5197 dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given."));
5198 gaim_notify_info(gc, NULL, _("Authorization Denied"), dialog_msg);
5199 }
5200 g_free(dialog_msg);
5201 g_free(nombre);
5202
5203 return 1;
5204 }
5205
5206 static int gaim_ssi_gotadded(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
5207 GaimConnection *gc = od->gc;
5208 va_list ap;
5209 char *sn;
5210 GaimBuddy *buddy;
5211
5212 va_start(ap, fr);
5213 sn = va_arg(ap, char *);
5214 va_end(ap);
5215
5216 buddy = gaim_find_buddy(gc->account, sn);
5217 gaim_debug_info("oscar", "ssi: %s added you to their buddy list\n", sn);
5218 gaim_account_notify_added(gc->account, sn, NULL, (buddy ? gaim_buddy_get_alias_only(buddy) : NULL), NULL);
5219
5220 return 1;
5221 }
5222
5223 GList *oscar_chat_info(GaimConnection *gc) {
5224 GList *m = NULL;
5225 struct proto_chat_entry *pce;
5226
5227 pce = g_new0(struct proto_chat_entry, 1);
5228 pce->label = _("_Room:");
5229 pce->identifier = "room";
5230 pce->required = TRUE;
5231 m = g_list_append(m, pce);
5232
5233 pce = g_new0(struct proto_chat_entry, 1);
5234 pce->label = _("_Exchange:");
5235 pce->identifier = "exchange";
5236 pce->required = TRUE;
5237 pce->is_int = TRUE;
5238 pce->min = 4;
5239 pce->max = 20;
5240 m = g_list_append(m, pce);
5241
5242 return m;
5243 }
5244
5245 GHashTable *oscar_chat_info_defaults(GaimConnection *gc, const char *chat_name)
5246 {
5247 GHashTable *defaults;
5248
5249 defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
5250
5251 if (chat_name != NULL)
5252 g_hash_table_insert(defaults, "room", g_strdup(chat_name));
5253
5254 return defaults;
5255 }
5256
5257 char *
5258 oscar_get_chat_name(GHashTable *data)
5259 {
5260 return g_strdup(g_hash_table_lookup(data, "room"));
5261 }
5262
5263 void
5264 oscar_join_chat(GaimConnection *gc, GHashTable *data)
5265 {
5266 OscarData *od = (OscarData *)gc->proto_data;
5267 FlapConnection *conn;
5268 char *name, *exchange;
5269
5270 name = g_hash_table_lookup(data, "room");
5271 exchange = g_hash_table_lookup(data, "exchange");
5272
5273 if ((name == NULL) || (*name == '\0')) {
5274 gaim_notify_error(gc, NULL, _("Invalid chat name specified."), NULL);
5275 return;
5276 }
5277
5278 gaim_debug_info("oscar", "Attempting to join chat room %s.\n", name);
5279
5280 if ((conn = flap_connection_getbytype(od, SNAC_FAMILY_CHATNAV)))
5281 {
5282 gaim_debug_info("oscar", "chatnav exists, creating room\n");
5283 aim_chatnav_createroom(od, conn, name, atoi(exchange));
5284 } else {
5285 /* this gets tricky */
5286 struct create_room *cr = g_new0(struct create_room, 1);
5287 gaim_debug_info("oscar", "chatnav does not exist, opening chatnav\n");
5288 cr->exchange = atoi(exchange);
5289 cr->name = g_strdup(name);
5290 od->create_rooms = g_slist_prepend(od->create_rooms, cr);
5291 aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
5292 }
5293 }
5294
5295 void
5296 oscar_chat_invite(GaimConnection *gc, int id, const char *message, const char *name)
5297 {
5298 OscarData *od = (OscarData *)gc->proto_data;
5299 struct chat_connection *ccon = find_oscar_chat(gc, id);
5300
5301 if (ccon == NULL)
5302 return;
5303
5304 aim_im_sendch2_chatinvite(od, name, message ? message : "",
5305 ccon->exchange, ccon->name, 0x0);
5306 }
5307
5308 void
5309 oscar_chat_leave(GaimConnection *gc, int id)
5310 {
5311 GaimConversation *conv;
5312 struct chat_connection *cc;
5313
5314 conv = gaim_find_chat(gc, id);
5315
5316 g_return_if_fail(conv != NULL);
5317
5318 gaim_debug_info("oscar", "Leaving chat room %s\n", conv->name);
5319
5320 cc = find_oscar_chat(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(conv)));
5321 oscar_chat_kill(gc, cc);
5322 }
5323
5324 int oscar_send_chat(GaimConnection *gc, int id, const char *message, GaimMessageFlags flags) {
5325 OscarData *od = (OscarData *)gc->proto_data;
5326 GaimConversation *conv = NULL;
5327 struct chat_connection *c = NULL;
5328 char *buf, *buf2;
5329 guint16 charset, charsubset;
5330 char *charsetstr = NULL;
5331 int len;
5332
5333 if (!(conv = gaim_find_chat(gc, id)))
5334 return -EINVAL;
5335
5336 if (!(c = find_oscar_chat_by_conv(gc, conv)))
5337 return -EINVAL;
5338
5339 buf = gaim_strdup_withhtml(message);
5340 len = strlen(buf);
5341
5342 if (strstr(buf, "<IMG "))
5343 gaim_conversation_write(conv, "",
5344 _("Your IM Image was not sent. "
5345 "You cannot send IM Images in AIM chats."),
5346 GAIM_MESSAGE_ERROR, time(NULL));
5347
5348 gaim_plugin_oscar_convert_to_best_encoding(gc, NULL, buf, &buf2, &len, &charset, &charsubset);
5349 /*
5350 * Evan S. suggested that maxvis really does mean "number of
5351 * visible characters" and not "number of bytes"
5352 */
5353 if ((len > c->maxlen) || (len > c->maxvis)) {
5354 g_free(buf2);
5355 return -E2BIG;
5356 }
5357
5358 if (charset == AIM_CHARSET_ASCII)
5359 charsetstr = "us-ascii";
5360 else if (charset == AIM_CHARSET_UNICODE)
5361 charsetstr = "unicode-2-0";
5362 else if (charset == AIM_CHARSET_CUSTOM)
5363 charsetstr = "iso-8859-1";
5364 aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en");
5365 g_free(buf2);
5366
5367 return 0;
5368 }
5369
5370 const char *oscar_list_icon_icq(GaimAccount *a, GaimBuddy *b)
5371 {
5372 if ((b == NULL) || (b->name == NULL) || aim_sn_is_sms(b->name))
5373 {
5374 if (a == NULL || aim_sn_is_icq(gaim_account_get_username(a)))
5375 return "icq";
5376 else
5377 return "aim";
5378 }
5379
5380 if (aim_sn_is_icq(b->name))
5381 return "icq";
5382 return "aim";
5383 }
5384
5385 const char *oscar_list_icon_aim(GaimAccount *a, GaimBuddy *b)
5386 {
5387 if ((b == NULL) || (b->name == NULL) || aim_sn_is_sms(b->name))
5388 {
5389 if (a != NULL && aim_sn_is_icq(gaim_account_get_username(a)))
5390 return "icq";
5391 else
5392 return "aim";
5393 }
5394
5395 if (aim_sn_is_icq(b->name))
5396 return "icq";
5397 return "aim";
5398 }
5399
5400 void oscar_list_emblems(GaimBuddy *b, const char **se, const char **sw, const char **nw, const char **ne)
5401 {
5402 GaimConnection *gc = NULL;
5403 OscarData *od = NULL;
5404 GaimAccount *account = NULL;
5405 GaimPresence *presence;
5406 GaimStatus *status;
5407 const char *status_id;
5408 char *emblems[4] = {NULL,NULL,NULL,NULL};
5409 int i = 0;
5410 aim_userinfo_t *userinfo = NULL;
5411
5412 account = b->account;
5413 if (account != NULL)
5414 gc = account->gc;
5415 if (gc != NULL)
5416 od = gc->proto_data;
5417 if (od != NULL)
5418 userinfo = aim_locate_finduserinfo(od, b->name);
5419
5420 presence = gaim_buddy_get_presence(b);
5421 status = gaim_presence_get_active_status(presence);
5422 status_id = gaim_status_get_id(status);
5423
5424 if (gaim_presence_is_online(presence) == FALSE) {
5425 char *gname;
5426 if ((b->name) && (od) && (od->ssi.received_data) &&
5427 (gname = aim_ssi_itemlist_findparentname(od->ssi.local, b->name)) &&
5428 (aim_ssi_waitingforauth(od->ssi.local, gname, b->name))) {
5429 emblems[i++] = "notauthorized";
5430 } else {
5431 emblems[i++] = "offline";
5432 }
5433 }
5434
5435 if (b->name && aim_sn_is_icq(b->name)) {
5436 if (!strcmp(status_id, OSCAR_STATUS_ID_INVISIBLE))
5437 emblems[i++] = "invisible";
5438 else if (!strcmp(status_id, OSCAR_STATUS_ID_FREE4CHAT))
5439 emblems[i++] = "freeforchat";
5440 else if (!strcmp(status_id, OSCAR_STATUS_ID_DND))
5441 emblems[i++] = "dnd";
5442 else if (!strcmp(status_id, OSCAR_STATUS_ID_NA))
5443 emblems[i++] = "unavailable";
5444 else if (!strcmp(status_id, OSCAR_STATUS_ID_OCCUPIED))
5445 emblems[i++] = "occupied";
5446 else if (!strcmp(status_id, OSCAR_STATUS_ID_AWAY))
5447 emblems[i++] = "away";
5448 } else if (!strcmp(status_id, OSCAR_STATUS_ID_AWAY)) {
5449 emblems[i++] = "away";
5450 }
5451
5452 if (userinfo != NULL ) {
5453 /* if (userinfo->flags & AIM_FLAG_UNCONFIRMED)
5454 emblems[i++] = "unconfirmed"; */
5455 if ((i < 4) && userinfo->flags & AIM_FLAG_ADMINISTRATOR)
5456 emblems[i++] = "admin";
5457 if ((i < 4) && userinfo->flags & AIM_FLAG_AOL)
5458 emblems[i++] = "aol";
5459 if ((i < 4) && userinfo->flags & AIM_FLAG_WIRELESS)
5460 emblems[i++] = "wireless";
5461 if ((i < 4) && userinfo->flags & AIM_FLAG_ACTIVEBUDDY)
5462 emblems[i++] = "activebuddy";
5463
5464 if ((i < 4) && (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP))
5465 emblems[i++] = "hiptop";
5466
5467 if ((i < 4) && (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM))
5468 emblems[i++] = "secure";
5469 }
5470
5471 *se = emblems[0];
5472 *sw = emblems[1];
5473 *nw = emblems[2];
5474 *ne = emblems[3];
5475 }
5476
5477 void oscar_tooltip_text(GaimBuddy *b, GaimNotifyUserInfo *user_info, gboolean full) {
5478 GaimConnection *gc = b->account->gc;
5479 OscarData *od = gc->proto_data;
5480 aim_userinfo_t *userinfo = aim_locate_finduserinfo(od, b->name);
5481
5482 if (GAIM_BUDDY_IS_ONLINE(b)) {
5483 GaimPresence *presence;
5484 GaimStatus *status;
5485 const char *message;
5486
5487 if (full)
5488 oscar_string_append_info(gc, user_info, b, userinfo);
5489
5490 presence = gaim_buddy_get_presence(b);
5491 status = gaim_presence_get_active_status(presence);
5492 message = gaim_status_get_attr_string(status, "message");
5493
5494 if (gaim_status_is_available(status))
5495 {
5496 if (message != NULL)
5497 {
5498 /* Available status messages are plain text */
5499 gchar *tmp;
5500 tmp = g_markup_escape_text(message, -1);
5501 gaim_notify_user_info_add_pair(user_info, _("Message"), tmp);
5502 g_free(tmp);
5503 }
5504 }
5505 else
5506 {
5507 if (message != NULL)
5508 {
5509 /* Away messages are HTML */
5510 gchar *tmp1, *tmp2;
5511 tmp2 = gaim_markup_strip_html(message);
5512 tmp1 = g_markup_escape_text(tmp2, -1);
5513 g_free(tmp2);
5514 tmp2 = gaim_str_sub_away_formatters(tmp1, gaim_account_get_username(gaim_connection_get_account(gc)));
5515 g_free(tmp1);
5516 gaim_notify_user_info_add_pair(user_info, _("Away Message"), tmp2);
5517 g_free(tmp2);
5518 }
5519 else
5520 {
5521 gaim_notify_user_info_add_pair(user_info, _("Away Message"), _("<i>(retrieving)</i>"));
5522 }
5523 }
5524 }
5525 }
5526
5527 char *oscar_status_text(GaimBuddy *b)
5528 {
5529 GaimConnection *gc;
5530 GaimAccount *account;
5531 OscarData *od;
5532 const GaimPresence *presence;
5533 const GaimStatus *status;
5534 const char *id;
5535 const char *message;
5536 gchar *ret = NULL;
5537
5538 gc = gaim_account_get_connection(gaim_buddy_get_account(b));
5539 account = gaim_connection_get_account(gc);
5540 od = gc->proto_data;
5541 presence = gaim_buddy_get_presence(b);
5542 status = gaim_presence_get_active_status(presence);
5543 id = gaim_status_get_id(status);
5544
5545 if (!gaim_presence_is_online(presence))
5546 {
5547 char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, b->name);
5548 if (aim_ssi_waitingforauth(od->ssi.local, gname, b->name))
5549 ret = g_strdup(_("Not Authorized"));
5550 else
5551 ret = g_strdup(_("Offline"));
5552 }
5553 else if (gaim_status_is_available(status) && !strcmp(id, OSCAR_STATUS_ID_AVAILABLE))
5554 {
5555 /* Available */
5556 message = gaim_status_get_attr_string(status, "message");
5557 if (message != NULL)
5558 {
5559 ret = g_markup_escape_text(message, -1);
5560 gaim_util_chrreplace(ret, '\n', ' ');
5561 }
5562 }
5563 else if (!gaim_status_is_available(status) && !strcmp(id, OSCAR_STATUS_ID_AWAY))
5564 {
5565 /* Away */
5566 message = gaim_status_get_attr_string(status, "message");
5567 if (message != NULL)
5568 {
5569 gchar *tmp1, *tmp2;
5570 tmp1 = gaim_markup_strip_html(message);
5571 gaim_util_chrreplace(tmp1, '\n', ' ');
5572 tmp2 = g_markup_escape_text(tmp1, -1);
5573 ret = gaim_str_sub_away_formatters(tmp2, gaim_account_get_username(account));
5574 g_free(tmp1);
5575 g_free(tmp2);
5576 }
5577 else
5578 {
5579 ret = g_strdup(_("Away"));
5580 }
5581 }
5582 else
5583 ret = g_strdup(gaim_status_get_name(status));
5584
5585 return ret;
5586 }
5587
5588
5589 static int oscar_icon_req(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
5590 GaimConnection *gc = od->gc;
5591 va_list ap;
5592 guint16 type;
5593 guint8 flags = 0, length = 0;
5594 guchar *md5 = NULL;
5595
5596 va_start(ap, fr);
5597 type = va_arg(ap, int);
5598
5599 switch(type) {
5600 case 0x0000:
5601 case 0x0001: {
5602 flags = va_arg(ap, int);
5603 length = va_arg(ap, int);
5604 md5 = va_arg(ap, guchar *);
5605
5606 if (flags == 0x41) {
5607 if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) {
5608 od->iconconnecting = TRUE;
5609 od->set_icon = TRUE;
5610 aim_srv_requestnew(od, SNAC_FAMILY_BART);
5611 } else {
5612 struct stat st;
5613 char *iconfile = gaim_buddy_icons_get_full_path(gaim_account_get_buddy_icon(gaim_connection_get_account(gc)));
5614 if (iconfile == NULL) {
5615 aim_ssi_delicon(od);
5616 } else if (!g_stat(iconfile, &st)) {
5617 guchar *buf = g_malloc(st.st_size);
5618 FILE *file = g_fopen(iconfile, "rb");
5619 if (file) {
5620 /* XXX - Use g_file_get_contents()? */
5621 fread(buf, 1, st.st_size, file);
5622 fclose(file);
5623 gaim_debug_info("oscar",
5624 "Uploading icon to icon server\n");
5625 aim_bart_upload(od, buf, st.st_size);
5626 } else
5627 gaim_debug_error("oscar",
5628 "Can't open buddy icon file!\n");
5629 g_free(buf);
5630 } else {
5631 gaim_debug_error("oscar",
5632 "Can't stat buddy icon file!\n");
5633 }
5634 g_free(iconfile);
5635 }
5636 } else if (flags == 0x81) {
5637 char *iconfile = gaim_buddy_icons_get_full_path(gaim_account_get_buddy_icon(gaim_connection_get_account(gc)));
5638 if (iconfile == NULL)
5639 aim_ssi_delicon(od);
5640 else {
5641 aim_ssi_seticon(od, md5, length);
5642 g_free(iconfile);
5643 }
5644 }
5645 } break;
5646
5647 case 0x0002: { /* We just set an "available" message? */
5648 } break;
5649 }
5650
5651 va_end(ap);
5652
5653 return 0;
5654 }
5655
5656 void oscar_set_permit_deny(GaimConnection *gc) {
5657 GaimAccount *account = gaim_connection_get_account(gc);
5658 OscarData *od = (OscarData *)gc->proto_data;
5659
5660 if (od->ssi.received_data) {
5661 switch (account->perm_deny) {
5662 case GAIM_PRIVACY_ALLOW_ALL:
5663 aim_ssi_setpermdeny(od, 0x01, 0xffffffff);
5664 break;
5665 case GAIM_PRIVACY_ALLOW_BUDDYLIST:
5666 aim_ssi_setpermdeny(od, 0x05, 0xffffffff);
5667 break;
5668 case GAIM_PRIVACY_ALLOW_USERS:
5669 aim_ssi_setpermdeny(od, 0x03, 0xffffffff);
5670 break;
5671 case GAIM_PRIVACY_DENY_ALL:
5672 aim_ssi_setpermdeny(od, 0x02, 0xffffffff);
5673 break;
5674 case GAIM_PRIVACY_DENY_USERS:
5675 aim_ssi_setpermdeny(od, 0x04, 0xffffffff);
5676 break;
5677 default:
5678 aim_ssi_setpermdeny(od, 0x01, 0xffffffff);
5679 break;
5680 }
5681 }
5682 }
5683
5684 void oscar_add_permit(GaimConnection *gc, const char *who) {
5685 OscarData *od = (OscarData *)gc->proto_data;
5686 gaim_debug_info("oscar", "ssi: About to add a permit\n");
5687 if (od->ssi.received_data)
5688 aim_ssi_addpermit(od, who);
5689 }
5690
5691 void oscar_add_deny(GaimConnection *gc, const char *who) {
5692 OscarData *od = (OscarData *)gc->proto_data;
5693 gaim_debug_info("oscar", "ssi: About to add a deny\n");
5694 if (od->ssi.received_data)
5695 aim_ssi_adddeny(od, who);
5696 }
5697
5698 void oscar_rem_permit(GaimConnection *gc, const char *who) {
5699 OscarData *od = (OscarData *)gc->proto_data;
5700 gaim_debug_info("oscar", "ssi: About to delete a permit\n");
5701 if (od->ssi.received_data)
5702 aim_ssi_delpermit(od, who);
5703 }
5704
5705 void oscar_rem_deny(GaimConnection *gc, const char *who) {
5706 OscarData *od = (OscarData *)gc->proto_data;
5707 gaim_debug_info("oscar", "ssi: About to delete a deny\n");
5708 if (od->ssi.received_data)
5709 aim_ssi_deldeny(od, who);
5710 }
5711
5712 GList *
5713 oscar_status_types(GaimAccount *account)
5714 {
5715 gboolean is_icq;
5716 GList *status_types = NULL;
5717 GaimStatusType *type;
5718
5719 g_return_val_if_fail(account != NULL, NULL);
5720
5721 /* Used to flag some statuses as "user settable" or not */
5722 is_icq = aim_sn_is_icq(gaim_account_get_username(account));
5723
5724 /* Common status types */
5725 /* Really the available message should only be settable for AIM accounts */
5726 type = gaim_status_type_new_with_attrs(GAIM_STATUS_AVAILABLE,
5727 OSCAR_STATUS_ID_AVAILABLE,
5728 NULL, TRUE, TRUE, FALSE,
5729 "message", _("Message"),
5730 gaim_value_new(GAIM_TYPE_STRING), NULL);
5731 status_types = g_list_prepend(status_types, type);
5732
5733 type = gaim_status_type_new_full(GAIM_STATUS_AVAILABLE,
5734 OSCAR_STATUS_ID_FREE4CHAT,
5735 _("Free For Chat"), TRUE, is_icq, FALSE);
5736 status_types = g_list_prepend(status_types, type);
5737
5738 type = gaim_status_type_new_with_attrs(GAIM_STATUS_AWAY,
5739 OSCAR_STATUS_ID_AWAY,
5740 NULL, TRUE, TRUE, FALSE,
5741 "message", _("Message"),
5742 gaim_value_new(GAIM_TYPE_STRING), NULL);
5743 status_types = g_list_prepend(status_types, type);
5744
5745 type = gaim_status_type_new_full(GAIM_STATUS_INVISIBLE,
5746 OSCAR_STATUS_ID_INVISIBLE,
5747 NULL, TRUE, TRUE, FALSE);
5748 status_types = g_list_prepend(status_types, type);
5749
5750 /* ICQ-specific status types */
5751 type = gaim_status_type_new_with_attrs(GAIM_STATUS_UNAVAILABLE,
5752 OSCAR_STATUS_ID_OCCUPIED,
5753 _("Occupied"), TRUE, is_icq, FALSE,
5754 "message", _("Message"),
5755 gaim_value_new(GAIM_TYPE_STRING), NULL);
5756 status_types = g_list_prepend(status_types, type);
5757
5758 type = gaim_status_type_new_with_attrs(GAIM_STATUS_EXTENDED_AWAY,
5759 OSCAR_STATUS_ID_DND,
5760 _("Do Not Disturb"), TRUE, is_icq, FALSE,
5761 "message", _("Message"),
5762 gaim_value_new(GAIM_TYPE_STRING), NULL);
5763 status_types = g_list_prepend(status_types, type);
5764
5765 type = gaim_status_type_new_with_attrs(GAIM_STATUS_EXTENDED_AWAY,
5766 OSCAR_STATUS_ID_NA,
5767 _("Not Available"), TRUE, is_icq, FALSE,
5768 "message", _("Message"),
5769 gaim_value_new(GAIM_TYPE_STRING), NULL);
5770 status_types = g_list_prepend(status_types, type);
5771
5772 type = gaim_status_type_new_full(GAIM_STATUS_OFFLINE,
5773 OSCAR_STATUS_ID_OFFLINE,
5774 NULL, TRUE, TRUE, FALSE);
5775 status_types = g_list_prepend(status_types, type);
5776
5777 status_types = g_list_reverse(status_types);
5778
5779 return status_types;
5780 }
5781
5782 static void oscar_ssi_editcomment(struct name_data *data, const char *text) {
5783 GaimConnection *gc = data->gc;
5784 OscarData *od = gc->proto_data;
5785 GaimBuddy *b;
5786 GaimGroup *g;
5787
5788 if (!(b = gaim_find_buddy(gaim_connection_get_account(data->gc), data->name))) {
5789 oscar_free_name_data(data);
5790 return;
5791 }
5792
5793 if (!(g = gaim_buddy_get_group(b))) {
5794 oscar_free_name_data(data);
5795 return;
5796 }
5797
5798 aim_ssi_editcomment(od, g->name, data->name, text);
5799
5800 if (!aim_sncmp(data->name, gc->account->username))
5801 gaim_check_comment(od, text);
5802
5803 oscar_free_name_data(data);
5804 }
5805
5806 static void oscar_buddycb_edit_comment(GaimBlistNode *node, gpointer ignore) {
5807
5808 GaimBuddy *buddy;
5809 GaimConnection *gc;
5810 OscarData *od;
5811 struct name_data *data;
5812 GaimGroup *g;
5813 char *comment;
5814 gchar *comment_utf8;
5815 gchar *title;
5816
5817 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
5818
5819 buddy = (GaimBuddy *) node;
5820 gc = gaim_account_get_connection(buddy->account);
5821 od = gc->proto_data;
5822
5823 data = g_new(struct name_data, 1);
5824
5825 if (!(g = gaim_buddy_get_group(buddy)))
5826 return;
5827 comment = aim_ssi_getcomment(od->ssi.local, g->name, buddy->name);
5828 comment_utf8 = comment ? oscar_utf8_try_convert(gc->account, comment) : NULL;
5829
5830 data->gc = gc;
5831 data->name = g_strdup(buddy->name);
5832 data->nick = NULL;
5833
5834 title = g_strdup_printf(_("Buddy Comment for %s"), data->name);
5835 gaim_request_input(gc, title, _("Buddy Comment:"), NULL,
5836 comment_utf8, TRUE, FALSE, NULL,
5837 _("_OK"), G_CALLBACK(oscar_ssi_editcomment),
5838 _("_Cancel"), G_CALLBACK(oscar_free_name_data),
5839 data);
5840 g_free(title);
5841
5842 g_free(comment);
5843 g_free(comment_utf8);
5844 }
5845
5846 static void
5847 oscar_ask_directim_yes_cb(struct oscar_ask_directim_data *data)
5848 {
5849 peer_connection_propose(data->od, OSCAR_CAPABILITY_DIRECTIM, data->who);
5850 g_free(data->who);
5851 g_free(data);
5852 }
5853
5854 static void
5855 oscar_ask_directim_no_cb(struct oscar_ask_directim_data *data)
5856 {
5857 g_free(data->who);
5858 g_free(data);
5859 }
5860
5861 /* This is called from right-click menu on a buddy node. */
5862 static void
5863 oscar_ask_directim(gpointer object, gpointer ignored)
5864 {
5865 GaimBlistNode *node;
5866 GaimBuddy *buddy;
5867 GaimConnection *gc;
5868 gchar *buf;
5869 struct oscar_ask_directim_data *data;
5870
5871 node = object;
5872
5873 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
5874
5875 buddy = (GaimBuddy *)node;
5876 gc = gaim_account_get_connection(buddy->account);
5877
5878 data = g_new0(struct oscar_ask_directim_data, 1);
5879 data->who = g_strdup(buddy->name);
5880 data->od = gc->proto_data;
5881 buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."),
5882 buddy->name);
5883
5884 gaim_request_action(gc, NULL, buf,
5885 _("Because this reveals your IP address, it "
5886 "may be considered a security risk. Do you "
5887 "wish to continue?"),
5888 0, data, 2,
5889 _("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb),
5890 _("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb));
5891 g_free(buf);
5892 }
5893
5894 static void
5895 oscar_get_aim_info_cb(GaimBlistNode *node, gpointer ignore)
5896 {
5897 GaimBuddy *buddy;
5898 GaimConnection *gc;
5899
5900 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
5901
5902 buddy = (GaimBuddy *)node;
5903 gc = gaim_account_get_connection(buddy->account);
5904
5905 aim_locate_getinfoshort(gc->proto_data, gaim_buddy_get_name(buddy), 0x00000003);
5906 }
5907
5908 static GList *
5909 oscar_buddy_menu(GaimBuddy *buddy) {
5910
5911 GaimConnection *gc;
5912 OscarData *od;
5913 GList *menu;
5914 GaimMenuAction *act;
5915 aim_userinfo_t *userinfo;
5916
5917 gc = gaim_account_get_connection(buddy->account);
5918 od = gc->proto_data;
5919 userinfo = aim_locate_finduserinfo(od, buddy->name);
5920 menu = NULL;
5921
5922 if (od->icq && aim_sn_is_icq(gaim_buddy_get_name(buddy)))
5923 {
5924 act = gaim_menu_action_new(_("Get AIM Info"),
5925 GAIM_CALLBACK(oscar_get_aim_info_cb),
5926 NULL, NULL);
5927 menu = g_list_prepend(menu, act);
5928 }
5929
5930 act = gaim_menu_action_new(_("Edit Buddy Comment"),
5931 GAIM_CALLBACK(oscar_buddycb_edit_comment),
5932 NULL, NULL);
5933 menu = g_list_prepend(menu, act);
5934
5935 #if 0
5936 if (od->icq)
5937 {
5938 act = gaim_menu_action_new(_("Get Status Msg"),
5939 GAIM_CALLBACK(oscar_get_icqstatusmsg),
5940 NULL, NULL);
5941 menu = g_list_prepend(menu, act);
5942 }
5943 #endif
5944
5945 if (userinfo &&
5946 aim_sncmp(gaim_account_get_username(buddy->account), buddy->name) &&
5947 GAIM_BUDDY_IS_ONLINE(buddy))
5948 {
5949 if (userinfo->capabilities & OSCAR_CAPABILITY_DIRECTIM)
5950 {
5951 act = gaim_menu_action_new(_("Direct IM"),
5952 GAIM_CALLBACK(oscar_ask_directim),
5953 NULL, NULL);
5954 menu = g_list_prepend(menu, act);
5955 }
5956 #if 0
5957 /* TODO: This menu item should be added by the core */
5958 if (userinfo->capabilities & OSCAR_CAPABILITY_GETFILE) {
5959 act = gaim_menu_action_new(_("Get File"),
5960 GAIM_CALLBACK(oscar_ask_getfile),
5961 NULL, NULL);
5962 menu = g_list_prepend(menu, act);
5963 }
5964 #endif
5965 }
5966
5967 if (od->ssi.received_data)
5968 {
5969 char *gname;
5970 gname = aim_ssi_itemlist_findparentname(od->ssi.local, buddy->name);
5971 if (gname && aim_ssi_waitingforauth(od->ssi.local, gname, buddy->name))
5972 {
5973 act = gaim_menu_action_new(_("Re-request Authorization"),
5974 GAIM_CALLBACK(gaim_auth_sendrequest_menu),
5975 NULL, NULL);
5976 menu = g_list_prepend(menu, act);
5977 }
5978 }
5979
5980 menu = g_list_reverse(menu);
5981
5982 return menu;
5983 }
5984
5985
5986 GList *oscar_blist_node_menu(GaimBlistNode *node) {
5987 if(GAIM_BLIST_NODE_IS_BUDDY(node)) {
5988 return oscar_buddy_menu((GaimBuddy *) node);
5989 } else {
5990 return NULL;
5991 }
5992 }
5993
5994 static void
5995 oscar_icq_privacy_opts(GaimConnection *gc, GaimRequestFields *fields)
5996 {
5997 OscarData *od = gc->proto_data;
5998 GaimAccount *account = gaim_connection_get_account(gc);
5999 GaimRequestField *f;
6000 gboolean auth, web_aware;
6001
6002 f = gaim_request_fields_get_field(fields, "authorization");
6003 auth = gaim_request_field_bool_get_value(f);
6004
6005 f = gaim_request_fields_get_field(fields, "web_aware");
6006 web_aware = gaim_request_field_bool_get_value(f);
6007
6008 gaim_account_set_bool(account, "authorization", auth);
6009 gaim_account_set_bool(account, "web_aware", web_aware);
6010
6011 oscar_set_extendedstatus(gc);
6012 aim_icq_setsecurity(od, auth, web_aware);
6013 }
6014
6015 static void
6016 oscar_show_icq_privacy_opts(GaimPluginAction *action)
6017 {
6018 GaimConnection *gc = (GaimConnection *) action->context;
6019 GaimAccount *account = gaim_connection_get_account(gc);
6020 GaimRequestFields *fields;
6021 GaimRequestFieldGroup *g;
6022 GaimRequestField *f;
6023 gboolean auth, web_aware;
6024
6025 auth = gaim_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION);
6026 web_aware = gaim_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE);
6027
6028 fields = gaim_request_fields_new();
6029
6030 g = gaim_request_field_group_new(NULL);
6031
6032 f = gaim_request_field_bool_new("authorization", _("Require authorization"), auth);
6033 gaim_request_field_group_add_field(g, f);
6034
6035 f = gaim_request_field_bool_new("web_aware", _("Web aware (enabling this will cause you to receive SPAM!)"), web_aware);
6036 gaim_request_field_group_add_field(g, f);
6037
6038 gaim_request_fields_add_group(fields, g);
6039
6040 gaim_request_fields(gc, _("ICQ Privacy Options"), _("ICQ Privacy Options"),
6041 NULL, fields,
6042 _("OK"), G_CALLBACK(oscar_icq_privacy_opts),
6043 _("Cancel"), NULL, gc);
6044 }
6045
6046 static void oscar_format_screenname(GaimConnection *gc, const char *nick) {
6047 OscarData *od = gc->proto_data;
6048 if (!aim_sncmp(gaim_account_get_username(gaim_connection_get_account(gc)), nick)) {
6049 if (!flap_connection_getbytype(od, SNAC_FAMILY_ADMIN)) {
6050 od->setnick = TRUE;
6051 od->newsn = g_strdup(nick);
6052 aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
6053 } else {
6054 aim_admin_setnick(od, flap_connection_getbytype(od, SNAC_FAMILY_ADMIN), nick);
6055 }
6056 } else {
6057 gaim_notify_error(gc, NULL, _("The new formatting is invalid."),
6058 _("Screen name formatting can change only capitalization and whitespace."));
6059 }
6060 }
6061
6062 static void oscar_show_format_screenname(GaimPluginAction *action)
6063 {
6064 GaimConnection *gc = (GaimConnection *) action->context;
6065 gaim_request_input(gc, NULL, _("New screen name formatting:"), NULL,
6066 gaim_connection_get_display_name(gc), FALSE, FALSE, NULL,
6067 _("_OK"), G_CALLBACK(oscar_format_screenname),
6068 _("_Cancel"), NULL,
6069 gc);
6070 }
6071
6072 static void oscar_confirm_account(GaimPluginAction *action)
6073 {
6074 GaimConnection *gc;
6075 OscarData *od;
6076 FlapConnection *conn;
6077
6078 gc = (GaimConnection *)action->context;
6079 od = gc->proto_data;
6080
6081 conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
6082 if (conn != NULL) {
6083 aim_admin_reqconfirm(od, conn);
6084 } else {
6085 od->conf = TRUE;
6086 aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
6087 }
6088 }
6089
6090 static void oscar_show_email(GaimPluginAction *action)
6091 {
6092 GaimConnection *gc = (GaimConnection *) action->context;
6093 OscarData *od = gc->proto_data;
6094 FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
6095
6096 if (conn) {
6097 aim_admin_getinfo(od, conn, 0x11);
6098 } else {
6099 od->reqemail = TRUE;
6100 aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
6101 }
6102 }
6103
6104 static void oscar_change_email(GaimConnection *gc, const char *email)
6105 {
6106 OscarData *od = gc->proto_data;
6107 FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
6108
6109 if (conn) {
6110 aim_admin_setemail(od, conn, email);
6111 } else {
6112 od->setemail = TRUE;
6113 od->email = g_strdup(email);
6114 aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
6115 }
6116 }
6117
6118 static void oscar_show_change_email(GaimPluginAction *action)
6119 {
6120 GaimConnection *gc = (GaimConnection *) action->context;
6121 gaim_request_input(gc, NULL, _("Change Address To:"), NULL, NULL,
6122 FALSE, FALSE, NULL,
6123 _("_OK"), G_CALLBACK(oscar_change_email),
6124 _("_Cancel"), NULL,
6125 gc);
6126 }
6127
6128 static void oscar_show_awaitingauth(GaimPluginAction *action)
6129 {
6130 GaimConnection *gc = (GaimConnection *) action->context;
6131 OscarData *od = gc->proto_data;
6132 gchar *nombre, *text, *tmp;
6133 GaimBlistNode *gnode, *cnode, *bnode;
6134 int num=0;
6135
6136 text = g_strdup("");
6137
6138 for (gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
6139 GaimGroup *group = (GaimGroup *)gnode;
6140 if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
6141 continue;
6142 for (cnode = gnode->child; cnode; cnode = cnode->next) {
6143 if(!GAIM_BLIST_NODE_IS_CONTACT(cnode))
6144 continue;
6145 for (bnode = cnode->child; bnode; bnode = bnode->next) {
6146 GaimBuddy *buddy = (GaimBuddy *)bnode;
6147 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
6148 continue;
6149 if (buddy->account == gc->account && aim_ssi_waitingforauth(od->ssi.local, group->name, buddy->name)) {
6150 if (gaim_buddy_get_alias_only(buddy))
6151 nombre = g_strdup_printf(" %s (%s)", buddy->name, gaim_buddy_get_alias_only(buddy));
6152 else
6153 nombre = g_strdup_printf(" %s", buddy->name);
6154 tmp = g_strdup_printf("%s%s<br>", text, nombre);
6155 g_free(text);
6156 text = tmp;
6157 g_free(nombre);
6158 num++;
6159 }
6160 }
6161 }
6162 }
6163
6164 if (!num) {
6165 g_free(text);
6166 text = g_strdup(_("<i>you are not waiting for authorization</i>"));
6167 }
6168
6169 gaim_notify_formatted(gc, NULL, _("You are awaiting authorization from "
6170 "the following buddies"), _("You can re-request "
6171 "authorization from these buddies by "
6172 "right-clicking on them and selecting "
6173 "\"Re-request Authorization.\""), text, NULL, NULL);
6174 g_free(text);
6175 }
6176
6177 static void search_by_email_cb(GaimConnection *gc, const char *email)
6178 {
6179 OscarData *od = (OscarData *)gc->proto_data;
6180
6181 aim_search_address(od, email);
6182 }
6183
6184 static void oscar_show_find_email(GaimPluginAction *action)
6185 {
6186 GaimConnection *gc = (GaimConnection *) action->context;
6187 gaim_request_input(gc, _("Find Buddy by E-Mail"),
6188 _("Search for a buddy by e-mail address"),
6189 _("Type the e-mail address of the buddy you are "
6190 "searching for."),
6191 NULL, FALSE, FALSE, NULL,
6192 _("_Search"), G_CALLBACK(search_by_email_cb),
6193 _("_Cancel"), NULL, gc);
6194 }
6195
6196 static void oscar_show_set_info(GaimPluginAction *action)
6197 {
6198 GaimConnection *gc = (GaimConnection *) action->context;
6199 gaim_account_request_change_user_info(gaim_connection_get_account(gc));
6200 }
6201
6202 static void oscar_show_set_info_icqurl(GaimPluginAction *action)
6203 {
6204 GaimConnection *gc = (GaimConnection *) action->context;
6205 gaim_notify_uri(gc, "http://www.icq.com/whitepages/user_details.php");
6206 }
6207
6208 static void oscar_change_pass(GaimPluginAction *action)
6209 {
6210 GaimConnection *gc = (GaimConnection *) action->context;
6211 gaim_account_request_change_password(gaim_connection_get_account(gc));
6212 }
6213
6214 static void oscar_show_chpassurl(GaimPluginAction *action)
6215 {
6216 GaimConnection *gc = (GaimConnection *) action->context;
6217 OscarData *od = gc->proto_data;
6218 gchar *substituted = gaim_strreplace(od->authinfo->chpassurl, "%s", gaim_account_get_username(gaim_connection_get_account(gc)));
6219 gaim_notify_uri(gc, substituted);
6220 g_free(substituted);
6221 }
6222
6223 static void oscar_show_imforwardingurl(GaimPluginAction *action)
6224 {
6225 GaimConnection *gc = (GaimConnection *) action->context;
6226 gaim_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1");
6227 }
6228
6229 void oscar_set_icon(GaimConnection *gc, const char *iconfile)
6230 {
6231 OscarData *od = gc->proto_data;
6232 FILE *file;
6233 struct stat st;
6234
6235 if (iconfile == NULL) {
6236 aim_ssi_delicon(od);
6237 } else if (!g_stat(iconfile, &st)) {
6238 guchar *buf = g_malloc(st.st_size);
6239 file = g_fopen(iconfile, "rb");
6240 if (file)
6241 {
6242 GaimCipher *cipher;
6243 GaimCipherContext *context;
6244 guchar md5[16];
6245 int len;
6246
6247 /* XXX - Use g_file_get_contents()? */
6248 len = fread(buf, 1, st.st_size, file);
6249 fclose(file);
6250
6251 cipher = gaim_ciphers_find_cipher("md5");
6252 context = gaim_cipher_context_new(cipher, NULL);
6253 gaim_cipher_context_append(context, buf, len);
6254 gaim_cipher_context_digest(context, 16, md5, NULL);
6255 gaim_cipher_context_destroy(context);
6256
6257 aim_ssi_seticon(od, md5, 16);
6258 } else
6259 gaim_debug_error("oscar",
6260 "Can't open buddy icon file!\n");
6261 g_free(buf);
6262 } else
6263 gaim_debug_error("oscar", "Can't stat buddy icon file!\n");
6264 }
6265
6266 /**
6267 * Called by the Gaim core to determine whether or not we're
6268 * allowed to send a file to this user.
6269 */
6270 gboolean
6271 oscar_can_receive_file(GaimConnection *gc, const char *who)
6272 {
6273 OscarData *od;
6274 GaimAccount *account;
6275
6276 od = gc->proto_data;
6277 account = gaim_connection_get_account(gc);
6278
6279 if (od != NULL)
6280 {
6281 aim_userinfo_t *userinfo;
6282 userinfo = aim_locate_finduserinfo(od, who);
6283
6284 /*
6285 * Don't allowing sending a file to a user that does not support
6286 * file transfer, and don't allow sending to ourselves.
6287 */
6288 if (((userinfo == NULL) ||
6289 (userinfo->capabilities & OSCAR_CAPABILITY_SENDFILE)) &&
6290 aim_sncmp(who, gaim_account_get_username(account)))
6291 {
6292 return TRUE;
6293 }
6294 }
6295
6296 return FALSE;
6297 }
6298
6299 GaimXfer *
6300 oscar_new_xfer(GaimConnection *gc, const char *who)
6301 {
6302 GaimXfer *xfer;
6303 OscarData *od;
6304 GaimAccount *account;
6305 PeerConnection *conn;
6306
6307 od = gc->proto_data;
6308 account = gaim_connection_get_account(gc);
6309
6310 xfer = gaim_xfer_new(account, GAIM_XFER_SEND, who);
6311 if (xfer)
6312 {
6313 gaim_xfer_ref(xfer);
6314 gaim_xfer_set_init_fnc(xfer, peer_oft_sendcb_init);
6315 gaim_xfer_set_cancel_send_fnc(xfer, peer_oft_cb_generic_cancel);
6316 gaim_xfer_set_request_denied_fnc(xfer, peer_oft_cb_generic_cancel);
6317 gaim_xfer_set_ack_fnc(xfer, peer_oft_sendcb_ack);
6318
6319 conn = peer_connection_new(od, OSCAR_CAPABILITY_SENDFILE, who);
6320 conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
6321 conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
6322 aim_icbm_makecookie(conn->cookie);
6323 conn->xfer = xfer;
6324 xfer->data = conn;
6325 }
6326
6327 return xfer;
6328 }
6329
6330 /*
6331 * Called by the Gaim core when the user indicates that a
6332 * file is to be sent to a special someone.
6333 */
6334 void
6335 oscar_send_file(GaimConnection *gc, const char *who, const char *file)
6336 {
6337 GaimXfer *xfer;
6338
6339 xfer = oscar_new_xfer(gc, who);
6340
6341 if (file != NULL)
6342 gaim_xfer_request_accepted(xfer, file);
6343 else
6344 gaim_xfer_request(xfer);
6345 }
6346
6347 GList *
6348 oscar_actions(GaimPlugin *plugin, gpointer context)
6349 {
6350 GaimConnection *gc = (GaimConnection *) context;
6351 OscarData *od = gc->proto_data;
6352 GList *menu = NULL;
6353 GaimPluginAction *act;
6354
6355 act = gaim_plugin_action_new(_("Set User Info..."),
6356 oscar_show_set_info);
6357 menu = g_list_prepend(menu, act);
6358
6359 if (od->icq)
6360 {
6361 act = gaim_plugin_action_new(_("Set User Info (URL)..."),
6362 oscar_show_set_info_icqurl);
6363 menu = g_list_prepend(menu, act);
6364 }
6365
6366 act = gaim_plugin_action_new(_("Change Password..."),
6367 oscar_change_pass);
6368 menu = g_list_prepend(menu, act);
6369
6370 if (od->authinfo->chpassurl != NULL)
6371 {
6372 act = gaim_plugin_action_new(_("Change Password (URL)"),
6373 oscar_show_chpassurl);
6374 menu = g_list_prepend(menu, act);
6375
6376 act = gaim_plugin_action_new(_("Configure IM Forwarding (URL)"),
6377 oscar_show_imforwardingurl);
6378 menu = g_list_prepend(menu, act);
6379 }
6380
6381 menu = g_list_prepend(menu, NULL);
6382
6383 if (od->icq)
6384 {
6385 /* ICQ actions */
6386 act = gaim_plugin_action_new(_("Set Privacy Options..."),
6387 oscar_show_icq_privacy_opts);
6388 menu = g_list_prepend(menu, act);
6389 }
6390 else
6391 {
6392 /* AIM actions */
6393 act = gaim_plugin_action_new(_("Format Screen Name..."),
6394 oscar_show_format_screenname);
6395 menu = g_list_prepend(menu, act);
6396
6397 act = gaim_plugin_action_new(_("Confirm Account"),
6398 oscar_confirm_account);
6399 menu = g_list_prepend(menu, act);
6400
6401 act = gaim_plugin_action_new(_("Display Currently Registered E-Mail Address"),
6402 oscar_show_email);
6403 menu = g_list_prepend(menu, act);
6404
6405 act = gaim_plugin_action_new(_("Change Currently Registered E-Mail Address..."),
6406 oscar_show_change_email);
6407 menu = g_list_prepend(menu, act);
6408 }
6409
6410 menu = g_list_prepend(menu, NULL);
6411
6412 act = gaim_plugin_action_new(_("Show Buddies Awaiting Authorization"),
6413 oscar_show_awaitingauth);
6414 menu = g_list_prepend(menu, act);
6415
6416 menu = g_list_prepend(menu, NULL);
6417
6418 act = gaim_plugin_action_new(_("Search for Buddy by E-Mail Address..."),
6419 oscar_show_find_email);
6420 menu = g_list_prepend(menu, act);
6421
6422 #if 0
6423 act = gaim_plugin_action_new(_("Search for Buddy by Information"),
6424 show_find_info);
6425 menu = g_list_prepend(menu, act);
6426 #endif
6427
6428 menu = g_list_reverse(menu);
6429
6430 return menu;
6431 }
6432
6433 void oscar_change_passwd(GaimConnection *gc, const char *old, const char *new)
6434 {
6435 OscarData *od = gc->proto_data;
6436
6437 if (od->icq) {
6438 aim_icq_changepasswd(od, new);
6439 } else {
6440 FlapConnection *conn;
6441 conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
6442 if (conn) {
6443 aim_admin_changepasswd(od, conn, new, old);
6444 } else {
6445 od->chpass = TRUE;
6446 od->oldp = g_strdup(old);
6447 od->newp = g_strdup(new);
6448 aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
6449 }
6450 }
6451 }
6452
6453 void
6454 oscar_convo_closed(GaimConnection *gc, const char *who)
6455 {
6456 OscarData *od;
6457 PeerConnection *conn;
6458
6459 od = gc->proto_data;
6460 conn = peer_connection_find_by_type(od, who, OSCAR_CAPABILITY_DIRECTIM);
6461
6462 if (conn != NULL)
6463 {
6464 if (!conn->ready)
6465 aim_im_sendch2_cancel(conn);
6466
6467 peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
6468 }
6469 }
6470
6471 static void
6472 recent_buddies_cb(const char *name, GaimPrefType type,
6473 gconstpointer value, gpointer data)
6474 {
6475 GaimConnection *gc = data;
6476 OscarData *od = gc->proto_data;
6477 guint32 presence;
6478
6479 presence = aim_ssi_getpresence(od->ssi.local);
6480
6481 if (value) {
6482 presence &= ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES;
6483 aim_ssi_setpresence(od, presence);
6484 } else {
6485 presence |= AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES;
6486 aim_ssi_setpresence(od, presence);
6487 }
6488 }
6489
6490 #ifdef USE_PRPL_PREFERENCES
6491 ppref = gaim_plugin_pref_new_with_name_and_label("/plugins/prpl/oscar/recent_buddies", _("Use recent buddies group"));
6492 gaim_plugin_pref_frame_add(frame, ppref);
6493
6494 ppref = gaim_plugin_pref_new_with_name_and_label("/plugins/prpl/oscar/show_idle", _("Show how long you have been idle"));
6495 gaim_plugin_pref_frame_add(frame, ppref);
6496 #endif
6497
6498 const char *
6499 oscar_normalize(const GaimAccount *account, const char *str)
6500 {
6501 static char buf[BUF_LEN];
6502 char *tmp1, *tmp2;
6503 int i, j;
6504
6505 g_return_val_if_fail(str != NULL, NULL);
6506
6507 strncpy(buf, str, BUF_LEN);
6508 for (i=0, j=0; buf[j]; i++, j++)
6509 {
6510 while (buf[j] == ' ')
6511 j++;
6512 buf[i] = buf[j];
6513 }
6514 buf[i] = '\0';
6515
6516 tmp1 = g_utf8_strdown(buf, -1);
6517 tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT);
6518 g_snprintf(buf, sizeof(buf), "%s", tmp2);
6519 g_free(tmp2);
6520 g_free(tmp1);
6521
6522 return buf;
6523 }
6524
6525 gboolean
6526 oscar_offline_message(const GaimBuddy *buddy)
6527 {
6528 OscarData *od;
6529 GaimAccount *account;
6530 GaimConnection *gc;
6531
6532 account = gaim_buddy_get_account(buddy);
6533 gc = gaim_account_get_connection(account);
6534 od = (OscarData *)gc->proto_data;
6535
6536 return (od->icq && aim_sn_is_icq(gaim_account_get_username(account)));
6537 }
6538