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-2007, Mark Doliner <thekingant@users.sourceforge.net>
8 * Some code copyright (C) 2005, Jonathan Clark <ardentlygnarly@users.sourceforge.net>
9 * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com>
10 * Some code copyright (C) 2008, Aman Gupta
12 * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx>
13 * Some libfaim code copyright (C) 2001-2004 Mark Doliner <thekingant@users.sourceforge.net>
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
34 #include "accountopt.h"
35 #include "buddyicon.h"
37 #include "conversation.h"
50 #include "oscarcommon.h"
54 #define OSCAR_STATUS_ID_INVISIBLE "invisible"
55 #define OSCAR_STATUS_ID_OFFLINE "offline"
56 #define OSCAR_STATUS_ID_AVAILABLE "available"
57 #define OSCAR_STATUS_ID_AWAY "away"
58 #define OSCAR_STATUS_ID_DND "dnd"
59 #define OSCAR_STATUS_ID_NA "na"
60 #define OSCAR_STATUS_ID_OCCUPIED "occupied"
61 #define OSCAR_STATUS_ID_FREE4CHAT "free4chat"
62 #define OSCAR_STATUS_ID_CUSTOM "custom"
63 #define OSCAR_STATUS_ID_MOBILE "mobile"
65 #define AIMHASHDATA "http://pidgin.im/aim_data.php3"
67 #define OSCAR_CONNECT_STEPS 6
69 static OscarCapability purple_caps
= (OSCAR_CAPABILITY_CHAT
| OSCAR_CAPABILITY_BUDDYICON
| OSCAR_CAPABILITY_DIRECTIM
|
70 OSCAR_CAPABILITY_SENDFILE
| OSCAR_CAPABILITY_UNICODE
| OSCAR_CAPABILITY_INTEROPERATE
|
71 OSCAR_CAPABILITY_SHORTCAPS
| OSCAR_CAPABILITY_TYPING
);
73 static guint8 features_aim
[] = {0x01, 0x01, 0x01, 0x02};
74 static guint8 features_icq
[] = {0x01, 0x06};
75 static guint8 features_icq_offline
[] = {0x01};
76 static guint8 ck
[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
83 struct oscar_ask_directim_data
90 * Various PRPL-specific buddy info that we want to keep track of
91 * Some other info is maintained by locate.c, and I'd like to move
92 * the rest of this to libfaim, mostly im.c
94 * TODO: More of this should use the status API.
100 unsigned long ico_me_len
;
101 unsigned long ico_me_csum
;
103 gboolean ico_informed
;
105 unsigned long ico_len
;
106 unsigned long ico_csum
;
113 PurpleConnection
*gc
;
118 static const char * const msgerrreason
[] = {
122 N_("Rate to client"),
124 N_("Service unavailable"),
125 N_("Service not defined"),
127 N_("Not supported by host"),
128 N_("Not supported by client"),
129 N_("Refused by client"),
131 N_("Responses lost"),
132 N_("Request denied"),
133 N_("Busted SNAC payload"),
134 N_("Insufficient rights"),
135 N_("In local permit/deny"),
136 N_("Warning level too high (sender)"),
137 N_("Warning level too high (receiver)"),
138 N_("User temporarily unavailable"),
141 N_("Request ambiguous"),
143 N_("Not while on AOL")
145 static const int msgerrreasonlen
= G_N_ELEMENTS(msgerrreason
);
147 /* All the libfaim->purple callback functions */
149 /* Only used when connecting with the old-style BUCP login */
150 static int purple_parse_auth_resp (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
151 static int purple_parse_login (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
152 static int purple_parse_auth_securid_request(OscarData
*, FlapConnection
*, FlapFrame
*, ...);
154 static int purple_handle_redirect (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
155 static int purple_info_change (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
156 static int purple_account_confirm (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
157 static int purple_parse_oncoming (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
158 static int purple_parse_offgoing (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
159 static int purple_parse_incoming_im(OscarData
*, FlapConnection
*, FlapFrame
*, ...);
160 static int purple_parse_misses (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
161 static int purple_parse_clientauto (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
162 static int purple_parse_userinfo (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
163 static int purple_parse_motd (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
164 static int purple_chatnav_info (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
165 static int purple_conv_chat_join (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
166 static int purple_conv_chat_leave (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
167 static int purple_conv_chat_info_update (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
168 static int purple_conv_chat_incoming_msg(OscarData
*, FlapConnection
*, FlapFrame
*, ...);
169 static int purple_email_parseupdate(OscarData
*, FlapConnection
*, FlapFrame
*, ...);
170 static int purple_icon_parseicon (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
171 static int oscar_icon_req (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
172 static int purple_parse_msgack (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
173 static int purple_parse_ratechange (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
174 static int purple_parse_evilnotify (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
175 static int purple_parse_searcherror(OscarData
*, FlapConnection
*, FlapFrame
*, ...);
176 static int purple_parse_searchreply(OscarData
*, FlapConnection
*, FlapFrame
*, ...);
177 static int purple_bosrights (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
178 static int purple_connerr (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
179 static int purple_parse_msgerr (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
180 static int purple_parse_mtn (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
181 static int purple_parse_locaterights(OscarData
*, FlapConnection
*, FlapFrame
*, ...);
182 static int purple_parse_buddyrights(OscarData
*, FlapConnection
*, FlapFrame
*, ...);
183 static int purple_parse_locerr (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
184 static int purple_parse_genericerr (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
185 static int purple_memrequest (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
186 static int purple_selfinfo (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
187 #ifdef OLDSTYLE_ICQ_OFFLINEMSGS
188 static int purple_offlinemsg (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
189 static int purple_offlinemsgdone (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
190 #endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
191 static int purple_icqalias (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
192 static int purple_icqinfo (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
193 static int purple_popup (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
194 static int purple_ssi_parseerr (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
195 static int purple_ssi_parserights (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
196 static int purple_ssi_parselist (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
197 static int purple_ssi_parseack (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
198 static int purple_ssi_parseaddmod (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
199 static int purple_ssi_authgiven (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
200 static int purple_ssi_authrequest (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
201 static int purple_ssi_authreply (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
202 static int purple_ssi_gotadded (OscarData
*, FlapConnection
*, FlapFrame
*, ...);
204 static void purple_icons_fetch(PurpleConnection
*gc
);
206 void oscar_set_info(PurpleConnection
*gc
, const char *info
);
207 static void oscar_set_info_and_status(PurpleAccount
*account
, gboolean setinfo
, const char *rawinfo
, gboolean setstatus
, PurpleStatus
*status
);
208 static void oscar_set_extendedstatus(PurpleConnection
*gc
);
209 static gboolean
purple_ssi_rerequestdata(gpointer data
);
211 static void oscar_free_name_data(struct name_data
*data
) {
218 const char *oscar_get_locale_charset(void) {
219 static const char *charset
= NULL
;
221 g_get_charset(&charset
);
227 * Determine how we can send this message. Per the warnings elsewhere
228 * in this file, these little checks determine the simplest encoding
229 * we can use for a given message send using it.
232 oscar_charset_check(const char *utf8
)
235 int charset
= AIM_CHARSET_ASCII
;
238 * Can we get away with using our custom encoding?
242 if ((unsigned char)utf8
[i
] > 0x7f) {
244 charset
= AIM_CHARSET_CUSTOM
;
251 * Must we send this message as UNICODE (in the UTF-16BE encoding)?
255 /* ISO-8859-1 is 0x00-0xbf in the first byte
256 * followed by 0xc0-0xc3 in the second */
257 if ((unsigned char)utf8
[i
] < 0x80) {
260 } else if (((unsigned char)utf8
[i
] & 0xfc) == 0xc0 &&
261 ((unsigned char)utf8
[i
+ 1] & 0xc0) == 0x80) {
265 charset
= AIM_CHARSET_UNICODE
;
273 * Take a string of the form charset="bleh" where bleh is
274 * one of us-ascii, utf-8, iso-8859-1, or unicode-2-0, and
275 * return a newly allocated string containing bleh.
278 oscar_encoding_extract(const char *encoding
)
283 g_return_val_if_fail(encoding
!= NULL
, NULL
);
285 /* Make sure encoding begins with charset= */
286 if (strncmp(encoding
, "text/aolrtf; charset=", 21) &&
287 strncmp(encoding
, "text/x-aolrtf; charset=", 23) &&
288 strncmp(encoding
, "text/plain; charset=", 20))
293 begin
= strchr(encoding
, '"');
294 end
= strrchr(encoding
, '"');
296 if ((begin
== NULL
) || (end
== NULL
) || (begin
>= end
))
299 ret
= g_strndup(begin
+1, (end
-1) - begin
);
305 oscar_encoding_to_utf8(PurpleAccount
*account
, const char *encoding
, const char *text
, int textlen
)
309 if ((encoding
== NULL
) || encoding
[0] == '\0') {
310 purple_debug_info("oscar", "Empty encoding, assuming UTF-8\n");
311 } else if (!g_ascii_strcasecmp(encoding
, "iso-8859-1")) {
312 utf8
= g_convert(text
, textlen
, "UTF-8", "iso-8859-1", NULL
, NULL
, NULL
);
313 } else if (!g_ascii_strcasecmp(encoding
, "ISO-8859-1-Windows-3.1-Latin-1") ||
314 !g_ascii_strcasecmp(encoding
, "us-ascii"))
316 utf8
= g_convert(text
, textlen
, "UTF-8", "Windows-1252", NULL
, NULL
, NULL
);
317 } else if (!g_ascii_strcasecmp(encoding
, "unicode-2-0")) {
318 /* Some official ICQ clients are apparently total crack,
319 * and have been known to save a UTF-8 string converted
320 * from the locale character set to UTF-16 (not from UTF-8
321 * to UTF-16!) in the away message. This hack should find
322 * and do something (un)reasonable with that, and not
323 * mess up too much else. */
324 const gchar
*charset
= purple_account_get_string(account
, "encoding", NULL
);
327 utf8
= g_convert(text
, textlen
, charset
, "UTF-16BE", &len
, NULL
, NULL
);
328 if (!utf8
|| len
!= textlen
|| !g_utf8_validate(utf8
, -1, NULL
)) {
332 purple_debug_info("oscar", "Used broken ICQ fallback encoding\n");
336 utf8
= g_convert(text
, textlen
, "UTF-8", "UTF-16BE", NULL
, NULL
, NULL
);
337 } else if (g_ascii_strcasecmp(encoding
, "utf-8")) {
338 purple_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
339 "attempting to convert to UTF-8 anyway\n", encoding
);
340 utf8
= g_convert(text
, textlen
, "UTF-8", encoding
, NULL
, NULL
, NULL
);
344 * If utf8 is still NULL then either the encoding is utf-8 or
345 * we have been unable to convert the text to utf-8 from the encoding
346 * that was specified. So we check if the text is valid utf-8 then
350 if (textlen
!= 0 && *text
!= '\0'
351 && !g_utf8_validate(text
, textlen
, NULL
))
352 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.)"));
354 utf8
= g_strndup(text
, textlen
);
361 oscar_utf8_try_convert(PurpleAccount
*account
, const gchar
*msg
)
363 const char *charset
= NULL
;
366 if(oscar_util_valid_name_icq(purple_account_get_username(account
)))
367 charset
= purple_account_get_string(account
, "encoding", NULL
);
369 if(charset
&& *charset
)
370 ret
= g_convert(msg
, -1, "UTF-8", charset
, NULL
, NULL
, NULL
);
373 ret
= purple_utf8_try_convert(msg
);
379 purple_plugin_oscar_convert_to_utf8(const gchar
*data
, gsize datalen
, const char *charsetstr
, gboolean fallback
)
384 if ((charsetstr
== NULL
) || (*charsetstr
== '\0'))
387 if (g_ascii_strcasecmp("UTF-8", charsetstr
)) {
389 ret
= g_convert_with_fallback(data
, datalen
, "UTF-8", charsetstr
, "?", NULL
, NULL
, &err
);
391 ret
= g_convert(data
, datalen
, "UTF-8", charsetstr
, NULL
, NULL
, &err
);
393 purple_debug_warning("oscar", "Conversion from %s failed: %s.\n",
394 charsetstr
, err
->message
);
398 if (g_utf8_validate(data
, datalen
, NULL
))
399 ret
= g_strndup(data
, datalen
);
401 purple_debug_warning("oscar", "String is not valid UTF-8.\n");
408 * This attemps to decode an incoming IM into a UTF8 string.
410 * We try decoding using two different character sets. The charset
411 * specified in the IM determines the order in which we attempt to
412 * decode. We do this because there are lots of broken ICQ clients
413 * that don't correctly send non-ASCII messages. And if Purple isn't
414 * able to deal with that crap, then people complain like banshees.
415 * charsetstr1 is always set to what the correct encoding should be.
418 purple_plugin_oscar_decode_im_part(PurpleAccount
*account
, const char *sourcebn
, guint16 charset
, guint16 charsubset
, const gchar
*data
, gsize datalen
)
421 const gchar
*charsetstr1
, *charsetstr2
;
423 purple_debug_info("oscar", "Parsing IM part, charset=0x%04hx, charsubset=0x%04hx, datalen=%" G_GSIZE_FORMAT
"\n", charset
, charsubset
, datalen
);
425 if ((datalen
== 0) || (data
== NULL
))
428 if (charset
== AIM_CHARSET_UNICODE
) {
429 charsetstr1
= "UTF-16BE";
430 charsetstr2
= "UTF-8";
431 } else if (charset
== AIM_CHARSET_CUSTOM
) {
432 if ((sourcebn
!= NULL
) && oscar_util_valid_name_icq(sourcebn
))
433 charsetstr1
= purple_account_get_string(account
, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING
);
435 charsetstr1
= "ISO-8859-1";
436 charsetstr2
= "UTF-8";
437 } else if (charset
== AIM_CHARSET_ASCII
) {
438 /* Should just be "ASCII" */
439 charsetstr1
= "ASCII";
440 charsetstr2
= purple_account_get_string(account
, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING
);
441 } else if (charset
== 0x000d) {
442 /* Mobile AIM client on a Nokia 3100 and an LG VX6000 */
443 charsetstr1
= "ISO-8859-1";
444 charsetstr2
= purple_account_get_string(account
, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING
);
446 /* Unknown, hope for valid UTF-8... */
447 charsetstr1
= "UTF-8";
448 charsetstr2
= purple_account_get_string(account
, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING
);
451 ret
= purple_plugin_oscar_convert_to_utf8(data
, datalen
, charsetstr1
, FALSE
);
453 ret
= purple_plugin_oscar_convert_to_utf8(data
, datalen
, charsetstr2
, TRUE
);
455 char *str
, *salvage
, *tmp
;
457 str
= g_malloc(datalen
+ 1);
458 strncpy(str
, data
, datalen
);
460 salvage
= purple_utf8_salvage(str
);
461 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.)"),
463 ret
= g_strdup_printf("%s %s", salvage
, tmp
);
473 * Figure out what encoding to use when sending a given outgoing message.
476 purple_plugin_oscar_convert_to_best_encoding(PurpleConnection
*gc
,
477 const char *destbn
, const gchar
*from
,
478 gchar
**msg
, int *msglen_int
,
479 guint16
*charset
, guint16
*charsubset
)
481 OscarData
*od
= purple_connection_get_protocol_data(gc
);
482 PurpleAccount
*account
= purple_connection_get_account(gc
);
484 aim_userinfo_t
*userinfo
= NULL
;
485 const gchar
*charsetstr
;
488 /* Attempt to send as ASCII */
489 if (oscar_charset_check(from
) == AIM_CHARSET_ASCII
) {
490 *msg
= g_convert(from
, -1, "ASCII", "UTF-8", NULL
, &msglen
, NULL
);
491 *charset
= AIM_CHARSET_ASCII
;
492 *charsubset
= 0x0000;
493 *msglen_int
= msglen
;
498 * If we're sending to an ICQ user, and they are in our
499 * buddy list, and they are advertising the Unicode
500 * capability, and they are online, then attempt to send
503 if ((destbn
!= NULL
) && oscar_util_valid_name_icq(destbn
))
504 userinfo
= aim_locate_finduserinfo(od
, destbn
);
506 if ((userinfo
!= NULL
) && (userinfo
->capabilities
& OSCAR_CAPABILITY_UNICODE
))
509 b
= purple_find_buddy(account
, destbn
);
510 if ((b
!= NULL
) && (PURPLE_BUDDY_IS_ONLINE(b
)))
512 *msg
= g_convert(from
, -1, "UTF-16BE", "UTF-8", NULL
, &msglen
, &err
);
515 *charset
= AIM_CHARSET_UNICODE
;
516 *charsubset
= 0x0000;
517 *msglen_int
= msglen
;
521 purple_debug_error("oscar", "Conversion from UTF-8 to UTF-16BE failed: %s.\n",
529 * If this is AIM then attempt to send as ISO-8859-1. If this is
530 * ICQ then attempt to send as the user specified character encoding.
532 charsetstr
= "ISO-8859-1";
533 if ((destbn
!= NULL
) && oscar_util_valid_name_icq(destbn
))
534 charsetstr
= purple_account_get_string(account
, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING
);
537 * XXX - We need a way to only attempt to convert if we KNOW "from"
538 * can be converted to "charsetstr"
540 *msg
= g_convert(from
, -1, charsetstr
, "UTF-8", NULL
, &msglen
, &err
);
542 *charset
= AIM_CHARSET_CUSTOM
;
543 *charsubset
= 0x0000;
544 *msglen_int
= msglen
;
548 purple_debug_info("oscar", "Conversion from UTF-8 to %s failed (%s), falling back to unicode.\n",
549 charsetstr
, err
->message
);
554 * Nothing else worked, so send as UTF-16BE.
556 *msg
= g_convert(from
, -1, "UTF-16BE", "UTF-8", NULL
, &msglen
, &err
);
558 *charset
= AIM_CHARSET_UNICODE
;
559 *charsubset
= 0x0000;
560 *msglen_int
= msglen
;
564 purple_debug_error("oscar", "Error converting a Unicode message: %s\n", err
->message
);
568 purple_debug_error("oscar", "This should NEVER happen! Sending UTF-8 text flagged as ASCII.\n");
569 *msg
= g_strdup(from
);
570 *msglen_int
= strlen(*msg
);
571 *charset
= AIM_CHARSET_ASCII
;
572 *charsubset
= 0x0000;
577 * Looks for %n, %d, or %t in a string, and replaces them with the
578 * specified name, date, and time, respectively.
580 * @param str The string that may contain the special variables.
581 * @param name The sender name.
583 * @return A newly allocated string where the special variables are
584 * expanded. This should be g_free'd by the caller.
587 purple_str_sub_away_formatters(const char *str
, const char *name
)
594 g_return_val_if_fail(str
!= NULL
, NULL
);
595 g_return_val_if_fail(name
!= NULL
, NULL
);
597 /* Create an empty GString that is hopefully big enough for most messages */
598 cpy
= g_string_sized_new(1024);
611 g_string_append(cpy
, name
);
616 g_string_append(cpy
, purple_date_format_short(tme
));
621 g_string_append(cpy
, purple_time_format(tme
));
625 g_string_append_c(cpy
, *c
);
628 g_string_append_c(cpy
, *c
);
632 g_string_append_c(cpy
, *c
);
637 return g_string_free(cpy
, FALSE
);
640 static gchar
*oscar_caps_to_string(OscarCapability caps
)
646 str
= g_string_new("");
650 } else while (bit
<= OSCAR_CAPABILITY_LAST
) {
653 case OSCAR_CAPABILITY_BUDDYICON
:
654 tmp
= _("Buddy Icon");
656 case OSCAR_CAPABILITY_TALK
:
659 case OSCAR_CAPABILITY_DIRECTIM
:
660 tmp
= _("AIM Direct IM");
662 case OSCAR_CAPABILITY_CHAT
:
665 case OSCAR_CAPABILITY_GETFILE
:
668 case OSCAR_CAPABILITY_SENDFILE
:
669 tmp
= _("Send File");
671 case OSCAR_CAPABILITY_GAMES
:
672 case OSCAR_CAPABILITY_GAMES2
:
675 case OSCAR_CAPABILITY_ADDINS
:
678 case OSCAR_CAPABILITY_SENDBUDDYLIST
:
679 tmp
= _("Send Buddy List");
681 case OSCAR_CAPABILITY_ICQ_DIRECT
:
682 tmp
= _("ICQ Direct Connect");
684 case OSCAR_CAPABILITY_APINFO
:
687 case OSCAR_CAPABILITY_ICQRTF
:
690 case OSCAR_CAPABILITY_EMPTY
:
693 case OSCAR_CAPABILITY_ICQSERVERRELAY
:
694 tmp
= _("ICQ Server Relay");
696 case OSCAR_CAPABILITY_UNICODEOLD
:
697 tmp
= _("Old ICQ UTF8");
699 case OSCAR_CAPABILITY_TRILLIANCRYPT
:
700 tmp
= _("Trillian Encryption");
702 case OSCAR_CAPABILITY_UNICODE
:
705 case OSCAR_CAPABILITY_HIPTOP
:
708 case OSCAR_CAPABILITY_SECUREIM
:
709 tmp
= _("Security Enabled");
711 case OSCAR_CAPABILITY_VIDEO
:
712 tmp
= _("Video Chat");
714 /* Not actually sure about this one... WinAIM doesn't show anything */
715 case OSCAR_CAPABILITY_ICHATAV
:
718 case OSCAR_CAPABILITY_LIVEVIDEO
:
719 tmp
= _("Live Video");
721 case OSCAR_CAPABILITY_CAMERA
:
724 case OSCAR_CAPABILITY_ICHAT_SCREENSHARE
:
725 tmp
= _("Screen Sharing");
732 g_string_append_printf(str
, "%s%s", (*(str
->str
) == '\0' ? "" : ", "), tmp
);
737 return g_string_free(str
, FALSE
);
740 static char *oscar_icqstatus(int state
) {
741 /* Make a cute little string that shows the status of the dude or dudet */
742 if (state
& AIM_ICQ_STATE_CHAT
)
743 return g_strdup(_("Free For Chat"));
744 else if (state
& AIM_ICQ_STATE_DND
)
745 return g_strdup(_("Do Not Disturb"));
746 else if (state
& AIM_ICQ_STATE_OUT
)
747 return g_strdup(_("Not Available"));
748 else if (state
& AIM_ICQ_STATE_BUSY
)
749 return g_strdup(_("Occupied"));
750 else if (state
& AIM_ICQ_STATE_AWAY
)
751 return g_strdup(_("Away"));
752 else if (state
& AIM_ICQ_STATE_WEBAWARE
)
753 return g_strdup(_("Web Aware"));
754 else if (state
& AIM_ICQ_STATE_INVISIBLE
)
755 return g_strdup(_("Invisible"));
757 return g_strdup(_("Online"));
761 oscar_user_info_add_pair(PurpleNotifyUserInfo
*user_info
, const char *name
, const char *value
)
763 if (value
&& value
[0]) {
764 purple_notify_user_info_add_pair(user_info
, name
, value
);
769 oscar_user_info_convert_and_add_pair(PurpleAccount
*account
, PurpleNotifyUserInfo
*user_info
,
770 const char *name
, const char *value
)
774 if (value
&& value
[0] && (utf8
= oscar_utf8_try_convert(account
, value
))) {
775 purple_notify_user_info_add_pair(user_info
, name
, utf8
);
781 oscar_user_info_convert_and_add(PurpleAccount
*account
, PurpleNotifyUserInfo
*user_info
,
782 const char *name
, const char *value
)
786 if (value
&& value
[0] && (utf8
= oscar_utf8_try_convert(account
, value
))) {
787 purple_notify_user_info_add_pair(user_info
, name
, utf8
);
793 * @brief Append the status information to a user_info struct
795 * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
797 * @param gc The PurpleConnection
798 * @param user_info A PurpleNotifyUserInfo object to which status information will be added
799 * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status().
800 * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status().
801 * @param strip_html_tags If strip_html_tags is TRUE, tags embedded in the status message will be stripped, returning a non-formatted string. The string will still be HTML escaped.
803 static void oscar_user_info_append_status(PurpleConnection
*gc
, PurpleNotifyUserInfo
*user_info
, PurpleBuddy
*b
, aim_userinfo_t
*userinfo
, gboolean strip_html_tags
)
805 PurpleAccount
*account
= purple_connection_get_account(gc
);
807 PurplePresence
*presence
= NULL
;
808 PurpleStatus
*status
= NULL
;
809 gchar
*message
= NULL
, *itmsurl
= NULL
, *tmp
;
812 od
= purple_connection_get_protocol_data(gc
);
814 if (b
== NULL
&& userinfo
== NULL
)
818 b
= purple_find_buddy(purple_connection_get_account(gc
), userinfo
->bn
);
820 userinfo
= aim_locate_finduserinfo(od
, purple_buddy_get_name(b
));
823 presence
= purple_buddy_get_presence(b
);
824 status
= purple_presence_get_active_status(presence
);
827 /* If we have both b and userinfo we favor userinfo, because if we're
828 viewing someone's profile then we want the HTML away message, and
829 the "message" attribute of the status contains only the plaintext
832 if ((userinfo
->flags
& AIM_FLAG_AWAY
)
833 && userinfo
->away_len
> 0
834 && userinfo
->away
!= NULL
835 && userinfo
->away_encoding
!= NULL
)
838 tmp
= oscar_encoding_extract(userinfo
->away_encoding
);
839 message
= oscar_encoding_to_utf8(account
,
840 tmp
, userinfo
->away
, userinfo
->away_len
);
844 * Available message or non-HTML away message (because that's
845 * all we have right now.
847 if ((userinfo
->status
!= NULL
) && userinfo
->status
[0] != '\0') {
848 message
= oscar_encoding_to_utf8(account
,
849 userinfo
->status_encoding
, userinfo
->status
,
850 userinfo
->status_len
);
852 #if defined (_WIN32) || defined (__APPLE__)
853 if (userinfo
->itmsurl
&& (userinfo
->itmsurl
[0] != '\0'))
854 itmsurl
= oscar_encoding_to_utf8(account
, userinfo
->itmsurl_encoding
,
855 userinfo
->itmsurl
, userinfo
->itmsurl_len
);
859 message
= g_strdup(purple_status_get_attr_string(status
, "message"));
860 itmsurl
= g_strdup(purple_status_get_attr_string(status
, "itmsurl"));
863 is_away
= ((status
&& !purple_status_is_available(status
)) ||
864 (userinfo
&& (userinfo
->flags
& AIM_FLAG_AWAY
)));
866 if (strip_html_tags
) {
867 /* Away messges are HTML, but available messages were originally plain text.
868 * We therefore need to strip away messages but not available messages if we're asked to remove HTML tags.
870 if (is_away
&& message
) {
872 tmp
= purple_markup_strip_html(message
);
874 tmp2
= g_markup_escape_text(tmp
, -1);
881 tmp
= g_strdup_printf("<a href=\"%s\">%s</a>",
889 if (is_away
&& message
) {
890 tmp
= purple_str_sub_away_formatters(message
, purple_account_get_username(account
));
896 if (purple_presence_is_online(presence
)) {
897 if (oscar_util_valid_name_icq(purple_buddy_get_name(b
)) || is_away
|| !message
|| !(*message
)) {
898 /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
899 * If the status name and the message are the same, only show one. */
900 const char *status_name
= purple_status_get_name(status
);
901 if (status_name
&& message
&& !strcmp(status_name
, message
))
904 tmp
= g_strdup_printf("%s%s%s",
905 status_name
? status_name
: "",
906 ((status_name
&& message
) && *message
) ? ": " : "",
907 (message
&& *message
) ? message
: "");
912 } else if (aim_ssi_waitingforauth(od
->ssi
.local
,
913 aim_ssi_itemlist_findparentname(od
->ssi
.local
, purple_buddy_get_name(b
)),
914 purple_buddy_get_name(b
)))
916 /* Note if an offline buddy is not authorized */
917 tmp
= g_strdup_printf("%s%s%s",
919 (message
&& *message
) ? ": " : "",
920 (message
&& *message
) ? message
: "");
925 message
= g_strdup(_("Offline"));
929 purple_notify_user_info_add_pair(user_info
, _("Status"), message
);
933 static void oscar_user_info_append_extra_info(PurpleConnection
*gc
, PurpleNotifyUserInfo
*user_info
, PurpleBuddy
*b
, aim_userinfo_t
*userinfo
)
936 PurpleAccount
*account
;
937 PurplePresence
*presence
= NULL
;
938 PurpleStatus
*status
= NULL
;
939 PurpleGroup
*g
= NULL
;
940 struct buddyinfo
*bi
= NULL
;
942 const char *bname
= NULL
, *gname
= NULL
;
944 od
= purple_connection_get_protocol_data(gc
);
945 account
= purple_connection_get_account(gc
);
947 if ((user_info
== NULL
) || ((b
== NULL
) && (userinfo
== NULL
)))
950 if (userinfo
== NULL
)
951 userinfo
= aim_locate_finduserinfo(od
, purple_buddy_get_name(b
));
954 b
= purple_find_buddy(account
, userinfo
->bn
);
957 bname
= purple_buddy_get_name(b
);
958 g
= purple_buddy_get_group(b
);
959 gname
= purple_group_get_name(g
);
960 presence
= purple_buddy_get_presence(b
);
961 status
= purple_presence_get_active_status(presence
);
964 if (userinfo
!= NULL
)
965 bi
= g_hash_table_lookup(od
->buddyinfo
, purple_normalize(account
, userinfo
->bn
));
967 if ((bi
!= NULL
) && (bi
->ipaddr
!= 0)) {
968 tmp
= g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
969 (bi
->ipaddr
& 0xff000000) >> 24,
970 (bi
->ipaddr
& 0x00ff0000) >> 16,
971 (bi
->ipaddr
& 0x0000ff00) >> 8,
972 (bi
->ipaddr
& 0x000000ff));
973 oscar_user_info_add_pair(user_info
, _("IP Address"), tmp
);
977 if ((userinfo
!= NULL
) && (userinfo
->warnlevel
!= 0)) {
978 tmp
= g_strdup_printf("%d", (int)(userinfo
->warnlevel
/10.0 + .5));
979 oscar_user_info_add_pair(user_info
, _("Warning Level"), tmp
);
983 if ((b
!= NULL
) && (bname
!= NULL
) && (g
!= NULL
) && (gname
!= NULL
)) {
984 tmp
= aim_ssi_getcomment(od
->ssi
.local
, gname
, bname
);
986 char *tmp2
= g_markup_escape_text(tmp
, strlen(tmp
));
989 oscar_user_info_convert_and_add_pair(account
, user_info
, _("Buddy Comment"), tmp2
);
995 static char *extract_name(const char *name
) {
1002 x
= strchr(name
, '-');
1006 x
= strchr(x
+ 1, '-');
1010 tmp
= g_strdup(++x
);
1012 for (i
= 0, j
= 0; x
[i
]; i
++) {
1018 strncpy(hex
, x
+ ++i
, 2);
1021 tmp
[j
++] = strtol(hex
, NULL
, 16);
1028 static struct chat_connection
*
1029 find_oscar_chat(PurpleConnection
*gc
, int id
)
1031 OscarData
*od
= purple_connection_get_protocol_data(gc
);
1033 struct chat_connection
*cc
;
1035 for (cur
= od
->oscar_chats
; cur
!= NULL
; cur
= cur
->next
)
1037 cc
= (struct chat_connection
*)cur
->data
;
1045 static struct chat_connection
*
1046 find_oscar_chat_by_conn(PurpleConnection
*gc
, FlapConnection
*conn
)
1048 OscarData
*od
= purple_connection_get_protocol_data(gc
);
1050 struct chat_connection
*cc
;
1052 for (cur
= od
->oscar_chats
; cur
!= NULL
; cur
= cur
->next
)
1054 cc
= (struct chat_connection
*)cur
->data
;
1055 if (cc
->conn
== conn
)
1062 static struct chat_connection
*
1063 find_oscar_chat_by_conv(PurpleConnection
*gc
, PurpleConversation
*conv
)
1065 OscarData
*od
= purple_connection_get_protocol_data(gc
);
1067 struct chat_connection
*cc
;
1069 for (cur
= od
->oscar_chats
; cur
!= NULL
; cur
= cur
->next
)
1071 cc
= (struct chat_connection
*)cur
->data
;
1072 if (cc
->conv
== conv
)
1080 oscar_chat_destroy(struct chat_connection
*cc
)
1088 oscar_chat_kill(PurpleConnection
*gc
, struct chat_connection
*cc
)
1090 OscarData
*od
= purple_connection_get_protocol_data(gc
);
1092 /* Notify the conversation window that we've left the chat */
1093 serv_got_chat_left(gc
, purple_conv_chat_get_id(PURPLE_CONV_CHAT(cc
->conv
)));
1095 /* Destroy the chat_connection */
1096 od
->oscar_chats
= g_slist_remove(od
->oscar_chats
, cc
);
1097 flap_connection_schedule_destroy(cc
->conn
, OSCAR_DISCONNECT_DONE
, NULL
);
1098 oscar_chat_destroy(cc
);
1102 * This is called from the callback functions for establishing
1103 * a TCP connection with an oscar host if an error occurred.
1106 connection_common_error_cb(FlapConnection
*conn
, const gchar
*error_message
)
1109 PurpleConnection
*gc
;
1114 purple_debug_error("oscar", "unable to connect to FLAP "
1115 "server of type 0x%04hx\n", conn
->type
);
1117 if (conn
->type
== SNAC_FAMILY_AUTH
)
1119 /* This only happens when connecting with the old-style BUCP login */
1121 msg
= g_strdup_printf(_("Unable to connect to authentication server: %s"),
1123 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, msg
);
1126 else if (conn
->type
== SNAC_FAMILY_LOCATE
)
1129 msg
= g_strdup_printf(_("Unable to connect to BOS server: %s"),
1131 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, msg
);
1136 /* Maybe we should call this for BOS connections, too? */
1137 flap_connection_schedule_destroy(conn
,
1138 OSCAR_DISCONNECT_COULD_NOT_CONNECT
, error_message
);
1143 * This is called from the callback functions for establishing
1144 * a TCP connection with an oscar host. Depending on the type
1145 * of host, we do a few different things here.
1148 connection_common_established_cb(FlapConnection
*conn
)
1151 PurpleConnection
*gc
;
1152 PurpleAccount
*account
;
1156 account
= purple_connection_get_account(gc
);
1158 purple_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n",
1161 if (conn
->cookie
== NULL
)
1162 flap_connection_send_version(od
, conn
);
1165 if (purple_account_get_bool(account
, "use_clientlogin", OSCAR_DEFAULT_USE_CLIENTLOGIN
))
1167 ClientInfo aiminfo
= CLIENTINFO_PURPLE_AIM
;
1168 ClientInfo icqinfo
= CLIENTINFO_PURPLE_ICQ
;
1169 flap_connection_send_version_with_cookie_and_clientinfo(od
,
1170 conn
, conn
->cookielen
, conn
->cookie
,
1171 od
->icq
? &icqinfo
: &aiminfo
);
1173 flap_connection_send_version_with_cookie(od
, conn
,
1174 conn
->cookielen
, conn
->cookie
);
1178 g_free(conn
->cookie
);
1179 conn
->cookie
= NULL
;
1182 if (conn
->type
== SNAC_FAMILY_AUTH
)
1184 /* This only happens when connecting with the old-style BUCP login */
1185 aim_request_login(od
, conn
, purple_account_get_username(account
));
1186 purple_debug_info("oscar", "Username sent, waiting for response\n");
1187 purple_connection_update_progress(gc
, _("Username sent"), 1, OSCAR_CONNECT_STEPS
);
1190 else if (conn
->type
== SNAC_FAMILY_LOCATE
)
1192 purple_connection_update_progress(gc
, _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS
);
1195 else if (conn
->type
== SNAC_FAMILY_CHAT
)
1197 od
->oscar_chats
= g_slist_prepend(od
->oscar_chats
, conn
->new_conn_data
);
1198 conn
->new_conn_data
= NULL
;
1203 connection_established_cb(gpointer data
, gint source
, const gchar
*error_message
)
1205 FlapConnection
*conn
;
1209 conn
->connect_data
= NULL
;
1214 connection_common_error_cb(conn
, error_message
);
1218 conn
->watcher_incoming
= purple_input_add(conn
->fd
,
1219 PURPLE_INPUT_READ
, flap_connection_recv_cb
, conn
);
1220 connection_common_established_cb(conn
);
1224 ssl_connection_established_cb(gpointer data
, PurpleSslConnection
*gsc
,
1225 PurpleInputCondition cond
)
1227 FlapConnection
*conn
;
1231 purple_ssl_input_add(gsc
, flap_connection_recv_cb_ssl
, conn
);
1232 connection_common_established_cb(conn
);
1236 ssl_connection_error_cb(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
1239 FlapConnection
*conn
;
1243 if (conn
->watcher_outgoing
)
1245 purple_input_remove(conn
->watcher_outgoing
);
1246 conn
->watcher_outgoing
= 0;
1249 /* sslconn frees the connection on error */
1252 connection_common_error_cb(conn
, purple_ssl_strerror(error
));
1256 flap_connection_established_bos(OscarData
*od
, FlapConnection
*conn
)
1258 PurpleConnection
*gc
= od
->gc
;
1260 aim_srv_reqpersonalinfo(od
, conn
);
1262 purple_debug_info("oscar", "ssi: requesting rights and list\n");
1263 aim_ssi_reqrights(od
);
1264 aim_ssi_reqdata(od
);
1265 if (od
->getblisttimer
> 0)
1266 purple_timeout_remove(od
->getblisttimer
);
1267 od
->getblisttimer
= purple_timeout_add_seconds(30, purple_ssi_rerequestdata
, od
);
1269 aim_locate_reqrights(od
);
1270 aim_buddylist_reqrights(od
, conn
);
1271 aim_im_reqparams(od
);
1272 aim_bos_reqrights(od
, conn
); /* TODO: Don't call this with ssi */
1274 purple_connection_update_progress(gc
, _("Finalizing connection"), 5, OSCAR_CONNECT_STEPS
);
1278 flap_connection_established_admin(OscarData
*od
, FlapConnection
*conn
)
1280 aim_srv_clientready(od
, conn
);
1281 purple_debug_info("oscar", "connected to admin\n");
1284 purple_debug_info("oscar", "changing password\n");
1285 aim_admin_changepasswd(od
, conn
, od
->newp
, od
->oldp
);
1293 purple_debug_info("oscar", "formatting username\n");
1294 aim_admin_setnick(od
, conn
, od
->newformatting
);
1295 g_free(od
->newformatting
);
1296 od
->newformatting
= NULL
;
1297 od
->setnick
= FALSE
;
1300 purple_debug_info("oscar", "confirming account\n");
1301 aim_admin_reqconfirm(od
, conn
);
1305 purple_debug_info("oscar", "requesting email address\n");
1306 aim_admin_getinfo(od
, conn
, 0x0011);
1307 od
->reqemail
= FALSE
;
1310 purple_debug_info("oscar", "setting email address\n");
1311 aim_admin_setemail(od
, conn
, od
->email
);
1314 od
->setemail
= FALSE
;
1319 flap_connection_established_chat(OscarData
*od
, FlapConnection
*conn
)
1321 PurpleConnection
*gc
= od
->gc
;
1322 struct chat_connection
*chatcon
;
1325 aim_srv_clientready(od
, conn
);
1327 chatcon
= find_oscar_chat_by_conn(gc
, conn
);
1330 chatcon
->conv
= serv_got_joined_chat(gc
, id
++, chatcon
->show
);
1335 flap_connection_established_chatnav(OscarData
*od
, FlapConnection
*conn
)
1337 aim_srv_clientready(od
, conn
);
1338 aim_chatnav_reqrights(od
, conn
);
1342 flap_connection_established_alert(OscarData
*od
, FlapConnection
*conn
)
1344 aim_email_sendcookies(od
);
1345 aim_email_activate(od
);
1346 aim_srv_clientready(od
, conn
);
1350 flap_connection_established_bart(OscarData
*od
, FlapConnection
*conn
)
1352 PurpleConnection
*gc
= od
->gc
;
1354 aim_srv_clientready(od
, conn
);
1356 od
->iconconnecting
= FALSE
;
1358 purple_icons_fetch(gc
);
1362 flap_connection_established(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
1364 purple_debug_info("oscar", "FLAP connection of type 0x%04hx is "
1365 "now fully connected\n", conn
->type
);
1366 if (conn
->type
== SNAC_FAMILY_LOCATE
)
1367 flap_connection_established_bos(od
, conn
);
1368 else if (conn
->type
== SNAC_FAMILY_ADMIN
)
1369 flap_connection_established_admin(od
, conn
);
1370 else if (conn
->type
== SNAC_FAMILY_CHAT
)
1371 flap_connection_established_chat(od
, conn
);
1372 else if (conn
->type
== SNAC_FAMILY_CHATNAV
)
1373 flap_connection_established_chatnav(od
, conn
);
1374 else if (conn
->type
== SNAC_FAMILY_ALERT
)
1375 flap_connection_established_alert(od
, conn
);
1376 else if (conn
->type
== SNAC_FAMILY_BART
)
1377 flap_connection_established_bart(od
, conn
);
1383 idle_reporting_pref_cb(const char *name
, PurplePrefType type
,
1384 gconstpointer value
, gpointer data
)
1386 PurpleConnection
*gc
;
1388 gboolean report_idle
;
1392 od
= purple_connection_get_protocol_data(gc
);
1393 report_idle
= strcmp((const char *)value
, "none") != 0;
1394 presence
= aim_ssi_getpresence(od
->ssi
.local
);
1397 aim_ssi_setpresence(od
, presence
| 0x400);
1399 aim_ssi_setpresence(od
, presence
& ~0x400);
1403 * Should probably make a "Use recent buddies group" account preference
1404 * so that this option is surfaced to the user.
1407 recent_buddies_pref_cb(const char *name
, PurplePrefType type
,
1408 gconstpointer value
, gpointer data
)
1410 PurpleConnection
*gc
;
1415 od
= purple_connection_get_protocol_data(gc
);
1416 presence
= aim_ssi_getpresence(od
->ssi
.local
);
1419 aim_ssi_setpresence(od
, presence
& ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES
);
1421 aim_ssi_setpresence(od
, presence
| AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES
);
1425 oscar_login(PurpleAccount
*account
)
1427 PurpleConnection
*gc
;
1430 gc
= purple_account_get_connection(account
);
1431 od
= oscar_data_new();
1433 purple_connection_set_protocol_data(gc
, od
);
1435 oscar_data_addhandler(od
, AIM_CB_FAM_SPECIAL
, AIM_CB_SPECIAL_CONNERR
, purple_connerr
, 0);
1436 oscar_data_addhandler(od
, AIM_CB_FAM_SPECIAL
, AIM_CB_SPECIAL_CONNINITDONE
, flap_connection_established
, 0);
1438 oscar_data_addhandler(od
, SNAC_FAMILY_ADMIN
, 0x0003, purple_info_change
, 0);
1439 oscar_data_addhandler(od
, SNAC_FAMILY_ADMIN
, 0x0005, purple_info_change
, 0);
1440 oscar_data_addhandler(od
, SNAC_FAMILY_ADMIN
, 0x0007, purple_account_confirm
, 0);
1441 oscar_data_addhandler(od
, SNAC_FAMILY_ALERT
, 0x0001, purple_parse_genericerr
, 0);
1442 oscar_data_addhandler(od
, SNAC_FAMILY_ALERT
, SNAC_SUBTYPE_ALERT_MAILSTATUS
, purple_email_parseupdate
, 0);
1444 /* These are only needed when connecting with the old-style BUCP login */
1445 oscar_data_addhandler(od
, SNAC_FAMILY_AUTH
, 0x0003, purple_parse_auth_resp
, 0);
1446 oscar_data_addhandler(od
, SNAC_FAMILY_AUTH
, 0x0007, purple_parse_login
, 0);
1447 oscar_data_addhandler(od
, SNAC_FAMILY_AUTH
, SNAC_SUBTYPE_AUTH_SECURID_REQUEST
, purple_parse_auth_securid_request
, 0);
1449 oscar_data_addhandler(od
, SNAC_FAMILY_BART
, SNAC_SUBTYPE_BART_RESPONSE
, purple_icon_parseicon
, 0);
1450 oscar_data_addhandler(od
, SNAC_FAMILY_BOS
, 0x0001, purple_parse_genericerr
, 0);
1451 oscar_data_addhandler(od
, SNAC_FAMILY_BOS
, 0x0003, purple_bosrights
, 0);
1452 oscar_data_addhandler(od
, SNAC_FAMILY_BUDDY
, 0x0001, purple_parse_genericerr
, 0);
1453 oscar_data_addhandler(od
, SNAC_FAMILY_BUDDY
, SNAC_SUBTYPE_BUDDY_RIGHTSINFO
, purple_parse_buddyrights
, 0);
1454 oscar_data_addhandler(od
, SNAC_FAMILY_BUDDY
, SNAC_SUBTYPE_BUDDY_ONCOMING
, purple_parse_oncoming
, 0);
1455 oscar_data_addhandler(od
, SNAC_FAMILY_BUDDY
, SNAC_SUBTYPE_BUDDY_OFFGOING
, purple_parse_offgoing
, 0);
1456 oscar_data_addhandler(od
, SNAC_FAMILY_CHAT
, 0x0001, purple_parse_genericerr
, 0);
1457 oscar_data_addhandler(od
, SNAC_FAMILY_CHAT
, SNAC_SUBTYPE_CHAT_USERJOIN
, purple_conv_chat_join
, 0);
1458 oscar_data_addhandler(od
, SNAC_FAMILY_CHAT
, SNAC_SUBTYPE_CHAT_USERLEAVE
, purple_conv_chat_leave
, 0);
1459 oscar_data_addhandler(od
, SNAC_FAMILY_CHAT
, SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE
, purple_conv_chat_info_update
, 0);
1460 oscar_data_addhandler(od
, SNAC_FAMILY_CHAT
, SNAC_SUBTYPE_CHAT_INCOMINGMSG
, purple_conv_chat_incoming_msg
, 0);
1461 oscar_data_addhandler(od
, SNAC_FAMILY_CHATNAV
, 0x0001, purple_parse_genericerr
, 0);
1462 oscar_data_addhandler(od
, SNAC_FAMILY_CHATNAV
, SNAC_SUBTYPE_CHATNAV_INFO
, purple_chatnav_info
, 0);
1463 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_ERROR
, purple_ssi_parseerr
, 0);
1464 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO
, purple_ssi_parserights
, 0);
1465 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_LIST
, purple_ssi_parselist
, 0);
1466 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_SRVACK
, purple_ssi_parseack
, 0);
1467 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_ADD
, purple_ssi_parseaddmod
, 0);
1468 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_MOD
, purple_ssi_parseaddmod
, 0);
1469 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_RECVAUTH
, purple_ssi_authgiven
, 0);
1470 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ
, purple_ssi_authrequest
, 0);
1471 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP
, purple_ssi_authreply
, 0);
1472 oscar_data_addhandler(od
, SNAC_FAMILY_FEEDBAG
, SNAC_SUBTYPE_FEEDBAG_ADDED
, purple_ssi_gotadded
, 0);
1473 oscar_data_addhandler(od
, SNAC_FAMILY_ICBM
, SNAC_SUBTYPE_ICBM_INCOMING
, purple_parse_incoming_im
, 0);
1474 oscar_data_addhandler(od
, SNAC_FAMILY_ICBM
, SNAC_SUBTYPE_ICBM_MISSEDCALL
, purple_parse_misses
, 0);
1475 oscar_data_addhandler(od
, SNAC_FAMILY_ICBM
, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP
, purple_parse_clientauto
, 0);
1476 oscar_data_addhandler(od
, SNAC_FAMILY_ICBM
, SNAC_SUBTYPE_ICBM_ERROR
, purple_parse_msgerr
, 0);
1477 oscar_data_addhandler(od
, SNAC_FAMILY_ICBM
, SNAC_SUBTYPE_ICBM_MTN
, purple_parse_mtn
, 0);
1478 oscar_data_addhandler(od
, SNAC_FAMILY_ICBM
, SNAC_SUBTYPE_ICBM_ACK
, purple_parse_msgack
, 0);
1479 #ifdef OLDSTYLE_ICQ_OFFLINEMSGS
1480 oscar_data_addhandler(od
, SNAC_FAMILY_ICQ
, SNAC_SUBTYPE_ICQ_OFFLINEMSG
, purple_offlinemsg
, 0);
1481 oscar_data_addhandler(od
, SNAC_FAMILY_ICQ
, SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE
, purple_offlinemsgdone
, 0);
1482 #endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
1483 oscar_data_addhandler(od
, SNAC_FAMILY_ICQ
, SNAC_SUBTYPE_ICQ_ALIAS
, purple_icqalias
, 0);
1484 oscar_data_addhandler(od
, SNAC_FAMILY_ICQ
, SNAC_SUBTYPE_ICQ_INFO
, purple_icqinfo
, 0);
1485 oscar_data_addhandler(od
, SNAC_FAMILY_LOCATE
, SNAC_SUBTYPE_LOCATE_RIGHTSINFO
, purple_parse_locaterights
, 0);
1486 oscar_data_addhandler(od
, SNAC_FAMILY_LOCATE
, SNAC_SUBTYPE_LOCATE_USERINFO
, purple_parse_userinfo
, 0);
1487 oscar_data_addhandler(od
, SNAC_FAMILY_LOCATE
, SNAC_SUBTYPE_LOCATE_ERROR
, purple_parse_locerr
, 0);
1488 oscar_data_addhandler(od
, SNAC_FAMILY_OSERVICE
, 0x0001, purple_parse_genericerr
, 0);
1489 oscar_data_addhandler(od
, SNAC_FAMILY_OSERVICE
, 0x000f, purple_selfinfo
, 0);
1490 oscar_data_addhandler(od
, SNAC_FAMILY_OSERVICE
, 0x001f, purple_memrequest
, 0);
1491 oscar_data_addhandler(od
, SNAC_FAMILY_OSERVICE
, 0x0021, oscar_icon_req
,0);
1492 oscar_data_addhandler(od
, SNAC_FAMILY_OSERVICE
, SNAC_SUBTYPE_OSERVICE_RATECHANGE
, purple_parse_ratechange
, 0);
1493 oscar_data_addhandler(od
, SNAC_FAMILY_OSERVICE
, SNAC_SUBTYPE_OSERVICE_REDIRECT
, purple_handle_redirect
, 0);
1494 oscar_data_addhandler(od
, SNAC_FAMILY_OSERVICE
, SNAC_SUBTYPE_OSERVICE_MOTD
, purple_parse_motd
, 0);
1495 oscar_data_addhandler(od
, SNAC_FAMILY_OSERVICE
, SNAC_SUBTYPE_OSERVICE_EVIL
, purple_parse_evilnotify
, 0);
1496 oscar_data_addhandler(od
, SNAC_FAMILY_POPUP
, 0x0002, purple_popup
, 0);
1497 oscar_data_addhandler(od
, SNAC_FAMILY_USERLOOKUP
, SNAC_SUBTYPE_USERLOOKUP_ERROR
, purple_parse_searcherror
, 0);
1498 oscar_data_addhandler(od
, SNAC_FAMILY_USERLOOKUP
, 0x0003, purple_parse_searchreply
, 0);
1500 purple_debug_misc("oscar", "oscar_login: gc = %p\n", gc
);
1502 if (!oscar_util_valid_name(purple_account_get_username(account
))) {
1504 buf
= g_strdup_printf(_("Unable to sign on as %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account
));
1505 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
, buf
);
1510 if (oscar_util_valid_name_icq((purple_account_get_username(account
)))) {
1513 gc
->flags
|= PURPLE_CONNECTION_HTML
;
1514 gc
->flags
|= PURPLE_CONNECTION_AUTO_RESP
;
1517 od
->default_port
= purple_account_get_int(account
, "port", OSCAR_DEFAULT_LOGIN_PORT
);
1518 od
->use_ssl
= purple_account_get_bool(account
, "use_ssl", OSCAR_DEFAULT_USE_SSL
);
1520 /* Connect to core Purple signals */
1521 purple_prefs_connect_callback(gc
, "/purple/away/idle_reporting", idle_reporting_pref_cb
, gc
);
1522 purple_prefs_connect_callback(gc
, "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb
, gc
);
1525 * On 2008-03-05 AOL released some documentation on the OSCAR protocol
1526 * which includes a new login method called clientLogin. It is similar
1527 * (though not the same?) as what the AIM 6.0 series uses to
1530 * AIM 5.9 and lower use an MD5-based login procedure called "BUCP".
1531 * This authentication method is used for both ICQ and AIM when
1532 * clientLogin is not enabled.
1534 if (purple_account_get_bool(account
, "use_clientlogin", OSCAR_DEFAULT_USE_CLIENTLOGIN
)) {
1535 send_client_login(od
, purple_account_get_username(account
));
1537 FlapConnection
*newconn
;
1540 newconn
= flap_connection_new(od
, SNAC_FAMILY_AUTH
);
1543 if (!purple_ssl_is_supported()) {
1544 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT
,
1545 _("SSL support unavailable"));
1549 server
= purple_account_get_string(account
, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER
);
1552 * If the account's server is what the oscar prpl has offered as
1553 * the default login server through the vast eons (all two of
1554 * said default options, AFAIK) and the user wants SSL, we'll
1555 * do what we know is best for them and change the setting out
1556 * from under them to the SSL login server.
1558 if (!strcmp(server
, OSCAR_DEFAULT_LOGIN_SERVER
) || !strcmp(server
, OSCAR_OLD_LOGIN_SERVER
)) {
1559 purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n");
1560 purple_account_set_string(account
, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER
);
1561 server
= OSCAR_DEFAULT_SSL_LOGIN_SERVER
;
1564 newconn
->gsc
= purple_ssl_connect(account
, server
,
1565 purple_account_get_int(account
, "port", OSCAR_DEFAULT_LOGIN_PORT
),
1566 ssl_connection_established_cb
, ssl_connection_error_cb
, newconn
);
1568 server
= purple_account_get_string(account
, "server", OSCAR_DEFAULT_LOGIN_SERVER
);
1571 * See the comment above. We do the reverse here. If they don't want
1572 * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER,
1573 * set it back to the default.
1575 if (!strcmp(server
, OSCAR_DEFAULT_SSL_LOGIN_SERVER
)) {
1576 purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n");
1577 purple_account_set_string(account
, "server", OSCAR_DEFAULT_LOGIN_SERVER
);
1578 server
= OSCAR_DEFAULT_LOGIN_SERVER
;
1581 newconn
->connect_data
= purple_proxy_connect(NULL
, account
, server
,
1582 purple_account_get_int(account
, "port", OSCAR_DEFAULT_LOGIN_PORT
),
1583 connection_established_cb
, newconn
);
1586 if (newconn
->gsc
== NULL
&& newconn
->connect_data
== NULL
) {
1587 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
1588 _("Unable to connect"));
1593 purple_connection_update_progress(gc
, _("Connecting"), 0, OSCAR_CONNECT_STEPS
);
1598 oscar_close(PurpleConnection
*gc
)
1602 od
= purple_connection_get_protocol_data(gc
);
1604 while (od
->oscar_chats
)
1606 struct chat_connection
*cc
= od
->oscar_chats
->data
;
1607 od
->oscar_chats
= g_slist_remove(od
->oscar_chats
, cc
);
1608 oscar_chat_destroy(cc
);
1610 while (od
->create_rooms
)
1612 struct create_room
*cr
= od
->create_rooms
->data
;
1614 od
->create_rooms
= g_slist_remove(od
->create_rooms
, cr
);
1617 oscar_data_destroy(od
);
1618 purple_connection_set_protocol_data(gc
, NULL
);
1620 purple_prefs_disconnect_by_handle(gc
);
1622 purple_debug_info("oscar", "Signed off.\n");
1625 /* XXX - Should use purple_util_fetch_url for the below stuff */
1626 struct pieceofcrap
{
1627 PurpleConnection
*gc
;
1628 unsigned long offset
;
1632 FlapConnection
*conn
;
1636 static void damn_you(gpointer data
, gint source
, PurpleInputCondition c
)
1638 struct pieceofcrap
*pos
= data
;
1639 OscarData
*od
= purple_connection_get_protocol_data(pos
->gc
);
1642 unsigned char m
[17];
1645 while (read(pos
->fd
, &in
, 1) == 1) {
1648 else if (in
!= '\r')
1656 g_snprintf(buf
, sizeof(buf
), _("You may be disconnected shortly. "
1657 "If so, check %s for updates."),
1658 oscar_get_ui_info_string("website", PURPLE_WEBSITE
));
1659 purple_notify_warning(pos
->gc
, NULL
,
1660 _("Unable to get a valid AIM login hash."),
1662 purple_input_remove(pos
->inpa
);
1667 if (read(pos
->fd
, m
, 16) != 16)
1669 purple_debug_warning("oscar", "Could not read full AIM login hash "
1670 "from " AIMHASHDATA
"--that's bad.\n");
1674 msg
= g_string_new("Sending hash: ");
1675 for (x
= 0; x
< 16; x
++)
1676 g_string_append_printf(msg
, "%02hhx ", (unsigned char)m
[x
]);
1677 g_string_append(msg
, "\n");
1678 purple_debug_misc("oscar", "%s", msg
->str
);
1679 g_string_free(msg
, TRUE
);
1681 purple_input_remove(pos
->inpa
);
1683 aim_sendmemblock(od
, pos
->conn
, 0, 16, m
, AIM_SENDMEMBLOCK_FLAG_ISHASH
);
1688 straight_to_hell(gpointer data
, gint source
, const gchar
*error_message
)
1690 struct pieceofcrap
*pos
= data
;
1697 buf
= g_strdup_printf(_("You may be disconnected shortly. "
1698 "If so, check %s for updates."),
1699 oscar_get_ui_info_string("website", PURPLE_WEBSITE
));
1700 purple_notify_warning(pos
->gc
, NULL
,
1701 _("Unable to get a valid AIM login hash."),
1704 g_free(pos
->modname
);
1709 buf
= g_strdup_printf("GET " AIMHASHDATA
"?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
1710 pos
->offset
, pos
->len
, pos
->modname
? pos
->modname
: "");
1711 result
= send(pos
->fd
, buf
, strlen(buf
), 0);
1712 if (result
!= strlen(buf
)) {
1714 purple_debug_error("oscar", "Error writing %" G_GSIZE_FORMAT
1715 " bytes to fetch AIM hash data: %s\n",
1716 strlen(buf
), g_strerror(errno
));
1718 purple_debug_error("oscar", "Tried to write %"
1719 G_GSIZE_FORMAT
" bytes to fetch AIM hash data but "
1720 "instead wrote %" G_GSSIZE_FORMAT
" bytes\n",
1721 strlen(buf
), result
);
1724 g_free(pos
->modname
);
1725 pos
->inpa
= purple_input_add(pos
->fd
, PURPLE_INPUT_READ
, damn_you
, pos
);
1729 /* size of icbmui.ocm, the largest module in AIM 3.5 */
1730 #define AIM_MAX_FILE_SIZE 98304
1732 static int purple_memrequest(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
1735 struct pieceofcrap
*pos
;
1736 guint32 offset
, len
;
1740 offset
= va_arg(ap
, guint32
);
1741 len
= va_arg(ap
, guint32
);
1742 modname
= va_arg(ap
, char *);
1745 purple_debug_misc("oscar", "offset: %u, len: %u, file: %s\n",
1746 offset
, len
, (modname
? modname
: "aim.exe"));
1749 purple_debug_misc("oscar", "len is 0, hashing NULL\n");
1750 aim_sendmemblock(od
, conn
, offset
, len
, NULL
,
1751 AIM_SENDMEMBLOCK_FLAG_ISREQUEST
);
1754 /* uncomment this when you're convinced it's right. remember, it's been wrong before. */
1756 if (offset
> AIM_MAX_FILE_SIZE
|| len
> AIM_MAX_FILE_SIZE
) {
1760 i
+= strlen(modname
);
1764 memcpy(buf
, modname
, strlen(modname
));
1765 i
+= strlen(modname
);
1767 buf
[i
++] = offset
& 0xff;
1768 buf
[i
++] = (offset
>> 8) & 0xff;
1769 buf
[i
++] = (offset
>> 16) & 0xff;
1770 buf
[i
++] = (offset
>> 24) & 0xff;
1771 buf
[i
++] = len
& 0xff;
1772 buf
[i
++] = (len
>> 8) & 0xff;
1773 buf
[i
++] = (len
>> 16) & 0xff;
1774 buf
[i
++] = (len
>> 24) & 0xff;
1775 purple_debug_misc("oscar", "len + offset is invalid, "
1776 "hashing request\n");
1777 aim_sendmemblock(od
, command
->conn
, offset
, i
, buf
, AIM_SENDMEMBLOCK_FLAG_ISREQUEST
);
1783 pos
= g_new0(struct pieceofcrap
, 1);
1787 pos
->offset
= offset
;
1789 pos
->modname
= g_strdup(modname
);
1791 if (purple_proxy_connect(pos
->gc
, pos
->gc
->account
, "pidgin.im", 80,
1792 straight_to_hell
, pos
) == NULL
)
1795 g_free(pos
->modname
);
1798 g_snprintf(buf
, sizeof(buf
), _("You may be disconnected shortly. "
1799 "If so, check %s for updates."),
1800 oscar_get_ui_info_string("website", PURPLE_WEBSITE
));
1801 purple_notify_warning(pos
->gc
, NULL
,
1802 _("Unable to get a valid login hash."),
1809 int oscar_connect_to_bos(PurpleConnection
*gc
, OscarData
*od
, const char *host
, guint16 port
, guint8
*cookie
, guint16 cookielen
)
1811 FlapConnection
*conn
;
1813 conn
= flap_connection_new(od
, SNAC_FAMILY_LOCATE
);
1814 conn
->cookielen
= cookielen
;
1815 conn
->cookie
= g_memdup(cookie
, cookielen
);
1816 conn
->connect_data
= purple_proxy_connect(NULL
,
1817 purple_connection_get_account(gc
), host
, port
,
1818 connection_established_cb
, conn
);
1819 if (conn
->connect_data
== NULL
)
1821 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, _("Unable to connect"));
1825 od
->default_port
= port
;
1827 purple_connection_update_progress(gc
, _("Received authorization"), 3, OSCAR_CONNECT_STEPS
);
1834 * Only used when connecting with the old-style BUCP login.
1837 purple_parse_auth_resp(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
1839 PurpleConnection
*gc
= od
->gc
;
1840 PurpleAccount
*account
= purple_connection_get_account(gc
);
1841 char *host
; int port
;
1843 FlapConnection
*newconn
;
1845 struct aim_authresp_info
*info
;
1847 port
= purple_account_get_int(account
, "port", od
->default_port
);
1850 info
= va_arg(ap
, struct aim_authresp_info
*);
1853 purple_debug_info("oscar",
1854 "inside auth_resp (Username: %s)\n", info
->bn
);
1856 if (info
->errorcode
|| !info
->bosip
|| !info
->cookielen
|| !info
->cookie
) {
1858 switch (info
->errorcode
) {
1860 /* Unregistered username */
1861 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_INVALID_USERNAME
, _("Username does not exist"));
1864 /* Incorrect password */
1865 if (!purple_account_get_remember_password(account
))
1866 purple_account_set_password(account
, NULL
);
1867 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
, _("Incorrect password"));
1870 /* Suspended account */
1871 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
, _("Your account is currently suspended"));
1875 /* service temporarily unavailable */
1876 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, _("The AOL Instant Messenger service is temporarily unavailable."));
1879 /* username connecting too frequently */
1880 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_OTHER_ERROR
, _("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."));
1884 /* client too old */
1885 g_snprintf(buf
, sizeof(buf
), _("The client version you are using is too old. Please upgrade at %s"),
1886 oscar_get_ui_info_string("website", PURPLE_WEBSITE
));
1887 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_OTHER_ERROR
, buf
);
1891 /* IP address connecting too frequently */
1892 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_OTHER_ERROR
, _("You have been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer."));
1895 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
, _("Unknown reason"));
1898 purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info
->errorcode
);
1899 purple_debug_info("oscar", "Error URL: %s\n", info
->errorurl
? info
->errorurl
: "");
1903 purple_debug_misc("oscar", "Reg status: %hu\n"
1907 info
->email
? info
->email
: "null",
1908 info
->bosip
? info
->bosip
: "null");
1909 purple_debug_info("oscar", "Closing auth connection...\n");
1910 flap_connection_schedule_destroy(conn
, OSCAR_DISCONNECT_DONE
, NULL
);
1912 for (i
= 0; i
< strlen(info
->bosip
); i
++) {
1913 if (info
->bosip
[i
] == ':') {
1914 port
= atoi(&(info
->bosip
[i
+1]));
1918 host
= g_strndup(info
->bosip
, i
);
1919 newconn
= flap_connection_new(od
, SNAC_FAMILY_LOCATE
);
1920 newconn
->cookielen
= info
->cookielen
;
1921 newconn
->cookie
= g_memdup(info
->cookie
, info
->cookielen
);
1926 * This shouldn't be hardcoded to "bos.oscar.aol.com" except that
1927 * the server isn't sending us a name to use for comparing the
1928 * certificate common name.
1930 newconn
->gsc
= purple_ssl_connect_with_ssl_cn(account
, host
, port
,
1931 ssl_connection_established_cb
, ssl_connection_error_cb
,
1932 "bos.oscar.aol.com", newconn
);
1936 newconn
->connect_data
= purple_proxy_connect(NULL
, account
, host
, port
,
1937 connection_established_cb
, newconn
);
1941 if (newconn
->gsc
== NULL
&& newconn
->connect_data
== NULL
)
1943 purple_connection_error_reason(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, _("Unable to connect"));
1947 purple_connection_update_progress(gc
, _("Received authorization"), 3, OSCAR_CONNECT_STEPS
);
1954 * Only used when connecting with the old-style BUCP login.
1957 purple_parse_auth_securid_request_yes_cb(gpointer user_data
, const char *msg
)
1959 PurpleConnection
*gc
= user_data
;
1960 OscarData
*od
= purple_connection_get_protocol_data(gc
);
1962 aim_auth_securid_send(od
, msg
);
1966 * Only used when connecting with the old-style BUCP login.
1969 purple_parse_auth_securid_request_no_cb(gpointer user_data
, const char *value
)
1971 PurpleConnection
*gc
= user_data
;
1974 purple_connection_error_reason(gc
,
1975 PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
,
1976 _("The SecurID key entered is invalid"));
1980 * Only used when connecting with the old-style BUCP login.
1983 purple_parse_auth_securid_request(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
1985 PurpleConnection
*gc
= od
->gc
;
1986 PurpleAccount
*account
= purple_connection_get_account(gc
);
1989 purple_debug_info("oscar", "Got SecurID request\n");
1991 primary
= g_strdup_printf("Enter the SecurID key for %s.", purple_account_get_username(account
));
1992 purple_request_input(gc
, NULL
, _("Enter SecurID"), primary
,
1993 _("Enter the 6 digit number from the digital display."),
1995 _("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb
),
1996 _("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb
),
1997 account
, NULL
, NULL
,
2005 * Only used when connecting with the old-style BUCP login.
2008 purple_parse_login(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
2010 PurpleConnection
*gc
;
2011 PurpleAccount
*account
;
2012 ClientInfo aiminfo
= CLIENTINFO_PURPLE_AIM
;
2013 ClientInfo icqinfo
= CLIENTINFO_PURPLE_ICQ
;
2016 gboolean truncate_pass
;
2019 account
= purple_connection_get_account(gc
);
2022 key
= va_arg(ap
, char *);
2023 truncate_pass
= va_arg(ap
, int);
2026 aim_send_login(od
, conn
, purple_account_get_username(account
),
2027 purple_connection_get_password(gc
), truncate_pass
,
2028 od
->icq
? &icqinfo
: &aiminfo
, key
,
2029 purple_account_get_bool(account
, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS
));
2031 purple_connection_update_progress(gc
, _("Password sent"), 2, OSCAR_CONNECT_STEPS
);
2038 purple_handle_redirect(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
2040 PurpleConnection
*gc
= od
->gc
;
2041 PurpleAccount
*account
= purple_connection_get_account(gc
);
2042 char *host
, *separator
;
2044 FlapConnection
*newconn
;
2046 struct aim_redirect_data
*redir
;
2049 redir
= va_arg(ap
, struct aim_redirect_data
*);
2052 port
= od
->default_port
;
2053 separator
= strchr(redir
->ip
, ':');
2054 if (separator
!= NULL
)
2056 host
= g_strndup(redir
->ip
, separator
- redir
->ip
);
2057 port
= atoi(separator
+ 1);
2060 host
= g_strdup(redir
->ip
);
2063 * These FLAP servers advertise SSL (type "0x02"), but SSL connections to these hosts
2064 * die a painful death. iChat and Miranda, when using SSL, still do these in plaintext.
2066 if (redir
->use_ssl
&& (redir
->group
== SNAC_FAMILY_ADMIN
||
2067 redir
->group
== SNAC_FAMILY_BART
))
2069 purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n",
2074 purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx%s\n",
2075 host
, port
, redir
->group
,
2076 od
->use_ssl
&& !redir
->use_ssl
? " without SSL, despite main stream encryption" : "");
2078 newconn
= flap_connection_new(od
, redir
->group
);
2079 newconn
->cookielen
= redir
->cookielen
;
2080 newconn
->cookie
= g_memdup(redir
->cookie
, redir
->cookielen
);
2081 if (newconn
->type
== SNAC_FAMILY_CHAT
)
2083 struct chat_connection
*cc
;
2084 cc
= g_new0(struct chat_connection
, 1);
2087 cc
->name
= g_strdup(redir
->chat
.room
);
2088 cc
->exchange
= redir
->chat
.exchange
;
2089 cc
->instance
= redir
->chat
.instance
;
2090 cc
->show
= extract_name(redir
->chat
.room
);
2091 newconn
->new_conn_data
= cc
;
2092 purple_debug_info("oscar", "Connecting to chat room %s exchange %hu\n", cc
->name
, cc
->exchange
);
2098 newconn
->gsc
= purple_ssl_connect_with_ssl_cn(account
, host
, port
,
2099 ssl_connection_established_cb
, ssl_connection_error_cb
,
2100 redir
->ssl_cert_cn
, newconn
);
2104 newconn
->connect_data
= purple_proxy_connect(NULL
, account
, host
, port
,
2105 connection_established_cb
, newconn
);
2108 if (newconn
->gsc
== NULL
&& newconn
->connect_data
== NULL
)
2110 flap_connection_schedule_destroy(newconn
,
2111 OSCAR_DISCONNECT_COULD_NOT_CONNECT
,
2112 _("Unable to initialize connection"));
2113 purple_debug_error("oscar", "Unable to connect to FLAP server "
2114 "of type 0x%04hx\n", redir
->group
);
2122 static int purple_parse_oncoming(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
2124 PurpleConnection
*gc
;
2125 PurpleAccount
*account
;
2126 struct buddyinfo
*bi
;
2127 time_t time_idle
= 0, signon
= 0;
2129 gboolean buddy_is_away
= FALSE
;
2130 const char *status_id
;
2132 aim_userinfo_t
*info
;
2133 char *message
= NULL
;
2134 char *itmsurl
= NULL
;
2139 account
= purple_connection_get_account(gc
);
2142 info
= va_arg(ap
, aim_userinfo_t
*);
2145 g_return_val_if_fail(info
!= NULL
, 1);
2146 g_return_val_if_fail(info
->bn
!= NULL
, 1);
2149 * If this is an AIM buddy and their name has formatting, set their
2152 if (!oscar_util_valid_name_icq(info
->bn
)) {
2153 gboolean bn_has_formatting
= FALSE
;
2155 for (c
= info
->bn
; *c
!= '\0'; c
++) {
2157 bn_has_formatting
= TRUE
;
2161 serv_got_alias(gc
, info
->bn
,
2162 bn_has_formatting
? info
->bn
: NULL
);
2165 if (info
->present
& AIM_USERINFO_PRESENT_FLAGS
) {
2166 if (info
->flags
& AIM_FLAG_AWAY
)
2167 buddy_is_away
= TRUE
;
2169 if (info
->present
& AIM_USERINFO_PRESENT_ICQEXTSTATUS
) {
2170 type
= info
->icqinfo
.status
;
2171 if (!(info
->icqinfo
.status
& AIM_ICQ_STATE_CHAT
) &&
2172 (info
->icqinfo
.status
!= AIM_ICQ_STATE_NORMAL
)) {
2173 buddy_is_away
= TRUE
;
2177 if (oscar_util_valid_name_icq(info
->bn
)) {
2178 if (type
& AIM_ICQ_STATE_CHAT
)
2179 status_id
= OSCAR_STATUS_ID_FREE4CHAT
;
2180 else if (type
& AIM_ICQ_STATE_DND
)
2181 status_id
= OSCAR_STATUS_ID_DND
;
2182 else if (type
& AIM_ICQ_STATE_OUT
)
2183 status_id
= OSCAR_STATUS_ID_NA
;
2184 else if (type
& AIM_ICQ_STATE_BUSY
)
2185 status_id
= OSCAR_STATUS_ID_OCCUPIED
;
2186 else if (type
& AIM_ICQ_STATE_AWAY
)
2187 status_id
= OSCAR_STATUS_ID_AWAY
;
2188 else if (type
& AIM_ICQ_STATE_INVISIBLE
)
2189 status_id
= OSCAR_STATUS_ID_INVISIBLE
;
2191 status_id
= OSCAR_STATUS_ID_AVAILABLE
;
2193 if (type
& AIM_ICQ_STATE_INVISIBLE
)
2194 status_id
= OSCAR_STATUS_ID_INVISIBLE
;
2195 else if (buddy_is_away
)
2196 status_id
= OSCAR_STATUS_ID_AWAY
;
2198 status_id
= OSCAR_STATUS_ID_AVAILABLE
;
2201 if (info
->flags
& AIM_FLAG_WIRELESS
)
2203 purple_prpl_got_user_status(account
, info
->bn
, OSCAR_STATUS_ID_MOBILE
, NULL
);
2205 purple_prpl_got_user_status_deactive(account
, info
->bn
, OSCAR_STATUS_ID_MOBILE
);
2208 if (info
->status
!= NULL
&& info
->status
[0] != '\0')
2209 /* Grab the available message */
2210 message
= oscar_encoding_to_utf8(account
, info
->status_encoding
,
2211 info
->status
, info
->status_len
);
2213 tmp2
= tmp
= (message
? purple_markup_escape_text(message
, -1) : NULL
);
2215 if (strcmp(status_id
, OSCAR_STATUS_ID_AVAILABLE
) == 0) {
2216 if (info
->itmsurl_encoding
&& info
->itmsurl
&& info
->itmsurl_len
)
2217 /* Grab the iTunes Music Store URL */
2218 itmsurl
= oscar_encoding_to_utf8(account
, info
->itmsurl_encoding
,
2219 info
->itmsurl
, info
->itmsurl_len
);
2221 if (tmp2
== NULL
&& itmsurl
!= NULL
)
2223 * The message can't be NULL because NULL means it was the
2224 * last attribute, so the itmsurl would get ignored below.
2228 purple_prpl_got_user_status(account
, info
->bn
, status_id
,
2229 "message", tmp2
, "itmsurl", itmsurl
, NULL
);
2232 purple_prpl_got_user_status(account
, info
->bn
, status_id
, "message", tmp2
, NULL
);
2239 /* Login time stuff */
2240 if (info
->present
& AIM_USERINFO_PRESENT_ONLINESINCE
)
2241 signon
= info
->onlinesince
;
2242 else if (info
->present
& AIM_USERINFO_PRESENT_SESSIONLEN
)
2243 signon
= time(NULL
) - info
->sessionlen
;
2244 purple_prpl_got_user_login_time(account
, info
->bn
, signon
);
2246 /* Idle time stuff */
2247 /* info->idletime is the number of minutes that this user has been idle */
2248 if (info
->present
& AIM_USERINFO_PRESENT_IDLE
)
2249 time_idle
= time(NULL
) - info
->idletime
* 60;
2252 purple_prpl_got_user_idle(account
, info
->bn
, TRUE
, time_idle
);
2254 purple_prpl_got_user_idle(account
, info
->bn
, FALSE
, 0);
2256 /* Server stored icon stuff */
2257 bi
= g_hash_table_lookup(od
->buddyinfo
, purple_normalize(account
, info
->bn
));
2259 bi
= g_new0(struct buddyinfo
, 1);
2260 g_hash_table_insert(od
->buddyinfo
, g_strdup(purple_normalize(account
, info
->bn
)), bi
);
2262 bi
->typingnot
= FALSE
;
2263 bi
->ico_informed
= FALSE
;
2264 bi
->ipaddr
= info
->icqinfo
.ipaddr
;
2266 if (info
->iconcsumlen
) {
2267 const char *saved_b16
= NULL
;
2269 PurpleBuddy
*b
= NULL
;
2271 b16
= purple_base16_encode(info
->iconcsum
, info
->iconcsumlen
);
2272 b
= purple_find_buddy(account
, info
->bn
);
2274 saved_b16
= purple_buddy_icons_get_checksum_for_user(b
);
2276 if (!b16
|| !saved_b16
|| strcmp(b16
, saved_b16
)) {
2277 /* Invalidate the old icon for this user */
2278 purple_buddy_icons_set_for_user(account
, info
->bn
, NULL
, 0, NULL
);
2280 /* Fetch the new icon (if we're not already doing so) */
2281 if (g_slist_find_custom(od
->requesticon
, info
->bn
,
2282 (GCompareFunc
)oscar_util_name_compare
) == NULL
)
2284 od
->requesticon
= g_slist_prepend(od
->requesticon
,
2285 g_strdup(purple_normalize(account
, info
->bn
)));
2286 purple_icons_fetch(gc
);
2295 static void purple_check_comment(OscarData
*od
, const char *str
) {
2296 if ((str
== NULL
) || strcmp(str
, (const char *)ck
))
2297 aim_locate_setcaps(od
, purple_caps
);
2299 aim_locate_setcaps(od
, purple_caps
| OSCAR_CAPABILITY_SECUREIM
);
2302 static int purple_parse_offgoing(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
2303 PurpleConnection
*gc
= od
->gc
;
2304 PurpleAccount
*account
= purple_connection_get_account(gc
);
2306 aim_userinfo_t
*info
;
2309 info
= va_arg(ap
, aim_userinfo_t
*);
2312 purple_prpl_got_user_status(account
, info
->bn
, OSCAR_STATUS_ID_OFFLINE
, NULL
);
2313 purple_prpl_got_user_status_deactive(account
, info
->bn
, OSCAR_STATUS_ID_MOBILE
);
2314 g_hash_table_remove(od
->buddyinfo
, purple_normalize(gc
->account
, info
->bn
));
2319 static int incomingim_chan1(OscarData
*od
, FlapConnection
*conn
, aim_userinfo_t
*userinfo
, struct aim_incomingim_ch1_args
*args
) {
2320 PurpleConnection
*gc
= od
->gc
;
2321 PurpleAccount
*account
= purple_connection_get_account(gc
);
2322 PurpleMessageFlags flags
= 0;
2323 struct buddyinfo
*bi
;
2324 PurpleStoredImage
*img
;
2327 aim_mpmsg_section_t
*curpart
;
2328 const char *start
, *end
;
2331 purple_debug_misc("oscar", "Received IM from %s with %d parts\n",
2332 userinfo
->bn
, args
->mpmsg
.numparts
);
2334 if (args
->mpmsg
.numparts
== 0)
2337 bi
= g_hash_table_lookup(od
->buddyinfo
, purple_normalize(account
, userinfo
->bn
));
2339 bi
= g_new0(struct buddyinfo
, 1);
2340 g_hash_table_insert(od
->buddyinfo
, g_strdup(purple_normalize(account
, userinfo
->bn
)), bi
);
2343 if (args
->icbmflags
& AIM_IMFLAGS_AWAY
)
2344 flags
|= PURPLE_MESSAGE_AUTO_RESP
;
2346 if (args
->icbmflags
& AIM_IMFLAGS_TYPINGNOT
)
2347 bi
->typingnot
= TRUE
;
2349 bi
->typingnot
= FALSE
;
2351 if ((args
->icbmflags
& AIM_IMFLAGS_HASICON
) && (args
->iconlen
) && (args
->iconsum
) && (args
->iconstamp
)) {
2352 purple_debug_misc("oscar", "%s has an icon\n", userinfo
->bn
);
2353 if ((args
->iconlen
!= bi
->ico_len
) || (args
->iconsum
!= bi
->ico_csum
) || (args
->iconstamp
!= bi
->ico_time
)) {
2354 bi
->ico_need
= TRUE
;
2355 bi
->ico_len
= args
->iconlen
;
2356 bi
->ico_csum
= args
->iconsum
;
2357 bi
->ico_time
= args
->iconstamp
;
2361 img
= purple_buddy_icons_find_account_icon(account
);
2362 if ((img
!= NULL
) &&
2363 (args
->icbmflags
& AIM_IMFLAGS_BUDDYREQ
) && !bi
->ico_sent
&& bi
->ico_informed
) {
2364 gconstpointer data
= purple_imgstore_get_data(img
);
2365 size_t len
= purple_imgstore_get_size(img
);
2366 purple_debug_info("oscar",
2367 "Sending buddy icon to %s (%" G_GSIZE_FORMAT
" bytes)\n",
2369 aim_im_sendch2_icon(od
, userinfo
->bn
, data
, len
,
2370 purple_buddy_icons_get_account_icon_timestamp(account
),
2371 aimutil_iconsum(data
, len
));
2373 purple_imgstore_unref(img
);
2375 message
= g_string_new("");
2376 curpart
= args
->mpmsg
.parts
;
2377 while (curpart
!= NULL
) {
2378 tmp
= purple_plugin_oscar_decode_im_part(account
, userinfo
->bn
, curpart
->charset
,
2379 curpart
->charsubset
, curpart
->data
, curpart
->datalen
);
2381 g_string_append(message
, tmp
);
2385 curpart
= curpart
->next
;
2387 tmp
= g_string_free(message
, FALSE
);
2390 * If the message is from an ICQ user and to an ICQ user then escape any HTML,
2391 * because HTML is not sent over ICQ as a means to format a message.
2392 * So any HTML we receive is intended to be displayed. Also, \r\n must be
2393 * replaced with <br>
2395 * Note: There *may* be some clients which send messages as HTML formatted -
2396 * they need to be special-cased somehow.
2398 if (oscar_util_valid_name_icq(purple_account_get_username(account
)) && oscar_util_valid_name_icq(userinfo
->bn
)) {
2399 /* being recevied by ICQ from ICQ - escape HTML so it is displayed as sent */
2400 gchar
*tmp2
= g_markup_escape_text(tmp
, -1);
2403 tmp2
= purple_strreplace(tmp
, "\r\n", "<br>");
2409 * Convert iChat color tags to normal font tags.
2411 if (purple_markup_find_tag("body", tmp
, &start
, &end
, &attribs
))
2413 const char *ichattextcolor
, *ichatballooncolor
;
2414 const char *start2
, *end2
;
2418 * Find the ending </body> so we can strip off the outer <html/>
2421 if (purple_markup_find_tag("/body", end
+ 1, &start2
, &end2
, &unused
))
2424 tmp2
= g_strndup(end
+ 1, (start2
- 1) - (end
+ 1) + 1);
2427 g_datalist_clear(&unused
);
2430 ichattextcolor
= g_datalist_get_data(&attribs
, "ichattextcolor");
2431 if (ichattextcolor
!= NULL
)
2434 tmp2
= g_strdup_printf("<font color=\"%s\">%s</font>", ichattextcolor
, tmp
);
2439 ichatballooncolor
= g_datalist_get_data(&attribs
, "ichatballooncolor");
2440 if (ichatballooncolor
!= NULL
)
2443 tmp2
= g_strdup_printf("<font back=\"%s\">%s</font>", ichatballooncolor
, tmp
);
2448 g_datalist_clear(&attribs
);
2451 serv_got_im(gc
, userinfo
->bn
, tmp
, flags
,
2452 (args
->icbmflags
& AIM_IMFLAGS_OFFLINE
) ? args
->timestamp
: time(NULL
));
2459 incomingim_chan2(OscarData
*od
, FlapConnection
*conn
, aim_userinfo_t
*userinfo
, IcbmArgsCh2
*args
)
2461 PurpleConnection
*gc
;
2462 PurpleAccount
*account
;
2463 char *message
= NULL
;
2465 g_return_val_if_fail(od
!= NULL
, 0);
2466 g_return_val_if_fail(od
->gc
!= NULL
, 0);
2469 account
= purple_connection_get_account(gc
);
2470 od
= purple_connection_get_protocol_data(gc
);
2475 purple_debug_misc("oscar", "Incoming rendezvous message of type %u, "
2476 "user %s, status %hu\n", args
->type
, userinfo
->bn
, args
->status
);
2478 if (args
->msg
!= NULL
)
2480 if (args
->encoding
!= NULL
)
2482 char *encoding
= NULL
;
2483 encoding
= oscar_encoding_extract(args
->encoding
);
2484 message
= oscar_encoding_to_utf8(account
, encoding
, args
->msg
,
2488 if (g_utf8_validate(args
->msg
, args
->msglen
, NULL
))
2489 message
= g_strdup(args
->msg
);
2493 if (args
->type
& OSCAR_CAPABILITY_CHAT
)
2495 char *encoding
, *utf8name
, *tmp
;
2496 GHashTable
*components
;
2498 if (!args
->info
.chat
.roominfo
.name
|| !args
->info
.chat
.roominfo
.exchange
) {
2502 encoding
= args
->encoding
? oscar_encoding_extract(args
->encoding
) : NULL
;
2503 utf8name
= oscar_encoding_to_utf8(account
, encoding
,
2504 args
->info
.chat
.roominfo
.name
,
2505 args
->info
.chat
.roominfo
.namelen
);
2508 tmp
= extract_name(utf8name
);
2515 components
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
,
2517 g_hash_table_replace(components
, g_strdup("room"), utf8name
);
2518 g_hash_table_replace(components
, g_strdup("exchange"),
2519 g_strdup_printf("%d", args
->info
.chat
.roominfo
.exchange
));
2520 serv_got_chat_invite(gc
,
2527 else if ((args
->type
& OSCAR_CAPABILITY_SENDFILE
) ||
2528 (args
->type
& OSCAR_CAPABILITY_DIRECTIM
))
2530 if (args
->status
== AIM_RENDEZVOUS_PROPOSE
)
2532 peer_connection_got_proposition(od
, userinfo
->bn
, message
, args
);
2534 else if (args
->status
== AIM_RENDEZVOUS_CANCEL
)
2536 /* The other user canceled a peer request */
2537 PeerConnection
*conn
;
2539 conn
= peer_connection_find_by_cookie(od
, userinfo
->bn
, args
->cookie
);
2541 * If conn is NULL it means we haven't tried to create
2542 * a connection with that user. They may be trying to
2543 * do something malicious.
2547 peer_connection_destroy(conn
, OSCAR_DISCONNECT_REMOTE_CLOSED
, NULL
);
2550 else if (args
->status
== AIM_RENDEZVOUS_CONNECTED
)
2553 * Remote user has accepted our peer request. If we
2554 * wanted to we could look up the PeerConnection using
2555 * args->cookie, but we don't need to do anything here.
2560 else if (args
->type
& OSCAR_CAPABILITY_GETFILE
)
2564 else if (args
->type
& OSCAR_CAPABILITY_TALK
)
2568 else if (args
->type
& OSCAR_CAPABILITY_BUDDYICON
)
2570 purple_buddy_icons_set_for_user(account
, userinfo
->bn
,
2571 g_memdup(args
->info
.icon
.icon
, args
->info
.icon
.length
),
2572 args
->info
.icon
.length
,
2576 else if (args
->type
& OSCAR_CAPABILITY_ICQSERVERRELAY
)
2578 purple_debug_error("oscar", "Got an ICQ Server Relay message of "
2579 "type %d\n", args
->info
.rtfmsg
.msgtype
);
2584 purple_debug_error("oscar", "Unknown request class %hu\n",
2594 * Authorization Functions
2595 * Most of these are callbacks from dialogs. They're used by both
2596 * methods of authorization (SSI and old-school channel 4 ICBM)
2598 /* When you ask other people for authorization */
2600 purple_auth_request(struct name_data
*data
, char *msg
)
2602 PurpleConnection
*gc
;
2604 PurpleAccount
*account
;
2607 const char *bname
, *gname
;
2610 od
= purple_connection_get_protocol_data(gc
);
2611 account
= purple_connection_get_account(gc
);
2612 buddy
= purple_find_buddy(account
, data
->name
);
2614 group
= purple_buddy_get_group(buddy
);
2620 bname
= purple_buddy_get_name(buddy
);
2621 gname
= purple_group_get_name(group
);
2622 purple_debug_info("oscar", "ssi: adding buddy %s to group %s\n",
2624 aim_ssi_sendauthrequest(od
, data
->name
, msg
? msg
: _("Please authorize me so I can add you to my buddy list."));
2625 if (!aim_ssi_itemlist_finditem(od
->ssi
.local
, gname
, bname
, AIM_SSI_TYPE_BUDDY
))
2627 aim_ssi_addbuddy(od
, bname
, gname
, NULL
, purple_buddy_get_alias_only(buddy
), NULL
, NULL
, TRUE
);
2629 /* Mobile users should always be online */
2630 if (bname
[0] == '+') {
2631 purple_prpl_got_user_status(account
,
2632 purple_buddy_get_name(buddy
),
2633 OSCAR_STATUS_ID_AVAILABLE
, NULL
);
2634 purple_prpl_got_user_status(account
,
2635 purple_buddy_get_name(buddy
),
2636 OSCAR_STATUS_ID_MOBILE
, NULL
);
2641 oscar_free_name_data(data
);
2645 purple_auth_sendrequest(PurpleConnection
*gc
, const char *name
)
2647 struct name_data
*data
;
2649 data
= g_new0(struct name_data
, 1);
2651 data
->name
= g_strdup(name
);
2653 purple_request_input(data
->gc
, NULL
, _("Authorization Request Message:"),
2654 NULL
, _("Please authorize me!"), TRUE
, FALSE
, NULL
,
2655 _("_OK"), G_CALLBACK(purple_auth_request
),
2656 _("_Cancel"), G_CALLBACK(oscar_free_name_data
),
2657 purple_connection_get_account(gc
), name
, NULL
,
2662 purple_auth_sendrequest_menu(PurpleBlistNode
*node
, gpointer ignored
)
2665 PurpleConnection
*gc
;
2667 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
2669 buddy
= (PurpleBuddy
*) node
;
2670 gc
= purple_account_get_connection(purple_buddy_get_account(buddy
));
2671 purple_auth_sendrequest(gc
, purple_buddy_get_name(buddy
));
2674 /* When other people ask you for authorization */
2676 purple_auth_grant(gpointer cbdata
)
2678 struct name_data
*data
= cbdata
;
2679 PurpleConnection
*gc
= data
->gc
;
2680 OscarData
*od
= purple_connection_get_protocol_data(gc
);
2682 aim_ssi_sendauthreply(od
, data
->name
, 0x01, NULL
);
2684 oscar_free_name_data(data
);
2687 /* When other people ask you for authorization */
2689 purple_auth_dontgrant(struct name_data
*data
, char *msg
)
2691 PurpleConnection
*gc
= data
->gc
;
2692 OscarData
*od
= purple_connection_get_protocol_data(gc
);
2694 aim_ssi_sendauthreply(od
, data
->name
, 0x00, msg
? msg
: _("No reason given."));
2698 purple_auth_dontgrant_msgprompt(gpointer cbdata
)
2700 struct name_data
*data
= cbdata
;
2701 purple_request_input(data
->gc
, NULL
, _("Authorization Denied Message:"),
2702 NULL
, _("No reason given."), TRUE
, FALSE
, NULL
,
2703 _("_OK"), G_CALLBACK(purple_auth_dontgrant
),
2704 _("_Cancel"), G_CALLBACK(oscar_free_name_data
),
2705 purple_connection_get_account(data
->gc
), data
->name
, NULL
,
2709 /* When someone sends you buddies */
2711 purple_icq_buddyadd(struct name_data
*data
)
2713 PurpleConnection
*gc
= data
->gc
;
2715 purple_blist_request_add_buddy(purple_connection_get_account(gc
), data
->name
, NULL
, data
->nick
);
2717 oscar_free_name_data(data
);
2721 incomingim_chan4(OscarData
*od
, FlapConnection
*conn
, aim_userinfo_t
*userinfo
, struct aim_incomingim_ch4_args
*args
, time_t t
)
2723 PurpleConnection
*gc
= od
->gc
;
2724 PurpleAccount
*account
= purple_connection_get_account(gc
);
2725 gchar
**msg1
, **msg2
;
2728 if (!args
->type
|| !args
->msg
|| !args
->uin
)
2731 purple_debug_info("oscar",
2732 "Received a channel 4 message of type 0x%02hx.\n",
2736 * Split up the message at the delimeter character, then convert each
2737 * string to UTF-8. Unless, of course, this is a type 1 message. If
2738 * this is a type 1 message, then the delimiter 0xfe could be a valid
2739 * character in whatever encoding the message was sent in. Type 1
2740 * messages are always made up of only one part, so we can easily account
2741 * for this suck-ass part of the protocol by splitting the string into at
2742 * most 1 baby string.
2744 msg1
= g_strsplit(args
->msg
, "\376", (args
->type
== 0x01 ? 1 : 0));
2745 for (numtoks
=0; msg1
[numtoks
]; numtoks
++);
2746 msg2
= (gchar
**)g_malloc((numtoks
+1)*sizeof(gchar
*));
2747 for (i
=0; msg1
[i
]; i
++) {
2748 gchar
*uin
= g_strdup_printf("%u", args
->uin
);
2750 purple_str_strip_char(msg1
[i
], '\r');
2751 /* TODO: Should use an encoding other than ASCII? */
2752 msg2
[i
] = purple_plugin_oscar_decode_im_part(account
, uin
, AIM_CHARSET_ASCII
, 0x0000, msg1
[i
], strlen(msg1
[i
]));
2757 switch (args
->type
) {
2758 case 0x01: { /* MacICQ message or basic offline message */
2760 gchar
*uin
= g_strdup_printf("%u", args
->uin
);
2763 /* If the message came from an ICQ user then escape any HTML */
2764 tmp
= g_markup_escape_text(msg2
[0], -1);
2766 if (t
) { /* This is an offline message */
2767 /* The timestamp is UTC-ish, so we need to get the offset */
2768 #ifdef HAVE_TM_GMTOFF
2772 tm
= localtime(&now
);
2775 # ifdef HAVE_TIMEZONE
2780 serv_got_im(gc
, uin
, tmp
, 0, t
);
2781 } else { /* This is a message from MacICQ/Miranda */
2782 serv_got_im(gc
, uin
, tmp
, 0, time(NULL
));
2789 case 0x04: { /* Someone sent you a URL */
2791 if (msg2
[1] != NULL
) {
2792 gchar
*uin
= g_strdup_printf("%u", args
->uin
);
2793 gchar
*message
= g_strdup_printf("<A HREF=\"%s\">%s</A>",
2795 (msg2
[0] && msg2
[0][0]) ? msg2
[0] : msg2
[1]);
2796 serv_got_im(gc
, uin
, message
, 0, time(NULL
));
2803 case 0x06: { /* Someone requested authorization */
2805 struct name_data
*data
= g_new(struct name_data
, 1);
2806 gchar
*bn
= g_strdup_printf("%u", args
->uin
);
2807 gchar
*reason
= NULL
;
2809 if (msg2
[5] != NULL
)
2810 reason
= purple_plugin_oscar_decode_im_part(account
, bn
, AIM_CHARSET_CUSTOM
, 0x0000, msg2
[5], strlen(msg2
[5]));
2812 purple_debug_info("oscar",
2813 "Received an authorization request from UIN %u\n",
2819 purple_account_request_authorization(account
, bn
, NULL
, NULL
,
2820 reason
, purple_find_buddy(account
, bn
) != NULL
,
2822 purple_auth_dontgrant_msgprompt
, data
);
2827 case 0x07: { /* Someone has denied you authorization */
2829 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."));
2830 purple_notify_info(gc
, NULL
, _("ICQ authorization denied."),
2836 case 0x08: { /* Someone has granted you authorization */
2837 gchar
*dialog_msg
= g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args
->uin
);
2838 purple_notify_info(gc
, NULL
, "ICQ authorization accepted.",
2843 case 0x09: { /* Message from the Godly ICQ server itself, I think */
2845 gchar
*dialog_msg
= g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2
[0], msg2
[3], msg2
[5]);
2846 purple_notify_info(gc
, NULL
, "ICQ Server Message", dialog_msg
);
2851 case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
2853 gchar
*dialog_msg
= g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2
[0], msg2
[3], msg2
[5]);
2854 purple_notify_info(gc
, NULL
, "ICQ Page", dialog_msg
);
2859 case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
2861 gchar
*dialog_msg
= g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2
[0], msg2
[3], msg2
[5]);
2862 purple_notify_info(gc
, NULL
, "ICQ Email", dialog_msg
);
2868 /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */
2869 /* Someone added you to their buddy list? */
2872 case 0x13: { /* Someone has sent you some ICQ buddies */
2875 text
= g_strsplit(args
->msg
, "\376", 0);
2877 /* Read the number of contacts that we were sent */
2879 num
= strtoul(text
[0], NULL
, 10);
2881 if (num
> 0 && errno
== 0) {
2882 for (i
=0; i
<num
; i
++) {
2883 struct name_data
*data
;
2886 if (!text
[i
*2 + 1] || !text
[i
*2 + 2]) {
2887 /* We're missing the contact name or nickname. Bail out. */
2888 gchar
*tmp
= g_strescape(args
->msg
, NULL
);
2889 purple_debug_error("oscar", "Unknown syntax parsing "
2890 "ICQ buddies. args->msg=%s\n", tmp
);
2895 message
= g_strdup_printf(_("ICQ user %u has sent you a buddy: %s (%s)"), args
->uin
, text
[i
*2+2], text
[i
*2+1]);
2897 data
= g_new(struct name_data
, 1);
2899 data
->name
= g_strdup(text
[i
*2+1]);
2900 data
->nick
= g_strdup(text
[i
*2+2]);
2902 purple_request_action(gc
, NULL
, message
,
2903 _("Do you want to add this buddy "
2904 "to your buddy list?"),
2905 PURPLE_DEFAULT_ACTION_NONE
,
2906 purple_connection_get_account(gc
), data
->name
, NULL
,
2908 _("_Add"), G_CALLBACK(purple_icq_buddyadd
),
2909 _("_Decline"), G_CALLBACK(oscar_free_name_data
));
2913 gchar
*tmp
= g_strescape(args
->msg
, NULL
);
2914 purple_debug_error("oscar", "Unknown syntax parsing "
2915 "ICQ buddies. args->msg=%s\n", tmp
);
2922 case 0x1a: { /* Handle SMS or someone has sent you a greeting card or requested buddies? */
2924 int smstype
, taglen
, smslen
;
2925 char *tagstr
= NULL
, *smsmsg
= NULL
;
2926 xmlnode
*xmlroot
= NULL
, *xmltmp
= NULL
;
2927 gchar
*uin
= NULL
, *message
= NULL
;
2929 /* From libicq2000-0.3.2/src/ICQ.cpp */
2930 byte_stream_init(&qbs
, (guint8
*)args
->msg
, args
->msglen
);
2931 byte_stream_advance(&qbs
, 21);
2932 /* expected: 01 00 00 20 00 0e 28 f6 00 11 e7 d3 11 bc f3 00 04 ac 96 9d c2 | 00 00 | 06 00 00 00 | 49 43 51 53 43 53 ...*/
2933 /* unexpected: 00 00 26 00 81 1a 18 bc 0e 6c 18 47 a5 91 6f 18 dc c7 6f 1a | 00 00 | 0d 00 00 00 | 49 43 51 57 65 62 4d 65 73 73 61 67 65 ... */
2934 smstype
= byte_stream_getle16(&qbs
);
2937 taglen
= byte_stream_getle32(&qbs
);
2938 tagstr
= byte_stream_getstr(&qbs
, taglen
);
2941 byte_stream_advance(&qbs
, 3);
2942 byte_stream_advance(&qbs
, 4);
2943 smslen
= byte_stream_getle32(&qbs
);
2944 smsmsg
= byte_stream_getstr(&qbs
, smslen
);
2946 /* Check if this is an SMS being sent from server */
2947 if ((smstype
== 0) && (!strcmp(tagstr
, "ICQSMS")) && (smsmsg
!= NULL
))
2949 xmlroot
= xmlnode_from_str(smsmsg
, -1);
2950 if (xmlroot
!= NULL
)
2952 xmltmp
= xmlnode_get_child(xmlroot
, "sender");
2954 uin
= xmlnode_get_data(xmltmp
);
2956 xmltmp
= xmlnode_get_child(xmlroot
, "text");
2958 message
= xmlnode_get_data(xmltmp
);
2960 if ((uin
!= NULL
) && (message
!= NULL
))
2961 serv_got_im(gc
, uin
, message
, 0, time(NULL
));
2965 xmlnode_free(xmlroot
);
2973 purple_debug_info("oscar",
2974 "Received a channel 4 message of unknown type "
2975 "(type 0x%02hhx).\n", args
->type
);
2985 static int purple_parse_incoming_im(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
2988 aim_userinfo_t
*userinfo
;
2992 channel
= (guint16
)va_arg(ap
, unsigned int);
2993 userinfo
= va_arg(ap
, aim_userinfo_t
*);
2996 case 1: { /* standard message */
2997 struct aim_incomingim_ch1_args
*args
;
2998 args
= va_arg(ap
, struct aim_incomingim_ch1_args
*);
2999 ret
= incomingim_chan1(od
, conn
, userinfo
, args
);
3002 case 2: { /* rendezvous */
3004 args
= va_arg(ap
, IcbmArgsCh2
*);
3005 ret
= incomingim_chan2(od
, conn
, userinfo
, args
);
3009 struct aim_incomingim_ch4_args
*args
;
3010 args
= va_arg(ap
, struct aim_incomingim_ch4_args
*);
3011 ret
= incomingim_chan4(od
, conn
, userinfo
, args
, 0);
3015 purple_debug_warning("oscar",
3016 "ICBM received on unsupported channel (channel "
3017 "0x%04hx).", channel
);
3026 static int purple_parse_misses(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3027 PurpleConnection
*gc
= od
->gc
;
3028 PurpleAccount
*account
= purple_connection_get_account(gc
);
3031 guint16 chan
, nummissed
, reason
;
3032 aim_userinfo_t
*userinfo
;
3035 chan
= (guint16
)va_arg(ap
, unsigned int);
3036 userinfo
= va_arg(ap
, aim_userinfo_t
*);
3037 nummissed
= (guint16
)va_arg(ap
, unsigned int);
3038 reason
= (guint16
)va_arg(ap
, unsigned int);
3042 case 0: /* Invalid (0) */
3043 buf
= g_strdup_printf(
3045 "You missed %hu message from %s because it was invalid.",
3046 "You missed %hu messages from %s because they were invalid.",
3051 case 1: /* Message too large */
3052 buf
= g_strdup_printf(
3054 "You missed %hu message from %s because it was too large.",
3055 "You missed %hu messages from %s because they were too large.",
3060 case 2: /* Rate exceeded */
3061 buf
= g_strdup_printf(
3063 "You missed %hu message from %s because the rate limit has been exceeded.",
3064 "You missed %hu messages from %s because the rate limit has been exceeded.",
3069 case 3: /* Evil Sender */
3070 buf
= g_strdup_printf(
3072 "You missed %hu message from %s because his/her warning level is too high.",
3073 "You missed %hu messages from %s because his/her warning level is too high.",
3078 case 4: /* Evil Receiver */
3079 buf
= g_strdup_printf(
3081 "You missed %hu message from %s because your warning level is too high.",
3082 "You missed %hu messages from %s because your warning level is too high.",
3088 buf
= g_strdup_printf(
3090 "You missed %hu message from %s for an unknown reason.",
3091 "You missed %hu messages from %s for an unknown reason.",
3098 if (!purple_conv_present_error(userinfo
->bn
, account
, buf
))
3099 purple_notify_error(od
->gc
, NULL
, buf
, NULL
);
3106 purple_parse_clientauto_ch2(OscarData
*od
, const char *who
, guint16 reason
, const guchar
*cookie
)
3108 if (reason
== 0x0003)
3110 /* Rendezvous was refused. */
3111 PeerConnection
*conn
;
3113 conn
= peer_connection_find_by_cookie(od
, who
, cookie
);
3117 purple_debug_info("oscar", "Received a rendezvous cancel message "
3118 "for a nonexistant connection from %s.\n", who
);
3122 peer_connection_destroy(conn
, OSCAR_DISCONNECT_REMOTE_REFUSED
, NULL
);
3127 purple_debug_warning("oscar", "Received an unknown rendezvous "
3128 "message from %s. Type 0x%04hx\n", who
, reason
);
3134 static int purple_parse_clientauto_ch4(OscarData
*od
, char *who
, guint16 reason
, guint32 state
, char *msg
) {
3135 PurpleConnection
*gc
= od
->gc
;
3138 case 0x0003: { /* Reply from an ICQ status message request */
3139 char *statusmsg
, **splitmsg
;
3140 PurpleNotifyUserInfo
*user_info
;
3142 /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
3143 statusmsg
= oscar_icqstatus(state
);
3144 splitmsg
= g_strsplit(msg
, "\r\n", 0);
3146 user_info
= purple_notify_user_info_new();
3148 purple_notify_user_info_add_pair(user_info
, _("UIN"), who
);
3149 purple_notify_user_info_add_pair(user_info
, _("Status"), statusmsg
);
3150 purple_notify_user_info_add_section_break(user_info
);
3151 purple_notify_user_info_add_pair(user_info
, NULL
, g_strjoinv("<BR>", splitmsg
));
3154 g_strfreev(splitmsg
);
3156 purple_notify_userinfo(gc
, who
, user_info
, NULL
, NULL
);
3157 purple_notify_user_info_destroy(user_info
);
3162 purple_debug_warning("oscar",
3163 "Received an unknown client auto-response from %s. "
3164 "Type 0x%04hx\n", who
, reason
);
3166 } /* end of switch */
3171 static int purple_parse_clientauto(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3173 guint16 chan
, reason
;
3177 chan
= (guint16
)va_arg(ap
, unsigned int);
3178 who
= va_arg(ap
, char *);
3179 reason
= (guint16
)va_arg(ap
, unsigned int);
3181 if (chan
== 0x0002) { /* File transfer declined */
3182 guchar
*cookie
= va_arg(ap
, guchar
*);
3183 return purple_parse_clientauto_ch2(od
, who
, reason
, cookie
);
3184 } else if (chan
== 0x0004) { /* ICQ message */
3187 if (reason
== 0x0003) {
3188 state
= va_arg(ap
, guint32
);
3189 msg
= va_arg(ap
, char *);
3191 return purple_parse_clientauto_ch4(od
, who
, reason
, state
, msg
);
3199 static int purple_parse_genericerr(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3204 reason
= (guint16
) va_arg(ap
, unsigned int);
3207 purple_debug_error("oscar",
3208 "snac threw error (reason 0x%04hx: %s)\n", reason
,
3209 (reason
< msgerrreasonlen
) ? msgerrreason
[reason
] : "unknown");
3213 static int purple_parse_msgerr(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3214 PurpleConnection
*gc
= od
->gc
;
3216 OscarData
*od
= purple_connection_get_protocol_data(gc
);
3224 reason
= (guint16
)va_arg(ap
, unsigned int);
3225 data
= va_arg(ap
, char *);
3228 purple_debug_error("oscar",
3229 "Message error with data %s and reason %hu\n",
3230 (data
!= NULL
? data
: ""), reason
);
3232 if ((data
== NULL
) || (*data
== '\0'))
3233 /* We can't do anything if data is empty */
3237 /* If this was a file transfer request, data is a cookie */
3238 if ((xfer
= oscar_find_xfer_by_cookie(od
->file_transfers
, data
))) {
3239 purple_xfer_cancel_remote(xfer
);
3244 /* Data is assumed to be the destination bn */
3245 buf
= g_strdup_printf(_("Unable to send message: %s"), (reason
< msgerrreasonlen
) ? _(msgerrreason
[reason
]) : _("Unknown reason."));
3246 if (!purple_conv_present_error(data
, purple_connection_get_account(gc
), buf
)) {
3248 buf
= g_strdup_printf(_("Unable to send message to %s:"), data
? data
: "(unknown)");
3249 purple_notify_error(od
->gc
, NULL
, buf
,
3250 (reason
< msgerrreasonlen
) ? _(msgerrreason
[reason
]) : _("Unknown reason."));
3257 static int purple_parse_mtn(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3258 PurpleConnection
*gc
= od
->gc
;
3260 guint16 type1
, type2
;
3264 type1
= (guint16
) va_arg(ap
, unsigned int);
3265 bn
= va_arg(ap
, char *);
3266 type2
= (guint16
) va_arg(ap
, unsigned int);
3270 case 0x0000: { /* Text has been cleared */
3271 serv_got_typing_stopped(gc
, bn
);
3274 case 0x0001: { /* Paused typing */
3275 serv_got_typing(gc
, bn
, 0, PURPLE_TYPED
);
3278 case 0x0002: { /* Typing */
3279 serv_got_typing(gc
, bn
, 0, PURPLE_TYPING
);
3284 * It looks like iChat sometimes sends typing notification
3285 * with type1=0x0001 and type2=0x000f. Not sure why.
3287 purple_debug_info("oscar", "Received unknown typing notification message from %s. Type1 is 0x%04x and type2 is 0x%04hx.\n", bn
, type1
, type2
);
3295 * We get this error when there was an error in the locate family. This
3296 * happens when you request info of someone who is offline.
3298 static int purple_parse_locerr(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3303 PurpleNotifyUserInfo
*user_info
;
3306 reason
= (guint16
) va_arg(ap
, unsigned int);
3307 destn
= va_arg(ap
, char *);
3313 user_info
= purple_notify_user_info_new();
3314 buf
= g_strdup_printf(_("User information not available: %s"), (reason
< msgerrreasonlen
) ? _(msgerrreason
[reason
]) : _("Unknown reason."));
3315 purple_notify_user_info_add_pair(user_info
, NULL
, buf
);
3316 purple_notify_userinfo(od
->gc
, destn
, user_info
, NULL
, NULL
);
3317 purple_notify_user_info_destroy(user_info
);
3318 purple_conv_present_error(destn
, purple_connection_get_account(od
->gc
), buf
);
3324 static int purple_parse_userinfo(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3325 PurpleConnection
*gc
= od
->gc
;
3326 PurpleAccount
*account
= purple_connection_get_account(gc
);
3327 PurpleNotifyUserInfo
*user_info
;
3328 gchar
*tmp
= NULL
, *info_utf8
= NULL
;
3330 aim_userinfo_t
*userinfo
;
3333 userinfo
= va_arg(ap
, aim_userinfo_t
*);
3336 user_info
= purple_notify_user_info_new();
3338 oscar_user_info_append_status(gc
, user_info
, /* PurpleBuddy */ NULL
, userinfo
, /* strip_html_tags */ FALSE
);
3340 if ((userinfo
->present
& AIM_USERINFO_PRESENT_IDLE
) && userinfo
->idletime
!= 0) {
3341 tmp
= purple_str_seconds_to_string(userinfo
->idletime
*60);
3342 oscar_user_info_add_pair(user_info
, _("Idle"), tmp
);
3346 oscar_user_info_append_extra_info(gc
, user_info
, NULL
, userinfo
);
3348 if ((userinfo
->present
& AIM_USERINFO_PRESENT_ONLINESINCE
) && !oscar_util_valid_name_sms(userinfo
->bn
)) {
3349 /* An SMS contact is always online; its Online Since value is not useful */
3350 time_t t
= userinfo
->onlinesince
;
3351 oscar_user_info_add_pair(user_info
, _("Online Since"), purple_date_format_full(localtime(&t
)));
3354 if (userinfo
->present
& AIM_USERINFO_PRESENT_MEMBERSINCE
) {
3355 time_t t
= userinfo
->membersince
;
3356 oscar_user_info_add_pair(user_info
, _("Member Since"), purple_date_format_full(localtime(&t
)));
3359 if (userinfo
->capabilities
!= 0) {
3360 tmp
= oscar_caps_to_string(userinfo
->capabilities
);
3361 oscar_user_info_add_pair(user_info
, _("Capabilities"), tmp
);
3366 if ((userinfo
->info_len
> 0) && (userinfo
->info
!= NULL
) && (userinfo
->info_encoding
!= NULL
)) {
3367 tmp
= oscar_encoding_extract(userinfo
->info_encoding
);
3368 info_utf8
= oscar_encoding_to_utf8(account
, tmp
, userinfo
->info
,
3369 userinfo
->info_len
);
3371 if (info_utf8
!= NULL
) {
3372 tmp
= purple_str_sub_away_formatters(info_utf8
, purple_account_get_username(account
));
3373 purple_notify_user_info_add_section_break(user_info
);
3374 oscar_user_info_add_pair(user_info
, _("Profile"), tmp
);
3380 purple_notify_user_info_add_section_break(user_info
);
3381 tmp
= g_strdup_printf("<a href=\"http://profiles.aim.com/%s\">%s</a>",
3382 purple_normalize(account
, userinfo
->bn
), _("View web profile"));
3383 purple_notify_user_info_add_pair(user_info
, NULL
, tmp
);
3386 purple_notify_userinfo(gc
, userinfo
->bn
, user_info
, NULL
, NULL
);
3387 purple_notify_user_info_destroy(user_info
);
3392 static int purple_parse_motd(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
3399 id
= (guint16
) va_arg(ap
, unsigned int);
3400 msg
= va_arg(ap
, char *);
3403 purple_debug_misc("oscar",
3404 "MOTD: %s (%hu)\n", msg
? msg
: "Unknown", id
);
3406 purple_notify_warning(od
->gc
, NULL
,
3407 _("Your AIM connection may be lost."), NULL
);
3412 static int purple_chatnav_info(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3417 type
= (guint16
) va_arg(ap
, unsigned int);
3422 struct aim_chat_exchangeinfo
*exchanges
;
3423 int exchangecount
, i
;
3425 maxrooms
= (guint8
) va_arg(ap
, unsigned int);
3426 exchangecount
= va_arg(ap
, int);
3427 exchanges
= va_arg(ap
, struct aim_chat_exchangeinfo
*);
3429 purple_debug_misc("oscar", "chat info: Chat Rights:\n");
3430 purple_debug_misc("oscar",
3431 "chat info: \tMax Concurrent Rooms: %hhd\n", maxrooms
);
3432 purple_debug_misc("oscar",
3433 "chat info: \tExchange List: (%d total)\n", exchangecount
);
3434 for (i
= 0; i
< exchangecount
; i
++)
3435 purple_debug_misc("oscar",
3436 "chat info: \t\t%hu %s\n",
3437 exchanges
[i
].number
, exchanges
[i
].name
? exchanges
[i
].name
: "");
3438 while (od
->create_rooms
) {
3439 struct create_room
*cr
= od
->create_rooms
->data
;
3440 purple_debug_info("oscar",
3441 "creating room %s\n", cr
->name
);
3442 aim_chatnav_createroom(od
, conn
, cr
->name
, cr
->exchange
);
3444 od
->create_rooms
= g_slist_remove(od
->create_rooms
, cr
);
3450 char *fqcn
, *name
, *ck
;
3451 guint16 instance
, flags
, maxmsglen
, maxoccupancy
, unknown
, exchange
;
3455 fqcn
= va_arg(ap
, char *);
3456 instance
= (guint16
)va_arg(ap
, unsigned int);
3457 exchange
= (guint16
)va_arg(ap
, unsigned int);
3458 flags
= (guint16
)va_arg(ap
, unsigned int);
3459 createtime
= va_arg(ap
, guint32
);
3460 maxmsglen
= (guint16
)va_arg(ap
, unsigned int);
3461 maxoccupancy
= (guint16
)va_arg(ap
, unsigned int);
3462 createperms
= (guint8
)va_arg(ap
, unsigned int);
3463 unknown
= (guint16
)va_arg(ap
, unsigned int);
3464 name
= va_arg(ap
, char *);
3465 ck
= va_arg(ap
, char *);
3467 purple_debug_misc("oscar",
3468 "created room: %s %hu %hu %hu %u %hu %hu %hhu %hu %s %s\n",
3469 fqcn
, exchange
, instance
, flags
, createtime
,
3470 maxmsglen
, maxoccupancy
, createperms
, unknown
,
3472 aim_chat_join(od
, exchange
, ck
, instance
);
3476 purple_debug_warning("oscar",
3477 "chatnav info: unknown type (%04hx)\n", type
);
3486 static int purple_conv_chat_join(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3489 aim_userinfo_t
*info
;
3490 PurpleConnection
*gc
= od
->gc
;
3492 struct chat_connection
*c
= NULL
;
3495 count
= va_arg(ap
, int);
3496 info
= va_arg(ap
, aim_userinfo_t
*);
3499 c
= find_oscar_chat_by_conn(gc
, conn
);
3503 for (i
= 0; i
< count
; i
++)
3504 purple_conv_chat_add_user(PURPLE_CONV_CHAT(c
->conv
), info
[i
].bn
, NULL
, PURPLE_CBFLAGS_NONE
, TRUE
);
3509 static int purple_conv_chat_leave(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3512 aim_userinfo_t
*info
;
3513 PurpleConnection
*gc
= od
->gc
;
3515 struct chat_connection
*c
= NULL
;
3518 count
= va_arg(ap
, int);
3519 info
= va_arg(ap
, aim_userinfo_t
*);
3522 c
= find_oscar_chat_by_conn(gc
, conn
);
3526 for (i
= 0; i
< count
; i
++)
3527 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c
->conv
), info
[i
].bn
, NULL
);
3532 static int purple_conv_chat_info_update(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3534 aim_userinfo_t
*userinfo
;
3535 struct aim_chat_roominfo
*roominfo
;
3539 guint16 unknown_c9
, unknown_d2
, unknown_d5
, maxmsglen
, maxvisiblemsglen
;
3540 guint32 creationtime
;
3541 PurpleConnection
*gc
= od
->gc
;
3542 struct chat_connection
*ccon
= find_oscar_chat_by_conn(gc
, conn
);
3548 roominfo
= va_arg(ap
, struct aim_chat_roominfo
*);
3549 roomname
= va_arg(ap
, char *);
3550 usercount
= va_arg(ap
, int);
3551 userinfo
= va_arg(ap
, aim_userinfo_t
*);
3552 roomdesc
= va_arg(ap
, char *);
3553 unknown_c9
= (guint16
)va_arg(ap
, unsigned int);
3554 creationtime
= va_arg(ap
, guint32
);
3555 maxmsglen
= (guint16
)va_arg(ap
, unsigned int);
3556 unknown_d2
= (guint16
)va_arg(ap
, unsigned int);
3557 unknown_d5
= (guint16
)va_arg(ap
, unsigned int);
3558 maxvisiblemsglen
= (guint16
)va_arg(ap
, unsigned int);
3561 purple_debug_misc("oscar",
3562 "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n",
3563 maxmsglen
, maxvisiblemsglen
);
3565 ccon
->maxlen
= maxmsglen
;
3566 ccon
->maxvis
= maxvisiblemsglen
;
3571 static int purple_conv_chat_incoming_msg(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3572 PurpleConnection
*gc
= od
->gc
;
3573 PurpleAccount
*account
= purple_connection_get_account(gc
);
3574 struct chat_connection
*ccon
= find_oscar_chat_by_conn(gc
, conn
);
3577 aim_userinfo_t
*info
;
3586 info
= va_arg(ap
, aim_userinfo_t
*);
3587 len
= va_arg(ap
, int);
3588 msg
= va_arg(ap
, char *);
3589 charset
= va_arg(ap
, char *);
3592 utf8
= oscar_encoding_to_utf8(account
, charset
, msg
, len
);
3594 /* The conversion failed! */
3595 utf8
= g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]"));
3596 serv_got_chat_in(gc
, ccon
->id
, info
->bn
, 0, utf8
, time((time_t)NULL
));
3602 static int purple_email_parseupdate(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3604 PurpleConnection
*gc
;
3605 PurpleAccount
*account
;
3606 struct aim_emailinfo
*emailinfo
;
3608 char *alertitle
, *alerturl
;
3611 account
= purple_connection_get_account(gc
);
3614 emailinfo
= va_arg(ap
, struct aim_emailinfo
*);
3615 havenewmail
= va_arg(ap
, int);
3616 alertitle
= va_arg(ap
, char *);
3617 alerturl
= va_arg(ap
, char *);
3620 if (account
!= NULL
&& emailinfo
!= NULL
&& purple_account_get_check_mail(account
) &&
3621 emailinfo
->unread
&& havenewmail
) {
3622 gchar
*to
= g_strdup_printf("%s%s%s",
3623 purple_account_get_username(account
),
3624 emailinfo
->domain
? "@" : "",
3625 emailinfo
->domain
? emailinfo
->domain
: "");
3626 const char *tos
[2] = { to
};
3627 const char *urls
[2] = { emailinfo
->url
};
3628 purple_notify_emails(gc
, emailinfo
->nummsgs
, FALSE
, NULL
, NULL
,
3629 tos
, urls
, NULL
, NULL
);
3634 purple_debug_misc("oscar", "Got an alert '%s' %s\n", alertitle
, alerturl
? alerturl
: "");
3639 static int purple_icon_parseicon(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3640 PurpleConnection
*gc
= od
->gc
;
3643 guint8 iconcsumtype
, *iconcsum
, *icon
;
3644 guint16 iconcsumlen
, iconlen
;
3647 bn
= va_arg(ap
, char *);
3648 iconcsumtype
= va_arg(ap
, int);
3649 iconcsum
= va_arg(ap
, guint8
*);
3650 iconcsumlen
= va_arg(ap
, int);
3651 icon
= va_arg(ap
, guint8
*);
3652 iconlen
= va_arg(ap
, int);
3656 * Some AIM clients will send a blank GIF image with iconlen 90 when
3657 * no icon is set. Ignore these.
3659 if ((iconlen
> 0) && (iconlen
!= 90)) {
3660 char *b16
= purple_base16_encode(iconcsum
, iconcsumlen
);
3661 purple_buddy_icons_set_for_user(purple_connection_get_account(gc
),
3662 bn
, g_memdup(icon
, iconlen
), iconlen
, b16
);
3670 purple_icons_fetch(PurpleConnection
*gc
)
3672 OscarData
*od
= purple_connection_get_protocol_data(gc
);
3673 aim_userinfo_t
*userinfo
;
3674 FlapConnection
*conn
;
3676 conn
= flap_connection_getbytype(od
, SNAC_FAMILY_BART
);
3678 if (!od
->iconconnecting
) {
3679 aim_srv_requestnew(od
, SNAC_FAMILY_BART
);
3680 od
->iconconnecting
= TRUE
;
3686 PurpleAccount
*account
= purple_connection_get_account(gc
);
3687 PurpleStoredImage
*img
= purple_buddy_icons_find_account_icon(account
);
3689 aim_ssi_delicon(od
);
3691 purple_debug_info("oscar",
3692 "Uploading icon to icon server\n");
3693 aim_bart_upload(od
, purple_imgstore_get_data(img
),
3694 purple_imgstore_get_size(img
));
3695 purple_imgstore_unref(img
);
3697 od
->set_icon
= FALSE
;
3700 while (od
->requesticon
!= NULL
)
3702 userinfo
= aim_locate_finduserinfo(od
, (char *)od
->requesticon
->data
);
3703 if ((userinfo
!= NULL
) && (userinfo
->iconcsumlen
> 0))
3704 aim_bart_request(od
, od
->requesticon
->data
, userinfo
->iconcsumtype
, userinfo
->iconcsum
, userinfo
->iconcsumlen
);
3706 g_free(od
->requesticon
->data
);
3707 od
->requesticon
= g_slist_delete_link(od
->requesticon
, od
->requesticon
);
3710 purple_debug_misc("oscar", "no more icons to request\n");
3714 * Received in response to an IM sent with the AIM_IMFLAGS_ACK option.
3716 static int purple_parse_msgack(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3722 type
= (guint16
) va_arg(ap
, unsigned int);
3723 bn
= va_arg(ap
, char *);
3726 purple_debug_info("oscar", "Sent message to %s.\n", bn
);
3731 static int purple_parse_ratechange(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3732 static const char *codes
[5] = {
3740 guint16 code
, rateclass
;
3741 guint32 windowsize
, clear
, alert
, limit
, disconnect
, currentavg
, maxavg
;
3744 code
= (guint16
)va_arg(ap
, unsigned int);
3745 rateclass
= (guint16
)va_arg(ap
, unsigned int);
3746 windowsize
= va_arg(ap
, guint32
);
3747 clear
= va_arg(ap
, guint32
);
3748 alert
= va_arg(ap
, guint32
);
3749 limit
= va_arg(ap
, guint32
);
3750 disconnect
= va_arg(ap
, guint32
);
3751 currentavg
= va_arg(ap
, guint32
);
3752 maxavg
= va_arg(ap
, guint32
);
3755 purple_debug_misc("oscar",
3756 "rate %s (param ID 0x%04hx): curavg = %u, maxavg = %u, alert at %u, "
3757 "clear warning at %u, limit at %u, disconnect at %u (window size = %u)\n",
3758 (code
< 5) ? codes
[code
] : codes
[0],
3765 if (code
== AIM_RATE_CODE_LIMIT
)
3767 purple_debug_warning("oscar", _("The last action you attempted could not be "
3768 "performed because you are over the rate limit. "
3769 "Please wait 10 seconds and try again."));
3775 static int purple_parse_evilnotify(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3776 #ifdef CRAZY_WARNING
3779 aim_userinfo_t
*userinfo
;
3782 newevil
= (guint16
) va_arg(ap
, unsigned int);
3783 userinfo
= va_arg(ap
, aim_userinfo_t
*);
3786 purple_prpl_got_account_warning_level(account
, (userinfo
&& userinfo
->bn
) ? userinfo
->bn
: NULL
, (newevil
/10.0) + 0.5);
3792 static int purple_selfinfo(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3795 aim_userinfo_t
*info
;
3798 info
= va_arg(ap
, aim_userinfo_t
*);
3801 purple_connection_set_display_name(od
->gc
, info
->bn
);
3804 * What's with the + 0.5?
3805 * The 0.5 is basically poor-man's rounding. Normally
3806 * casting "13.7" to an int will truncate to "13," but
3807 * with 13.7 + 0.5 = 14.2, which becomes "14" when
3810 warning_level
= info
->warnlevel
/10.0 + 0.5;
3812 #ifdef CRAZY_WARNING
3813 purple_presence_set_warning_level(presence
, warning_level
);
3819 static int purple_connerr(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3820 PurpleConnection
*gc
= od
->gc
;
3826 code
= (guint16
)va_arg(ap
, int);
3827 msg
= va_arg(ap
, char *);
3830 purple_debug_info("oscar", "Disconnected. Code is 0x%04x and msg is %s\n",
3831 code
, (msg
!= NULL
? msg
: ""));
3833 g_return_val_if_fail(conn
!= NULL
, 1);
3835 if (conn
->type
== SNAC_FAMILY_CHAT
) {
3836 struct chat_connection
*cc
;
3837 PurpleConversation
*conv
= NULL
;
3839 cc
= find_oscar_chat_by_conn(gc
, conn
);
3842 conv
= purple_find_chat(gc
, cc
->id
);
3847 * TOOD: Have flap_connection_destroy_cb() send us the
3848 * error message stored in 'tmp', which should be
3849 * human-friendly, and print that to the chat room.
3852 buf
= g_strdup_printf(_("You have been disconnected from chat "
3853 "room %s."), cc
->name
);
3854 purple_conversation_write(conv
, NULL
, buf
, PURPLE_MESSAGE_ERROR
, time(NULL
));
3857 oscar_chat_kill(gc
, cc
);
3864 static int purple_parse_locaterights(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
3866 PurpleConnection
*gc
= od
->gc
;
3867 PurpleAccount
*account
= purple_connection_get_account(gc
);
3872 maxsiglen
= (guint16
) va_arg(ap
, int);
3875 purple_debug_misc("oscar",
3876 "locate rights: max sig len = %d\n", maxsiglen
);
3878 od
->rights
.maxsiglen
= od
->rights
.maxawaymsglen
= (guint
)maxsiglen
;
3880 aim_locate_setcaps(od
, purple_caps
);
3881 oscar_set_info_and_status(account
, TRUE
, account
->user_info
, TRUE
,
3882 purple_account_get_active_status(account
));
3887 static int purple_parse_buddyrights(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3889 guint16 maxbuddies
, maxwatchers
;
3892 maxbuddies
= (guint16
) va_arg(ap
, unsigned int);
3893 maxwatchers
= (guint16
) va_arg(ap
, unsigned int);
3896 purple_debug_misc("oscar",
3897 "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies
, maxwatchers
);
3899 od
->rights
.maxbuddies
= (guint
)maxbuddies
;
3900 od
->rights
.maxwatchers
= (guint
)maxwatchers
;
3905 static int purple_bosrights(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3906 PurpleConnection
*gc
;
3907 PurpleAccount
*account
;
3908 PurpleStatus
*status
;
3909 gboolean is_available
;
3910 PurplePresence
*presence
;
3911 const char *username
, *message
, *itmsurl
;
3914 guint16 maxpermits
, maxdenies
;
3917 od
= purple_connection_get_protocol_data(gc
);
3918 account
= purple_connection_get_account(gc
);
3921 maxpermits
= (guint16
) va_arg(ap
, unsigned int);
3922 maxdenies
= (guint16
) va_arg(ap
, unsigned int);
3925 purple_debug_misc("oscar",
3926 "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits
, maxdenies
);
3928 od
->rights
.maxpermits
= (guint
)maxpermits
;
3929 od
->rights
.maxdenies
= (guint
)maxdenies
;
3931 purple_debug_info("oscar", "buddy list loaded\n");
3933 if (purple_account_get_user_info(account
) != NULL
)
3934 serv_set_info(gc
, purple_account_get_user_info(account
));
3936 username
= purple_account_get_username(account
);
3937 if (!od
->icq
&& strcmp(username
, purple_connection_get_display_name(gc
)) != 0)
3939 * Format the username for AIM accounts if it's different
3940 * than what's currently set.
3942 oscar_format_username(gc
, username
);
3944 /* Set our available message based on the current status */
3945 status
= purple_account_get_active_status(account
);
3946 is_available
= purple_status_is_available(status
);
3948 message
= purple_status_get_attr_string(status
, "message");
3951 tmp
= purple_markup_strip_html(message
);
3952 itmsurl
= purple_status_get_attr_string(status
, "itmsurl");
3953 aim_srv_setextrainfo(od
, FALSE
, 0, is_available
, tmp
, itmsurl
);
3956 presence
= purple_status_get_presence(status
);
3957 aim_srv_setidle(od
, !purple_presence_is_idle(presence
) ? 0 : time(NULL
) - purple_presence_get_idle_time(presence
));
3959 /* Request offline messages for AIM and ICQ */
3960 aim_im_reqofflinemsgs(od
);
3963 #ifdef OLDSTYLE_ICQ_OFFLINEMSGS
3964 aim_icq_reqofflinemsgs(od
);
3966 oscar_set_extendedstatus(gc
);
3967 aim_icq_setsecurity(od
,
3968 purple_account_get_bool(account
, "authorization", OSCAR_DEFAULT_AUTHORIZATION
),
3969 purple_account_get_bool(account
, "web_aware", OSCAR_DEFAULT_WEB_AWARE
));
3972 aim_srv_requestnew(od
, SNAC_FAMILY_ALERT
);
3973 aim_srv_requestnew(od
, SNAC_FAMILY_CHATNAV
);
3975 od
->bos
.have_rights
= TRUE
;
3978 * If we've already received our feedbag data then we're not waiting on
3979 * anything else, so send the server clientready.
3981 * Normally we get bos rights before we get our feedbag data, so this
3982 * rarely (never?) happens. And I'm not sure it actually matters if we
3983 * wait for bos rights before calling clientready. But it seems safer
3984 * to do it this way.
3986 if (od
->ssi
.received_data
) {
3987 aim_srv_clientready(od
, conn
);
3988 purple_connection_set_state(gc
, PURPLE_CONNECTED
);
3994 #ifdef OLDSTYLE_ICQ_OFFLINEMSGS
3995 static int purple_offlinemsg(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
3997 struct aim_icq_offlinemsg
*msg
;
3998 struct aim_incomingim_ch4_args args
;
4002 msg
= va_arg(ap
, struct aim_icq_offlinemsg
*);
4005 purple_debug_info("oscar",
4006 "Received offline message. Converting to channel 4 ICBM...\n");
4007 args
.uin
= msg
->sender
;
4008 args
.type
= msg
->type
;
4009 args
.flags
= msg
->flags
;
4010 args
.msglen
= msg
->msglen
;
4011 args
.msg
= msg
->msg
;
4012 t
= purple_time_build(msg
->year
, msg
->month
, msg
->day
, msg
->hour
, msg
->minute
, 0);
4013 incomingim_chan4(od
, conn
, NULL
, &args
, t
);
4018 static int purple_offlinemsgdone(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
4020 aim_icq_ackofflinemsgs(od
);
4023 #endif /* OLDSTYLE_ICQ_OFFLINEMSGS */
4025 static int purple_icqinfo(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
4027 PurpleConnection
*gc
;
4028 PurpleAccount
*account
;
4030 struct buddyinfo
*bi
;
4032 PurpleNotifyUserInfo
*user_info
;
4037 struct aim_icq_info
*info
;
4040 account
= purple_connection_get_account(gc
);
4043 info
= va_arg(ap
, struct aim_icq_info
*);
4049 user_info
= purple_notify_user_info_new();
4051 g_snprintf(who
, sizeof(who
), "%u", info
->uin
);
4052 buddy
= purple_find_buddy(account
, who
);
4054 bi
= g_hash_table_lookup(od
->buddyinfo
, purple_normalize(account
, purple_buddy_get_name(buddy
)));
4058 purple_notify_user_info_add_pair(user_info
, _("UIN"), who
);
4059 oscar_user_info_convert_and_add(account
, user_info
, _("Nick"), info
->nick
);
4060 if ((bi
!= NULL
) && (bi
->ipaddr
!= 0)) {
4061 char *tstr
= g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
4062 (bi
->ipaddr
& 0xff000000) >> 24,
4063 (bi
->ipaddr
& 0x00ff0000) >> 16,
4064 (bi
->ipaddr
& 0x0000ff00) >> 8,
4065 (bi
->ipaddr
& 0x000000ff));
4066 purple_notify_user_info_add_pair(user_info
, _("IP Address"), tstr
);
4069 oscar_user_info_convert_and_add(account
, user_info
, _("First Name"), info
->first
);
4070 oscar_user_info_convert_and_add(account
, user_info
, _("Last Name"), info
->last
);
4071 if (info
->email
&& info
->email
[0] && (utf8
= oscar_utf8_try_convert(account
, info
->email
))) {
4072 buf
= g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8
, utf8
);
4073 purple_notify_user_info_add_pair(user_info
, _("Email Address"), buf
);
4077 if (info
->numaddresses
&& info
->email2
) {
4079 for (i
= 0; i
< info
->numaddresses
; i
++) {
4080 if (info
->email2
[i
] && info
->email2
[i
][0] && (utf8
= oscar_utf8_try_convert(account
, info
->email2
[i
]))) {
4081 buf
= g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8
, utf8
);
4082 purple_notify_user_info_add_pair(user_info
, _("Email Address"), buf
);
4088 oscar_user_info_convert_and_add(account
, user_info
, _("Mobile Phone"), info
->mobile
);
4090 if (info
->gender
!= 0)
4091 purple_notify_user_info_add_pair(user_info
, _("Gender"), (info
->gender
== 1 ? _("Female") : _("Male")));
4093 if ((info
->birthyear
> 1900) && (info
->birthmonth
> 0) && (info
->birthday
> 0)) {
4094 /* Initialize the struct properly or strftime() will crash
4095 * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
4096 time_t t
= time(NULL
);
4097 struct tm
*tm
= localtime(&t
);
4099 tm
->tm_mday
= (int)info
->birthday
;
4100 tm
->tm_mon
= (int)info
->birthmonth
- 1;
4101 tm
->tm_year
= (int)info
->birthyear
- 1900;
4103 /* To be 100% sure that the fields are re-normalized.
4104 * If you're sure strftime() ALWAYS does this EVERYWHERE,
4105 * feel free to remove it. --rlaager */
4108 oscar_user_info_convert_and_add(account
, user_info
, _("Birthday"), purple_date_format_short(tm
));
4110 if ((info
->age
> 0) && (info
->age
< 255)) {
4112 snprintf(age
, sizeof(age
), "%hhd", info
->age
);
4113 purple_notify_user_info_add_pair(user_info
, _("Age"), age
);
4115 if (info
->personalwebpage
&& info
->personalwebpage
[0] && (utf8
= oscar_utf8_try_convert(account
, info
->personalwebpage
))) {
4116 buf
= g_strdup_printf("<a href=\"%s\">%s</a>", utf8
, utf8
);
4117 purple_notify_user_info_add_pair(user_info
, _("Personal Web Page"), buf
);
4123 oscar_user_info_append_status(gc
, user_info
, buddy
, /* aim_userinfo_t */ NULL
, /* strip_html_tags */ FALSE
);
4125 oscar_user_info_convert_and_add(account
, user_info
, _("Additional Information"), info
->info
);
4126 purple_notify_user_info_add_section_break(user_info
);
4128 if ((info
->homeaddr
&& (info
->homeaddr
[0])) || (info
->homecity
&& info
->homecity
[0]) || (info
->homestate
&& info
->homestate
[0]) || (info
->homezip
&& info
->homezip
[0])) {
4129 purple_notify_user_info_add_section_header(user_info
, _("Home Address"));
4131 oscar_user_info_convert_and_add(account
, user_info
, _("Address"), info
->homeaddr
);
4132 oscar_user_info_convert_and_add(account
, user_info
, _("City"), info
->homecity
);
4133 oscar_user_info_convert_and_add(account
, user_info
, _("State"), info
->homestate
);
4134 oscar_user_info_convert_and_add(account
, user_info
, _("Zip Code"), info
->homezip
);
4136 if ((info
->workaddr
&& info
->workaddr
[0]) || (info
->workcity
&& info
->workcity
[0]) || (info
->workstate
&& info
->workstate
[0]) || (info
->workzip
&& info
->workzip
[0])) {
4137 purple_notify_user_info_add_section_header(user_info
, _("Work Address"));
4139 oscar_user_info_convert_and_add(account
, user_info
, _("Address"), info
->workaddr
);
4140 oscar_user_info_convert_and_add(account
, user_info
, _("City"), info
->workcity
);
4141 oscar_user_info_convert_and_add(account
, user_info
, _("State"), info
->workstate
);
4142 oscar_user_info_convert_and_add(account
, user_info
, _("Zip Code"), info
->workzip
);
4144 if ((info
->workcompany
&& info
->workcompany
[0]) || (info
->workdivision
&& info
->workdivision
[0]) || (info
->workposition
&& info
->workposition
[0]) || (info
->workwebpage
&& info
->workwebpage
[0])) {
4145 purple_notify_user_info_add_section_header(user_info
, _("Work Information"));
4147 oscar_user_info_convert_and_add(account
, user_info
, _("Company"), info
->workcompany
);
4148 oscar_user_info_convert_and_add(account
, user_info
, _("Division"), info
->workdivision
);
4149 oscar_user_info_convert_and_add(account
, user_info
, _("Position"), info
->workposition
);
4151 if (info
->workwebpage
&& info
->workwebpage
[0] && (utf8
= oscar_utf8_try_convert(account
, info
->workwebpage
))) {
4152 char *webpage
= g_strdup_printf("<a href=\"%s\">%s</a>", utf8
, utf8
);
4153 purple_notify_user_info_add_pair(user_info
, _("Web Page"), webpage
);
4160 alias
= purple_buddy_get_alias(buddy
);
4163 purple_notify_userinfo(gc
, who
, user_info
, NULL
, NULL
);
4164 purple_notify_user_info_destroy(user_info
);
4169 static int purple_icqalias(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
4171 PurpleConnection
*gc
= od
->gc
;
4172 PurpleAccount
*account
= purple_connection_get_account(gc
);
4173 gchar who
[16], *utf8
;
4176 struct aim_icq_info
*info
;
4179 info
= va_arg(ap
, struct aim_icq_info
*);
4182 if (info
->uin
&& info
->nick
&& info
->nick
[0] && (utf8
= oscar_utf8_try_convert(account
, info
->nick
))) {
4183 g_snprintf(who
, sizeof(who
), "%u", info
->uin
);
4184 serv_got_alias(gc
, who
, utf8
);
4185 if ((b
= purple_find_buddy(account
, who
))) {
4186 purple_blist_node_set_string((PurpleBlistNode
*)b
, "servernick", utf8
);
4194 static int purple_popup(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
4196 PurpleConnection
*gc
= od
->gc
;
4200 guint16 wid
, hei
, delay
;
4203 msg
= va_arg(ap
, char *);
4204 url
= va_arg(ap
, char *);
4205 wid
= (guint16
) va_arg(ap
, int);
4206 hei
= (guint16
) va_arg(ap
, int);
4207 delay
= (guint16
) va_arg(ap
, int);
4210 text
= g_strdup_printf("%s<br><a href=\"%s\">%s</a>", msg
, url
, url
);
4211 purple_notify_formatted(gc
, NULL
, _("Pop-Up Message"), NULL
, text
, NULL
, NULL
);
4217 static void oscar_searchresults_add_buddy_cb(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4219 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
4220 g_list_nth_data(row
, 0), NULL
, NULL
);
4223 static int purple_parse_searchreply(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
4225 PurpleConnection
*gc
= od
->gc
;
4226 PurpleNotifySearchResults
*results
;
4227 PurpleNotifySearchColumn
*column
;
4231 char *email
, *usernames
;
4234 email
= va_arg(ap
, char *);
4235 num
= va_arg(ap
, int);
4236 usernames
= va_arg(ap
, char *);
4239 results
= purple_notify_searchresults_new();
4241 if (results
== NULL
) {
4242 purple_debug_error("oscar", "purple_parse_searchreply: "
4243 "Unable to display the search results.\n");
4244 purple_notify_error(gc
, NULL
,
4245 _("Unable to display the search results."),
4250 secondary
= g_strdup_printf(
4251 dngettext(PACKAGE
, "The following username is associated with %s",
4252 "The following usernames are associated with %s",
4256 column
= purple_notify_searchresults_column_new(_("Username"));
4257 purple_notify_searchresults_column_add(results
, column
);
4259 for (i
= 0; i
< num
; i
++) {
4261 row
= g_list_append(NULL
, g_strdup(&usernames
[i
* (MAXSNLEN
+ 1)]));
4262 purple_notify_searchresults_row_add(results
, row
);
4264 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
,
4265 oscar_searchresults_add_buddy_cb
);
4266 purple_notify_searchresults(gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
4273 static int purple_parse_searcherror(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
4279 email
= va_arg(ap
, char *);
4282 buf
= g_strdup_printf(_("No results found for email address %s"), email
);
4283 purple_notify_error(od
->gc
, NULL
, buf
, NULL
);
4289 static int purple_account_confirm(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
4290 PurpleConnection
*gc
= od
->gc
;
4296 status
= (guint16
) va_arg(ap
, unsigned int); /* status code of confirmation request */
4299 purple_debug_info("oscar",
4300 "account confirmation returned status 0x%04x (%s)\n", status
,
4301 status
? "unknown" : "email sent");
4303 g_snprintf(msg
, sizeof(msg
), _("You should receive an email asking to confirm %s."),
4304 purple_account_get_username(purple_connection_get_account(gc
)));
4305 purple_notify_info(gc
, NULL
, _("Account Confirmation Requested"), msg
);
4311 static int purple_info_change(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
4312 PurpleConnection
*gc
= od
->gc
;
4315 char *url
, *bn
, *email
;
4319 change
= va_arg(ap
, int);
4320 perms
= (guint16
) va_arg(ap
, unsigned int);
4321 err
= (guint16
) va_arg(ap
, unsigned int);
4322 url
= va_arg(ap
, char *);
4323 bn
= va_arg(ap
, char *);
4324 email
= va_arg(ap
, char *);
4327 purple_debug_misc("oscar",
4328 "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, bn=%s, email=%s\n",
4329 change
? "change" : "request", perms
, err
,
4330 (url
!= NULL
) ? url
: "(null)",
4331 (bn
!= NULL
) ? bn
: "(null)",
4332 (email
!= NULL
) ? email
: "(null)");
4334 if ((err
> 0) && (url
!= NULL
)) {
4338 dialog_msg
= g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name differs from the original."), err
);
4339 else if (err
== 0x0006)
4340 dialog_msg
= g_strdup_printf(_("Error 0x%04x: Unable to format username because it is invalid."), err
);
4341 else if (err
== 0x00b)
4342 dialog_msg
= g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name is too long."), err
);
4343 else if (err
== 0x001d)
4344 dialog_msg
= g_strdup_printf(_("Error 0x%04x: Unable to change email address because there is already a request pending for this username."), err
);
4345 else if (err
== 0x0021)
4346 dialog_msg
= g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address has too many usernames associated with it."), err
);
4347 else if (err
== 0x0023)
4348 dialog_msg
= g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err
);
4350 dialog_msg
= g_strdup_printf(_("Error 0x%04x: Unknown error."), err
);
4351 purple_notify_error(gc
, NULL
,
4352 _("Error Changing Account Info"), dialog_msg
);
4357 if (email
!= NULL
) {
4358 char *dialog_msg
= g_strdup_printf(_("The email address for %s is %s"),
4359 purple_account_get_username(purple_connection_get_account(gc
)), email
);
4360 purple_notify_info(gc
, NULL
, _("Account Info"), dialog_msg
);
4368 oscar_keepalive(PurpleConnection
*gc
)
4371 FlapConnection
*conn
;
4373 od
= purple_connection_get_protocol_data(gc
);
4374 conn
= flap_connection_getbytype(od
, SNAC_FAMILY_LOCATE
);
4376 flap_connection_send_keepalive(od
, conn
);
4380 oscar_send_typing(PurpleConnection
*gc
, const char *name
, PurpleTypingState state
)
4383 PeerConnection
*conn
;
4385 od
= purple_connection_get_protocol_data(gc
);
4386 conn
= peer_connection_find_by_type(od
, name
, OSCAR_CAPABILITY_DIRECTIM
);
4388 if ((conn
!= NULL
) && (conn
->ready
))
4390 peer_odc_send_typing(conn
, state
);
4393 /* Don't send if this turkey is in our deny list */
4395 for (list
=gc
->account
->deny
; (list
&& oscar_util_name_compare(name
, list
->data
)); list
=list
->next
);
4397 struct buddyinfo
*bi
= g_hash_table_lookup(od
->buddyinfo
, purple_normalize(gc
->account
, name
));
4398 if (bi
&& bi
->typingnot
) {
4399 if (state
== PURPLE_TYPING
)
4400 aim_im_sendmtn(od
, 0x0001, name
, 0x0002);
4401 else if (state
== PURPLE_TYPED
)
4402 aim_im_sendmtn(od
, 0x0001, name
, 0x0001);
4404 aim_im_sendmtn(od
, 0x0001, name
, 0x0000);
4411 /* TODO: Move this into odc.c! */
4413 purple_odc_send_im(PeerConnection
*conn
, const char *message
, PurpleMessageFlags imflags
)
4419 guint16 charset
, charsubset
;
4421 const char *start
, *end
, *last
;
4424 msg
= g_string_new("<HTML><BODY>");
4425 data
= g_string_new("<BINARY>");
4428 /* for each valid IMG tag... */
4429 while (last
&& *last
&& purple_markup_find_tag("img", last
, &start
, &end
, &attribs
))
4431 PurpleStoredImage
*image
= NULL
;
4435 g_string_append_len(msg
, last
, start
- last
);
4438 id
= g_datalist_get_data(&attribs
, "id");
4440 /* ... if it refers to a valid purple image ... */
4441 if (id
&& (image
= purple_imgstore_find_by_id(atoi(id
)))) {
4442 /* ... append the message from start to the tag ... */
4443 unsigned long size
= purple_imgstore_get_size(image
);
4444 const char *filename
= purple_imgstore_get_filename(image
);
4445 gconstpointer imgdata
= purple_imgstore_get_data(image
);
4449 /* ... insert a new img tag with the oscar id ... */
4451 g_string_append_printf(msg
,
4452 "<IMG SRC=\"%s\" ID=\"%d\" DATASIZE=\"%lu\">",
4453 filename
, oscar_id
, size
);
4455 g_string_append_printf(msg
,
4456 "<IMG ID=\"%d\" DATASIZE=\"%lu\">",
4459 /* ... and append the data to the binary section ... */
4460 g_string_append_printf(data
, "<DATA ID=\"%d\" SIZE=\"%lu\">",
4462 g_string_append_len(data
, imgdata
, size
);
4463 g_string_append(data
, "</DATA>");
4465 /* If the tag is invalid, skip it, thus no else here */
4467 g_datalist_clear(&attribs
);
4469 /* continue from the end of the tag */
4473 /* append any remaining message data */
4475 g_string_append(msg
, last
);
4477 g_string_append(msg
, "</BODY></HTML>");
4479 /* Convert the message to a good encoding */
4480 purple_plugin_oscar_convert_to_best_encoding(conn
->od
->gc
,
4481 conn
->bn
, msg
->str
, &tmp
, &tmplen
, &charset
, &charsubset
);
4482 g_string_free(msg
, TRUE
);
4483 msg
= g_string_new_len(tmp
, tmplen
);
4486 /* Append any binary data that we may have */
4488 msg
= g_string_append_len(msg
, data
->str
, data
->len
);
4489 msg
= g_string_append(msg
, "</BINARY>");
4491 g_string_free(data
, TRUE
);
4493 peer_odc_send_im(conn
, msg
->str
, msg
->len
, charset
,
4494 imflags
& PURPLE_MESSAGE_AUTO_RESP
);
4495 g_string_free(msg
, TRUE
);
4499 oscar_send_im(PurpleConnection
*gc
, const char *name
, const char *message
, PurpleMessageFlags imflags
)
4502 PurpleAccount
*account
;
4503 PeerConnection
*conn
;
4506 gboolean is_sms
, is_html
;
4508 od
= purple_connection_get_protocol_data(gc
);
4509 account
= purple_connection_get_account(gc
);
4512 is_sms
= oscar_util_valid_name_sms(name
);
4514 if (od
->icq
&& is_sms
) {
4516 * We're sending to a phone number and this is ICQ,
4517 * so send the message as an SMS using aim_icq_sendsms()
4520 purple_debug_info("oscar", "Sending SMS to %s.\n", name
);
4521 ret
= aim_icq_sendsms(od
, name
, message
, purple_account_get_username(account
));
4522 return (ret
>= 0 ? 1 : ret
);
4525 if (imflags
& PURPLE_MESSAGE_AUTO_RESP
)
4526 tmp1
= purple_str_sub_away_formatters(message
, name
);
4528 tmp1
= g_strdup(message
);
4530 conn
= peer_connection_find_by_type(od
, name
, OSCAR_CAPABILITY_DIRECTIM
);
4531 if ((conn
!= NULL
) && (conn
->ready
))
4533 /* If we're directly connected, send a direct IM */
4534 purple_debug_info("oscar", "Sending direct IM with flags %i", imflags
);
4535 purple_odc_send_im(conn
, tmp1
, imflags
);
4537 struct buddyinfo
*bi
;
4538 struct aim_sendimext_args args
;
4539 PurpleConversation
*conv
;
4540 PurpleStoredImage
*img
;
4543 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, name
, account
);
4545 if (strstr(tmp1
, "<IMG "))
4546 purple_conversation_write(conv
, "",
4547 _("Your IM Image was not sent. "
4548 "You must be Direct Connected to send IM Images."),
4549 PURPLE_MESSAGE_ERROR
, time(NULL
));
4551 buddy
= purple_find_buddy(account
, name
);
4553 bi
= g_hash_table_lookup(od
->buddyinfo
, purple_normalize(account
, name
));
4555 bi
= g_new0(struct buddyinfo
, 1);
4556 g_hash_table_insert(od
->buddyinfo
, g_strdup(purple_normalize(account
, name
)), bi
);
4559 args
.flags
= AIM_IMFLAGS_ACK
| AIM_IMFLAGS_CUSTOMFEATURES
;
4561 if (!is_sms
&& (!buddy
|| !PURPLE_BUDDY_IS_ONLINE(buddy
)))
4562 args
.flags
|= AIM_IMFLAGS_OFFLINE
;
4565 /* We have to present different "features" (whose meaning
4566 is unclear and are merely a result of protocol inspection)
4567 to offline ICQ buddies. Otherwise, the official
4568 ICQ client doesn't treat those messages as being "ANSI-
4569 encoded" (and instead, assumes them to be UTF-8).
4570 For more details, see SF issue 1179452.
4572 if (buddy
&& PURPLE_BUDDY_IS_ONLINE(buddy
)) {
4573 args
.features
= features_icq
;
4574 args
.featureslen
= sizeof(features_icq
);
4576 args
.features
= features_icq_offline
;
4577 args
.featureslen
= sizeof(features_icq_offline
);
4580 args
.features
= features_aim
;
4581 args
.featureslen
= sizeof(features_aim
);
4583 if (imflags
& PURPLE_MESSAGE_AUTO_RESP
)
4584 args
.flags
|= AIM_IMFLAGS_AWAY
;
4588 purple_debug_info("oscar",
4589 "Sending buddy icon request with message\n");
4590 args
.flags
|= AIM_IMFLAGS_BUDDYREQ
;
4591 bi
->ico_need
= FALSE
;
4594 img
= purple_buddy_icons_find_account_icon(account
);
4596 gconstpointer data
= purple_imgstore_get_data(img
);
4597 args
.iconlen
= purple_imgstore_get_size(img
);
4598 args
.iconsum
= aimutil_iconsum(data
, args
.iconlen
);
4599 args
.iconstamp
= purple_buddy_icons_get_account_icon_timestamp(account
);
4601 if ((args
.iconlen
!= bi
->ico_me_len
) || (args
.iconsum
!= bi
->ico_me_csum
) || (args
.iconstamp
!= bi
->ico_me_time
)) {
4602 bi
->ico_informed
= FALSE
;
4603 bi
->ico_sent
= FALSE
;
4608 * For some reason sending our icon to people only works
4609 * when we're the ones who initiated the conversation. If
4610 * the other person sends the first IM then they never get
4611 * the icon. We should fix that.
4613 if (!bi
->ico_informed
) {
4614 purple_debug_info("oscar",
4615 "Claiming to have a buddy icon\n");
4616 args
.flags
|= AIM_IMFLAGS_HASICON
;
4617 bi
->ico_me_len
= args
.iconlen
;
4618 bi
->ico_me_csum
= args
.iconsum
;
4619 bi
->ico_me_time
= args
.iconstamp
;
4620 bi
->ico_informed
= TRUE
;
4623 purple_imgstore_unref(img
);
4629 * If we're IMing an SMS user or an ICQ user from an ICQ account, then strip HTML.
4631 if (oscar_util_valid_name_sms(name
)) {
4632 /* Messaging an SMS (mobile) user */
4633 tmp2
= purple_markup_strip_html(tmp1
);
4635 } else if (oscar_util_valid_name_icq(purple_account_get_username(account
))) {
4636 if (oscar_util_valid_name_icq(name
)) {
4637 /* From ICQ to ICQ */
4638 tmp2
= purple_markup_strip_html(tmp1
);
4641 /* From ICQ to AIM */
4642 tmp2
= g_strdup(tmp1
);
4646 /* From AIM to AIM and AIM to ICQ */
4647 tmp2
= g_strdup(tmp1
);
4653 purple_plugin_oscar_convert_to_best_encoding(gc
, name
, tmp1
, (char **)&args
.msg
, &args
.msglen
, &args
.charset
, &args
.charsubset
);
4654 if (is_html
&& (args
.msglen
> MAXMSGLEN
)) {
4655 /* If the length was too long, try stripping the HTML and then running it back through
4656 * purple_strdup_withhtml() and the encoding process. The result may be shorter. */
4657 g_free((char *)args
.msg
);
4659 tmp2
= purple_markup_strip_html(tmp1
);
4662 /* re-escape the entities */
4663 tmp1
= g_markup_escape_text(tmp2
, -1);
4666 tmp2
= purple_strdup_withhtml(tmp1
);
4670 purple_plugin_oscar_convert_to_best_encoding(gc
, name
, tmp1
, (char **)&args
.msg
, &args
.msglen
, &args
.charset
, &args
.charsubset
);
4672 purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
4673 message
, (char *)args
.msg
);
4676 purple_debug_info("oscar", "Sending IM, charset=0x%04hx, charsubset=0x%04hx, length=%d\n",
4677 args
.charset
, args
.charsubset
, args
.msglen
);
4678 ret
= aim_im_sendch1_ext(od
, &args
);
4679 g_free((char *)args
.msg
);
4691 * As of 26 June 2006, ICQ users can request AIM info from
4692 * everyone, and can request ICQ info from ICQ users, and
4693 * AIM users can only request AIM info.
4695 void oscar_get_info(PurpleConnection
*gc
, const char *name
) {
4696 OscarData
*od
= purple_connection_get_protocol_data(gc
);
4698 if (od
->icq
&& oscar_util_valid_name_icq(name
))
4699 aim_icq_getallinfo(od
, name
);
4701 aim_locate_getinfoshort(od
, name
, 0x00000003);
4705 static void oscar_set_dir(PurpleConnection
*gc
, const char *first
, const char *middle
, const char *last
,
4706 const char *maiden
, const char *city
, const char *state
, const char *country
, int web
) {
4707 /* XXX - some of these things are wrong, but i'm lazy */
4708 OscarData
*od
= purple_connection_get_protocol_data(gc
);
4709 aim_locate_setdirinfo(od
, first
, middle
, last
,
4710 maiden
, NULL
, NULL
, city
, state
, NULL
, 0, web
);
4714 void oscar_set_idle(PurpleConnection
*gc
, int time
) {
4715 OscarData
*od
= purple_connection_get_protocol_data(gc
);
4716 aim_srv_setidle(od
, time
);
4720 gchar
*purple_prpl_oscar_convert_to_infotext(const gchar
*str
, gsize
*ret_len
, char **encoding
)
4723 char *encoded
= NULL
;
4725 charset
= oscar_charset_check(str
);
4726 if (charset
== AIM_CHARSET_UNICODE
) {
4727 encoded
= g_convert(str
, -1, "UTF-16BE", "UTF-8", NULL
, ret_len
, NULL
);
4728 *encoding
= "unicode-2-0";
4729 } else if (charset
== AIM_CHARSET_CUSTOM
) {
4730 encoded
= g_convert(str
, -1, "ISO-8859-1", "UTF-8", NULL
, ret_len
, NULL
);
4731 *encoding
= "iso-8859-1";
4733 encoded
= g_strdup(str
);
4734 *ret_len
= strlen(str
);
4735 *encoding
= "us-ascii";
4742 oscar_set_info(PurpleConnection
*gc
, const char *rawinfo
)
4744 PurpleAccount
*account
;
4745 PurpleStatus
*status
;
4747 account
= purple_connection_get_account(gc
);
4748 status
= purple_account_get_active_status(account
);
4749 oscar_set_info_and_status(account
, TRUE
, rawinfo
, FALSE
, status
);
4753 oscar_set_extendedstatus(PurpleConnection
*gc
)
4756 PurpleAccount
*account
;
4757 PurpleStatus
*status
;
4758 const gchar
*status_id
;
4759 guint32 data
= 0x00000000;
4761 od
= purple_connection_get_protocol_data(gc
);
4762 account
= purple_connection_get_account(gc
);
4763 status
= purple_account_get_active_status(account
);
4764 status_id
= purple_status_get_id(status
);
4766 data
|= AIM_ICQ_STATE_HIDEIP
;
4767 if (purple_account_get_bool(account
, "web_aware", OSCAR_DEFAULT_WEB_AWARE
))
4768 data
|= AIM_ICQ_STATE_WEBAWARE
;
4770 if (!strcmp(status_id
, OSCAR_STATUS_ID_AVAILABLE
))
4771 data
|= AIM_ICQ_STATE_NORMAL
;
4772 else if (!strcmp(status_id
, OSCAR_STATUS_ID_AWAY
))
4773 data
|= AIM_ICQ_STATE_AWAY
;
4774 else if (!strcmp(status_id
, OSCAR_STATUS_ID_DND
))
4775 data
|= AIM_ICQ_STATE_AWAY
| AIM_ICQ_STATE_DND
| AIM_ICQ_STATE_BUSY
;
4776 else if (!strcmp(status_id
, OSCAR_STATUS_ID_NA
))
4777 data
|= AIM_ICQ_STATE_OUT
| AIM_ICQ_STATE_AWAY
;
4778 else if (!strcmp(status_id
, OSCAR_STATUS_ID_OCCUPIED
))
4779 data
|= AIM_ICQ_STATE_AWAY
| AIM_ICQ_STATE_BUSY
;
4780 else if (!strcmp(status_id
, OSCAR_STATUS_ID_FREE4CHAT
))
4781 data
|= AIM_ICQ_STATE_CHAT
;
4782 else if (!strcmp(status_id
, OSCAR_STATUS_ID_INVISIBLE
))
4783 data
|= AIM_ICQ_STATE_INVISIBLE
;
4784 else if (!strcmp(status_id
, OSCAR_STATUS_ID_CUSTOM
))
4785 data
|= AIM_ICQ_STATE_OUT
| AIM_ICQ_STATE_AWAY
;
4787 aim_srv_setextrainfo(od
, TRUE
, data
, FALSE
, NULL
, NULL
);
4791 oscar_set_info_and_status(PurpleAccount
*account
, gboolean setinfo
, const char *rawinfo
,
4792 gboolean setstatus
, PurpleStatus
*status
)
4794 PurpleConnection
*gc
= purple_account_get_connection(account
);
4795 OscarData
*od
= purple_connection_get_protocol_data(gc
);
4796 PurpleStatusType
*status_type
;
4797 PurpleStatusPrimitive primitive
;
4799 char *info_encoding
= NULL
;
4803 char *away_encoding
= NULL
;
4807 char *status_text
= NULL
;
4808 const char *itmsurl
= NULL
;
4810 status_type
= purple_status_get_type(status
);
4811 primitive
= purple_status_type_get_primitive(status_type
);
4817 else if (od
->rights
.maxsiglen
== 0)
4819 purple_notify_warning(gc
, NULL
, _("Unable to set AIM profile."),
4820 _("You have probably requested to set your "
4821 "profile before the login procedure completed. "
4822 "Your profile remains unset; try setting it "
4823 "again when you are fully connected."));
4825 else if (rawinfo
!= NULL
)
4827 char *htmlinfo
= purple_strdup_withhtml(rawinfo
);
4828 info
= purple_prpl_oscar_convert_to_infotext(htmlinfo
, &infolen
, &info_encoding
);
4831 if (infolen
> od
->rights
.maxsiglen
)
4834 errstr
= g_strdup_printf(dngettext(PACKAGE
, "The maximum profile length of %d byte "
4835 "has been exceeded. It has been truncated for you.",
4836 "The maximum profile length of %d bytes "
4837 "has been exceeded. It has been truncated for you.",
4838 od
->rights
.maxsiglen
), od
->rights
.maxsiglen
);
4839 purple_notify_warning(gc
, NULL
, _("Profile too long."), errstr
);
4846 const char *status_html
;
4848 status_html
= purple_status_get_attr_string(status
, "message");
4850 if (status_html
== NULL
|| primitive
== PURPLE_STATUS_AVAILABLE
|| primitive
== PURPLE_STATUS_INVISIBLE
)
4852 /* This is needed for us to un-set any previous away message. */
4853 away
= g_strdup("");
4859 /* We do this for icq too so that they work for old third party clients */
4860 linkified
= purple_markup_linkify(status_html
);
4861 away
= purple_prpl_oscar_convert_to_infotext(linkified
, &awaylen
, &away_encoding
);
4864 if (awaylen
> od
->rights
.maxawaymsglen
)
4868 errstr
= g_strdup_printf(dngettext(PACKAGE
, "The maximum away message length of %d byte "
4869 "has been exceeded. It has been truncated for you.",
4870 "The maximum away message length of %d bytes "
4871 "has been exceeded. It has been truncated for you.",
4872 od
->rights
.maxawaymsglen
), od
->rights
.maxawaymsglen
);
4873 purple_notify_warning(gc
, NULL
, _("Away message too long."), errstr
);
4879 aim_locate_setprofile(od
,
4880 info_encoding
, info
, MIN(infolen
, od
->rights
.maxsiglen
),
4881 away_encoding
, away
, MIN(awaylen
, od
->rights
.maxawaymsglen
));
4887 const char *status_html
;
4889 status_html
= purple_status_get_attr_string(status
, "message");
4890 if (od
->icq
&& (status_html
== NULL
|| status_html
[0] == '\0'))
4891 status_html
= purple_status_type_get_name(status_type
);
4892 if (status_html
!= NULL
)
4894 status_text
= purple_markup_strip_html(status_html
);
4895 /* If the status_text is longer than 251 characters then truncate it */
4896 if (strlen(status_text
) > MAXAVAILMSGLEN
)
4898 char *tmp
= g_utf8_find_prev_char(status_text
, &status_text
[MAXAVAILMSGLEN
- 2]);
4903 itmsurl
= purple_status_get_attr_string(status
, "itmsurl");
4905 /* TODO: Combine these two calls! */
4906 aim_srv_setextrainfo(od
, FALSE
, 0, TRUE
, status_text
, itmsurl
);
4907 oscar_set_extendedstatus(gc
);
4908 g_free(status_text
);
4913 oscar_set_status_icq(PurpleAccount
*account
)
4915 PurpleConnection
*gc
= purple_account_get_connection(account
);
4917 /* Our permit/deny setting affects our invisibility */
4918 oscar_set_permit_deny(gc
);
4921 * TODO: I guess we should probably wait and do this after we get
4922 * confirmation from the above SSI call? Right now other people
4923 * see our status blip to "invisible" before we appear offline.
4925 oscar_set_extendedstatus(gc
);
4929 oscar_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
4931 purple_debug_info("oscar", "Set status to %s\n", purple_status_get_name(status
));
4933 if (!purple_status_is_active(status
))
4936 if (!purple_account_is_connected(account
))
4939 /* Set the AIM-style away message for both AIM and ICQ accounts */
4940 oscar_set_info_and_status(account
, FALSE
, NULL
, TRUE
, status
);
4942 /* Set the ICQ status for ICQ accounts only */
4943 if (oscar_util_valid_name_icq(purple_account_get_username(account
)))
4944 oscar_set_status_icq(account
);
4949 oscar_warn(PurpleConnection
*gc
, const char *name
, gboolean anonymous
) {
4950 OscarData
*od
= purple_connection_get_protocol_data(gc
);
4951 aim_im_warn(od
, od
->conn
, name
, anonymous
? AIM_WARN_ANON
: 0);
4956 oscar_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
) {
4958 PurpleAccount
*account
;
4959 const char *bname
, *gname
;
4961 od
= purple_connection_get_protocol_data(gc
);
4962 account
= purple_connection_get_account(gc
);
4963 bname
= purple_buddy_get_name(buddy
);
4964 gname
= purple_group_get_name(group
);
4966 if (!oscar_util_valid_name(bname
)) {
4968 buf
= g_strdup_printf(_("Unable to add the buddy %s because the username is invalid. Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), bname
);
4969 if (!purple_conv_present_error(bname
, account
, buf
))
4970 purple_notify_error(gc
, NULL
, _("Unable to Add"), buf
);
4973 /* Remove from local list */
4974 purple_blist_remove_buddy(buddy
);
4979 if (od
->ssi
.received_data
) {
4980 if (!aim_ssi_itemlist_finditem(od
->ssi
.local
, gname
, bname
, AIM_SSI_TYPE_BUDDY
)) {
4981 purple_debug_info("oscar",
4982 "ssi: adding buddy %s to group %s\n", bname
, gname
);
4983 aim_ssi_addbuddy(od
, bname
, gname
, NULL
, purple_buddy_get_alias_only(buddy
), NULL
, NULL
, 0);
4985 /* Mobile users should always be online */
4986 if (bname
[0] == '+') {
4987 purple_prpl_got_user_status(account
, bname
,
4988 OSCAR_STATUS_ID_AVAILABLE
, NULL
);
4989 purple_prpl_got_user_status(account
, bname
,
4990 OSCAR_STATUS_ID_MOBILE
, NULL
);
4992 } else if (aim_ssi_waitingforauth(od
->ssi
.local
,
4993 aim_ssi_itemlist_findparentname(od
->ssi
.local
, bname
),
4995 /* Not authorized -- Re-request authorization */
4996 purple_auth_sendrequest(gc
, bname
);
5000 /* XXX - Should this be done from AIM accounts, as well? */
5002 aim_icq_getalias(od
, bname
);
5005 void oscar_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
) {
5006 OscarData
*od
= purple_connection_get_protocol_data(gc
);
5008 if (od
->ssi
.received_data
) {
5009 const char *gname
= purple_group_get_name(group
);
5010 const char *bname
= purple_buddy_get_name(buddy
);
5011 purple_debug_info("oscar",
5012 "ssi: deleting buddy %s from group %s\n", bname
, gname
);
5013 aim_ssi_delbuddy(od
, bname
, gname
);
5017 void oscar_move_buddy(PurpleConnection
*gc
, const char *name
, const char *old_group
, const char *new_group
) {
5018 OscarData
*od
= purple_connection_get_protocol_data(gc
);
5020 if (od
->ssi
.received_data
&& strcmp(old_group
, new_group
)) {
5021 purple_debug_info("oscar",
5022 "ssi: moving buddy %s from group %s to group %s\n", name
, old_group
, new_group
);
5023 aim_ssi_movebuddy(od
, old_group
, new_group
, name
);
5027 void oscar_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
) {
5028 OscarData
*od
= purple_connection_get_protocol_data(gc
);
5030 if (od
->ssi
.received_data
) {
5031 char *gname
= aim_ssi_itemlist_findparentname(od
->ssi
.local
, name
);
5033 purple_debug_info("oscar",
5034 "ssi: changing the alias for buddy %s to %s\n", name
, alias
? alias
: "(none)");
5035 aim_ssi_aliasbuddy(od
, gname
, name
, alias
);
5041 * FYI, the OSCAR SSI code removes empty groups automatically.
5043 void oscar_rename_group(PurpleConnection
*gc
, const char *old_name
, PurpleGroup
*group
, GList
*moved_buddies
) {
5044 OscarData
*od
= purple_connection_get_protocol_data(gc
);
5046 if (od
->ssi
.received_data
) {
5047 const char *gname
= purple_group_get_name(group
);
5048 if (aim_ssi_itemlist_finditem(od
->ssi
.local
, gname
, NULL
, AIM_SSI_TYPE_GROUP
)) {
5049 GList
*cur
, *groups
= NULL
;
5050 PurpleAccount
*account
= purple_connection_get_account(gc
);
5052 /* Make a list of what the groups each buddy is in */
5053 for (cur
= moved_buddies
; cur
!= NULL
; cur
= cur
->next
) {
5054 PurpleBlistNode
*node
= cur
->data
;
5055 /* node is PurpleBuddy, parent is a PurpleContact.
5056 * We must go two levels up to get the Group */
5057 groups
= g_list_append(groups
,
5058 purple_buddy_get_group((PurpleBuddy
*)node
));
5061 purple_account_remove_buddies(account
, moved_buddies
, groups
);
5062 purple_account_add_buddies(account
, moved_buddies
);
5063 g_list_free(groups
);
5064 purple_debug_info("oscar",
5065 "ssi: moved all buddies from group %s to %s\n", old_name
, gname
);
5067 aim_ssi_rename_group(od
, old_name
, gname
);
5068 purple_debug_info("oscar",
5069 "ssi: renamed group %s to %s\n", old_name
, gname
);
5074 void oscar_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
5076 aim_ssi_delgroup(purple_connection_get_protocol_data(gc
), purple_group_get_name(group
));
5079 static gboolean
purple_ssi_rerequestdata(gpointer data
) {
5080 OscarData
*od
= data
;
5082 aim_ssi_reqdata(od
);
5087 static int purple_ssi_parseerr(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
5088 PurpleConnection
*gc
= od
->gc
;
5093 reason
= (guint16
)va_arg(ap
, unsigned int);
5096 purple_debug_error("oscar", "ssi: SNAC error %hu\n", reason
);
5098 if (reason
== 0x0005) {
5099 if (od
->getblisttimer
> 0)
5100 purple_timeout_remove(od
->getblisttimer
);
5102 /* We only show this error the first time it happens */
5103 purple_notify_error(gc
, NULL
,
5104 _("Unable to Retrieve Buddy List"),
5105 _("The AIM servers were temporarily unable to send "
5106 "your buddy list. Your buddy list is not lost, and "
5107 "will probably become available in a few minutes."));
5108 od
->getblisttimer
= purple_timeout_add_seconds(30, purple_ssi_rerequestdata
, od
);
5112 oscar_set_status_icq(purple_connection_get_account(gc
));
5117 static int purple_ssi_parserights(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
5125 numtypes
= va_arg(ap
, int);
5126 maxitems
= va_arg(ap
, guint16
*);
5129 msg
= g_string_new("ssi rights:");
5130 for (i
=0; i
<numtypes
; i
++)
5131 g_string_append_printf(msg
, " max type 0x%04x=%hd,", i
, maxitems
[i
]);
5132 g_string_append(msg
, "\n");
5133 purple_debug_misc("oscar", "%s", msg
->str
);
5134 g_string_free(msg
, TRUE
);
5137 od
->rights
.maxbuddies
= maxitems
[0];
5139 od
->rights
.maxgroups
= maxitems
[1];
5141 od
->rights
.maxpermits
= maxitems
[2];
5143 od
->rights
.maxdenies
= maxitems
[3];
5148 static int purple_ssi_parselist(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
5150 PurpleConnection
*gc
;
5151 PurpleAccount
*account
;
5154 struct aim_ssi_item
*curitem
;
5156 PurpleStoredImage
*img
;
5158 guint16 fmtver
, numitems
;
5162 od
= purple_connection_get_protocol_data(gc
);
5163 account
= purple_connection_get_account(gc
);
5166 fmtver
= (guint16
)va_arg(ap
, int);
5167 numitems
= (guint16
)va_arg(ap
, int);
5168 timestamp
= va_arg(ap
, guint32
);
5171 /* Don't attempt to re-request our buddy list later */
5172 if (od
->getblisttimer
!= 0)
5173 purple_timeout_remove(od
->getblisttimer
);
5174 od
->getblisttimer
= 0;
5176 purple_debug_info("oscar",
5177 "ssi: syncing local list and server list\n");
5179 /* Clean the buddy list */
5180 aim_ssi_cleanlist(od
);
5182 { /* If not in server list then prune from local list */
5184 GSList
*buddies
= purple_find_buddies(account
, NULL
);
5195 g
= purple_buddy_get_group(b
);
5196 gname
= purple_group_get_name(g
);
5197 bname
= purple_buddy_get_name(b
);
5199 if (aim_ssi_itemlist_exists(od
->ssi
.local
, bname
)) {
5200 /* If the buddy is an ICQ user then load his nickname */
5201 const char *servernick
= purple_blist_node_get_string((PurpleBlistNode
*)b
, "servernick");
5205 serv_got_alias(gc
, bname
, servernick
);
5207 /* Store local alias on server */
5208 alias
= aim_ssi_getalias(od
->ssi
.local
, gname
, bname
);
5209 balias
= purple_buddy_get_local_buddy_alias(b
);
5210 if (!alias
&& balias
&& *balias
)
5211 aim_ssi_aliasbuddy(od
, gname
, bname
, balias
);
5214 purple_debug_info("oscar",
5215 "ssi: removing buddy %s from local list\n", bname
);
5216 /* We can't actually remove now because it will screw up our looping */
5217 cur
= g_slist_prepend(cur
, b
);
5219 buddies
= g_slist_delete_link(buddies
, buddies
);
5222 while (cur
!= NULL
) {
5224 cur
= g_slist_remove(cur
, b
);
5225 purple_blist_remove_buddy(b
);
5229 if (account
->permit
) {
5230 next
= account
->permit
;
5231 while (next
!= NULL
) {
5234 if (!aim_ssi_itemlist_finditem(od
->ssi
.local
, NULL
, cur
->data
, AIM_SSI_TYPE_PERMIT
)) {
5235 purple_debug_info("oscar",
5236 "ssi: removing permit %s from local list\n", (const char *)cur
->data
);
5237 purple_privacy_permit_remove(account
, cur
->data
, TRUE
);
5243 if (account
->deny
) {
5244 next
= account
->deny
;
5245 while (next
!= NULL
) {
5248 if (!aim_ssi_itemlist_finditem(od
->ssi
.local
, NULL
, cur
->data
, AIM_SSI_TYPE_DENY
)) {
5249 purple_debug_info("oscar",
5250 "ssi: removing deny %s from local list\n", (const char *)cur
->data
);
5251 purple_privacy_deny_remove(account
, cur
->data
, TRUE
);
5255 /* Presence settings (idle time visibility) */
5256 tmp
= aim_ssi_getpresence(od
->ssi
.local
);
5257 if (tmp
!= 0xFFFFFFFF) {
5258 const char *idle_reporting_pref
;
5259 gboolean report_idle
;
5261 idle_reporting_pref
= purple_prefs_get_string("/purple/away/idle_reporting");
5262 report_idle
= strcmp(idle_reporting_pref
, "none") != 0;
5265 aim_ssi_setpresence(od
, tmp
| 0x400);
5267 aim_ssi_setpresence(od
, tmp
& ~0x400);
5271 } /* end pruning buddies from local list */
5273 /* Add from server list to local list */
5274 for (curitem
=od
->ssi
.local
; curitem
; curitem
=curitem
->next
) {
5275 if ((curitem
->name
== NULL
) || (g_utf8_validate(curitem
->name
, -1, NULL
)))
5276 switch (curitem
->type
) {
5277 case 0x0000: { /* Buddy */
5278 if (curitem
->name
) {
5279 struct aim_ssi_item
*groupitem
;
5280 char *gname
, *gname_utf8
, *alias
, *alias_utf8
;
5282 groupitem
= aim_ssi_itemlist_find(od
->ssi
.local
, curitem
->gid
, 0x0000);
5283 gname
= groupitem
? groupitem
->name
: NULL
;
5284 if (gname
!= NULL
) {
5285 if (g_utf8_validate(gname
, -1, NULL
))
5286 gname_utf8
= g_strdup(gname
);
5288 gname_utf8
= oscar_utf8_try_convert(account
, gname
);
5292 g
= purple_find_group(gname_utf8
? gname_utf8
: _("Orphans"));
5294 g
= purple_group_new(gname_utf8
? gname_utf8
: _("Orphans"));
5295 purple_blist_add_group(g
, NULL
);
5298 alias
= aim_ssi_getalias(od
->ssi
.local
, gname
, curitem
->name
);
5299 if (alias
!= NULL
) {
5300 if (g_utf8_validate(alias
, -1, NULL
))
5301 alias_utf8
= g_strdup(alias
);
5303 alias_utf8
= oscar_utf8_try_convert(account
, alias
);
5308 b
= purple_find_buddy_in_group(account
, curitem
->name
, g
);
5310 /* Get server stored alias */
5311 purple_blist_alias_buddy(b
, alias_utf8
);
5313 b
= purple_buddy_new(account
, curitem
->name
, alias_utf8
);
5315 purple_debug_info("oscar",
5316 "ssi: adding buddy %s to group %s to local list\n", curitem
->name
, gname
);
5317 purple_blist_add_buddy(b
, NULL
, g
, NULL
);
5319 if (!oscar_util_name_compare(curitem
->name
, purple_account_get_username(account
))) {
5320 char *comment
= aim_ssi_getcomment(od
->ssi
.local
, gname
, curitem
->name
);
5321 if (comment
!= NULL
)
5323 purple_check_comment(od
, comment
);
5328 /* Mobile users should always be online */
5329 if (curitem
->name
[0] == '+') {
5330 purple_prpl_got_user_status(account
,
5331 purple_buddy_get_name(b
),
5332 OSCAR_STATUS_ID_AVAILABLE
, NULL
);
5333 purple_prpl_got_user_status(account
,
5334 purple_buddy_get_name(b
),
5335 OSCAR_STATUS_ID_MOBILE
, NULL
);
5343 case 0x0001: { /* Group */
5347 gname
= curitem
->name
;
5348 if (gname
!= NULL
) {
5349 if (g_utf8_validate(gname
, -1, NULL
))
5350 gname_utf8
= g_strdup(gname
);
5352 gname_utf8
= oscar_utf8_try_convert(account
, gname
);
5356 if (gname_utf8
!= NULL
&& purple_find_group(gname_utf8
) == NULL
) {
5357 g
= purple_group_new(gname_utf8
);
5358 purple_blist_add_group(g
, NULL
);
5363 case 0x0002: { /* Permit buddy */
5364 if (curitem
->name
) {
5365 /* if (!find_permdeny_by_name(gc->permit, curitem->name)) { AAA */
5367 for (list
=account
->permit
; (list
&& oscar_util_name_compare(curitem
->name
, list
->data
)); list
=list
->next
);
5369 purple_debug_info("oscar",
5370 "ssi: adding permit buddy %s to local list\n", curitem
->name
);
5371 purple_privacy_permit_add(account
, curitem
->name
, TRUE
);
5376 case 0x0003: { /* Deny buddy */
5377 if (curitem
->name
) {
5379 for (list
=account
->deny
; (list
&& oscar_util_name_compare(curitem
->name
, list
->data
)); list
=list
->next
);
5381 purple_debug_info("oscar",
5382 "ssi: adding deny buddy %s to local list\n", curitem
->name
);
5383 purple_privacy_deny_add(account
, curitem
->name
, TRUE
);
5388 case 0x0004: { /* Permit/deny setting */
5390 * We don't inherit the permit/deny setting from the server
5391 * for ICQ because, for ICQ, this setting controls who can
5392 * see your online status when you are invisible. Thus it is
5393 * a part of your status and not really related to blocking.
5395 if (!od
->icq
&& curitem
->data
) {
5396 guint8 perm_deny
= aim_ssi_getpermdeny(od
->ssi
.local
);
5397 if (perm_deny
!= 0 && perm_deny
!= account
->perm_deny
)
5399 purple_debug_info("oscar",
5400 "ssi: changing permdeny from %d to %hhu\n", account
->perm_deny
, perm_deny
);
5401 account
->perm_deny
= perm_deny
;
5406 case 0x0005: { /* Presence setting */
5407 /* We don't want to change Purple's setting because it applies to all accounts */
5409 } /* End of switch on curitem->type */
5410 } /* End of for loop */
5412 oscar_set_status_icq(account
);
5415 /* Sending the enable causes other people to be able to see you, and you to see them */
5416 /* Make sure your privacy setting/invisibility is set how you want it before this! */
5417 purple_debug_info("oscar",
5418 "ssi: activating server-stored buddy list\n");
5422 * Make sure our server-stored icon is updated correctly in
5423 * the event that the local user set a new icon while this
5424 * account was offline.
5426 img
= purple_buddy_icons_find_account_icon(account
);
5427 oscar_set_icon(gc
, img
);
5428 purple_imgstore_unref(img
);
5431 * If we've already received our bos rights then we're not waiting on
5432 * anything else, so send the server clientready.
5434 if (od
->bos
.have_rights
) {
5435 aim_srv_clientready(od
, conn
);
5436 purple_connection_set_state(gc
, PURPLE_CONNECTED
);
5442 static int purple_ssi_parseack(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
5443 PurpleConnection
*gc
= od
->gc
;
5445 struct aim_ssi_tmp
*retval
;
5448 retval
= va_arg(ap
, struct aim_ssi_tmp
*);
5452 purple_debug_misc("oscar",
5453 "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");
5455 if (retval
->ack
!= 0xffff)
5456 switch (retval
->ack
) {
5457 case 0x0000: { /* added successfully */
5460 case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */
5462 buf
= g_strdup_printf(_("Unable to 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)")));
5463 if ((retval
->name
!= NULL
) && !purple_conv_present_error(retval
->name
, purple_connection_get_account(gc
), buf
))
5464 purple_notify_error(gc
, NULL
, _("Unable to Add"), buf
);
5468 case 0x000e: { /* buddy requires authorization */
5469 if ((retval
->action
== SNAC_SUBTYPE_FEEDBAG_ADD
) && (retval
->name
))
5470 purple_auth_sendrequest(gc
, retval
->name
);
5473 default: { /* La la la */
5475 purple_debug_error("oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval
->action
, retval
->ack
);
5476 buf
= g_strdup_printf(_("Unable to add the buddy %s for an unknown reason."),
5477 (retval
->name
? retval
->name
: _("(no name)")));
5478 if ((retval
->name
!= NULL
) && !purple_conv_present_error(retval
->name
, purple_connection_get_account(gc
), buf
))
5479 purple_notify_error(gc
, NULL
, _("Unable to Add"), buf
);
5484 retval
= retval
->next
;
5491 purple_ssi_parseaddmod(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...)
5493 PurpleConnection
*gc
;
5494 PurpleAccount
*account
;
5495 char *gname
, *gname_utf8
, *alias
, *alias_utf8
;
5498 struct aim_ssi_item
*ssi_item
;
5500 guint16 snac_subtype
, type
;
5504 account
= purple_connection_get_account(gc
);
5507 snac_subtype
= (guint16
)va_arg(ap
, int);
5508 type
= (guint16
)va_arg(ap
, int);
5509 name
= va_arg(ap
, char *);
5512 if ((type
!= 0x0000) || (name
== NULL
))
5515 gname
= aim_ssi_itemlist_findparentname(od
->ssi
.local
, name
);
5516 gname_utf8
= gname
? oscar_utf8_try_convert(account
, gname
) : NULL
;
5518 alias
= aim_ssi_getalias(od
->ssi
.local
, gname
, name
);
5521 if (g_utf8_validate(alias
, -1, NULL
))
5522 alias_utf8
= g_strdup(alias
);
5524 alias_utf8
= oscar_utf8_try_convert(account
, alias
);
5530 b
= purple_find_buddy(account
, name
);
5533 * You're logged in somewhere else and you aliased one
5534 * of your buddies, so update our local buddy list with
5535 * the person's new alias.
5537 purple_blist_alias_buddy(b
, alias_utf8
);
5538 } else if (snac_subtype
== 0x0008) {
5540 * You're logged in somewhere else and you added a buddy to
5541 * your server list, so add them to your local buddy list.
5543 b
= purple_buddy_new(account
, name
, alias_utf8
);
5545 if (!(g
= purple_find_group(gname_utf8
? gname_utf8
: _("Orphans")))) {
5546 g
= purple_group_new(gname_utf8
? gname_utf8
: _("Orphans"));
5547 purple_blist_add_group(g
, NULL
);
5550 purple_debug_info("oscar",
5551 "ssi: adding buddy %s to group %s to local list\n", name
, gname_utf8
? gname_utf8
: _("Orphans"));
5552 purple_blist_add_buddy(b
, NULL
, g
, NULL
);
5554 /* Mobile users should always be online */
5555 if (name
[0] == '+') {
5556 purple_prpl_got_user_status(account
,
5557 name
, OSCAR_STATUS_ID_AVAILABLE
, NULL
);
5558 purple_prpl_got_user_status(account
,
5559 name
, OSCAR_STATUS_ID_MOBILE
, NULL
);
5564 ssi_item
= aim_ssi_itemlist_finditem(od
->ssi
.local
,
5565 gname
, name
, AIM_SSI_TYPE_BUDDY
);
5566 if (ssi_item
== NULL
)
5568 purple_debug_error("oscar", "purple_ssi_parseaddmod: "
5569 "Could not find ssi item for oncoming buddy %s, "
5570 "group %s\n", name
, gname
);
5579 static int purple_ssi_authgiven(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
5580 PurpleConnection
*gc
= od
->gc
;
5583 gchar
*dialog_msg
, *nombre
;
5584 struct name_data
*data
;
5588 bn
= va_arg(ap
, char *);
5589 msg
= va_arg(ap
, char *);
5592 purple_debug_info("oscar",
5593 "ssi: %s has given you permission to add him to your buddy list\n", bn
);
5595 buddy
= purple_find_buddy(purple_connection_get_account(gc
), bn
);
5596 if (buddy
&& (purple_buddy_get_alias_only(buddy
)))
5597 nombre
= g_strdup_printf("%s (%s)", bn
, purple_buddy_get_alias_only(buddy
));
5599 nombre
= g_strdup(bn
);
5601 dialog_msg
= g_strdup_printf(_("The user %s has given you permission to add him or her to your buddy list. Do you want to add this user?"), nombre
);
5604 data
= g_new(struct name_data
, 1);
5606 data
->name
= g_strdup(bn
);
5607 data
->nick
= (buddy
? g_strdup(purple_buddy_get_alias_only(buddy
)) : NULL
);
5609 purple_request_yes_no(gc
, NULL
, _("Authorization Given"), dialog_msg
,
5610 PURPLE_DEFAULT_ACTION_NONE
,
5611 purple_connection_get_account(gc
), bn
, NULL
,
5613 G_CALLBACK(purple_icq_buddyadd
),
5614 G_CALLBACK(oscar_free_name_data
));
5620 static int purple_ssi_authrequest(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
5621 PurpleConnection
*gc
= od
->gc
;
5625 PurpleAccount
*account
= purple_connection_get_account(gc
);
5626 gchar
*reason
= NULL
;
5627 struct name_data
*data
;
5631 bn
= va_arg(ap
, char *);
5632 msg
= va_arg(ap
, char *);
5635 purple_debug_info("oscar",
5636 "ssi: received authorization request from %s\n", bn
);
5638 buddy
= purple_find_buddy(account
, bn
);
5641 reason
= purple_plugin_oscar_decode_im_part(account
, bn
, AIM_CHARSET_CUSTOM
, 0x0000, msg
, strlen(msg
));
5643 data
= g_new(struct name_data
, 1);
5645 data
->name
= g_strdup(bn
);
5646 data
->nick
= (buddy
? g_strdup(purple_buddy_get_alias_only(buddy
)) : NULL
);
5648 purple_account_request_authorization(account
, bn
, NULL
,
5649 (buddy
? purple_buddy_get_alias_only(buddy
) : NULL
),
5650 reason
, buddy
!= NULL
, purple_auth_grant
,
5651 purple_auth_dontgrant_msgprompt
, data
);
5657 static int purple_ssi_authreply(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
5658 PurpleConnection
*gc
= od
->gc
;
5661 gchar
*dialog_msg
, *nombre
;
5666 bn
= va_arg(ap
, char *);
5667 reply
= (guint8
)va_arg(ap
, int);
5668 msg
= va_arg(ap
, char *);
5671 purple_debug_info("oscar",
5672 "ssi: received authorization reply from %s. Reply is 0x%04hhx\n", bn
, reply
);
5674 buddy
= purple_find_buddy(purple_connection_get_account(gc
), bn
);
5675 if (buddy
&& (purple_buddy_get_alias_only(buddy
)))
5676 nombre
= g_strdup_printf("%s (%s)", bn
, purple_buddy_get_alias_only(buddy
));
5678 nombre
= g_strdup(bn
);
5682 dialog_msg
= g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre
);
5683 purple_notify_info(gc
, NULL
, _("Authorization Granted"), dialog_msg
);
5686 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."));
5687 purple_notify_info(gc
, NULL
, _("Authorization Denied"), dialog_msg
);
5695 static int purple_ssi_gotadded(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
5696 PurpleConnection
*gc
= od
->gc
;
5697 PurpleAccount
*account
= purple_connection_get_account(gc
);
5703 bn
= va_arg(ap
, char *);
5706 buddy
= purple_find_buddy(account
, bn
);
5707 purple_debug_info("oscar", "ssi: %s added you to their buddy list\n", bn
);
5708 purple_account_notify_added(account
, bn
, NULL
,
5709 (buddy
? purple_buddy_get_alias_only(buddy
) : NULL
), NULL
);
5714 GList
*oscar_chat_info(PurpleConnection
*gc
) {
5716 struct proto_chat_entry
*pce
;
5718 pce
= g_new0(struct proto_chat_entry
, 1);
5719 pce
->label
= _("_Room:");
5720 pce
->identifier
= "room";
5721 pce
->required
= TRUE
;
5722 m
= g_list_append(m
, pce
);
5724 pce
= g_new0(struct proto_chat_entry
, 1);
5725 pce
->label
= _("_Exchange:");
5726 pce
->identifier
= "exchange";
5727 pce
->required
= TRUE
;
5731 m
= g_list_append(m
, pce
);
5736 GHashTable
*oscar_chat_info_defaults(PurpleConnection
*gc
, const char *chat_name
)
5738 GHashTable
*defaults
;
5740 defaults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, g_free
);
5742 if (chat_name
!= NULL
)
5743 g_hash_table_insert(defaults
, "room", g_strdup(chat_name
));
5744 g_hash_table_insert(defaults
, "exchange", g_strdup("4"));
5750 oscar_get_chat_name(GHashTable
*data
)
5752 return g_strdup(g_hash_table_lookup(data
, "room"));
5756 oscar_join_chat(PurpleConnection
*gc
, GHashTable
*data
)
5758 OscarData
*od
= purple_connection_get_protocol_data(gc
);
5759 FlapConnection
*conn
;
5760 char *name
, *exchange
;
5763 name
= g_hash_table_lookup(data
, "room");
5764 exchange
= g_hash_table_lookup(data
, "exchange");
5766 g_return_if_fail(name
!= NULL
&& *name
!= '\0');
5767 g_return_if_fail(exchange
!= NULL
);
5770 exchange_int
= strtol(exchange
, NULL
, 10);
5771 g_return_if_fail(errno
== 0);
5773 purple_debug_info("oscar", "Attempting to join chat room %s.\n", name
);
5775 if ((conn
= flap_connection_getbytype(od
, SNAC_FAMILY_CHATNAV
)))
5777 purple_debug_info("oscar", "chatnav exists, creating room\n");
5778 aim_chatnav_createroom(od
, conn
, name
, exchange_int
);
5780 /* this gets tricky */
5781 struct create_room
*cr
= g_new0(struct create_room
, 1);
5782 purple_debug_info("oscar", "chatnav does not exist, opening chatnav\n");
5783 cr
->exchange
= exchange_int
;
5784 cr
->name
= g_strdup(name
);
5785 od
->create_rooms
= g_slist_prepend(od
->create_rooms
, cr
);
5786 aim_srv_requestnew(od
, SNAC_FAMILY_CHATNAV
);
5791 oscar_chat_invite(PurpleConnection
*gc
, int id
, const char *message
, const char *name
)
5793 OscarData
*od
= purple_connection_get_protocol_data(gc
);
5794 struct chat_connection
*ccon
= find_oscar_chat(gc
, id
);
5799 aim_im_sendch2_chatinvite(od
, name
, message
? message
: "",
5800 ccon
->exchange
, ccon
->name
, 0x0);
5804 oscar_chat_leave(PurpleConnection
*gc
, int id
)
5806 PurpleConversation
*conv
;
5807 struct chat_connection
*cc
;
5809 conv
= purple_find_chat(gc
, id
);
5811 g_return_if_fail(conv
!= NULL
);
5813 purple_debug_info("oscar", "Leaving chat room %s\n",
5814 purple_conversation_get_name(conv
));
5816 cc
= find_oscar_chat(gc
, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv
)));
5817 oscar_chat_kill(gc
, cc
);
5820 int oscar_send_chat(PurpleConnection
*gc
, int id
, const char *message
, PurpleMessageFlags flags
)
5822 OscarData
*od
= purple_connection_get_protocol_data(gc
);
5823 PurpleConversation
*conv
= NULL
;
5824 struct chat_connection
*c
= NULL
;
5825 char *buf
, *buf2
, *buf3
;
5826 guint16 charset
, charsubset
;
5827 char *charsetstr
= NULL
;
5830 if (!(conv
= purple_find_chat(gc
, id
)))
5833 if (!(c
= find_oscar_chat_by_conv(gc
, conv
)))
5836 buf
= purple_strdup_withhtml(message
);
5838 if (strstr(buf
, "<IMG "))
5839 purple_conversation_write(conv
, "",
5840 _("Your IM Image was not sent. "
5841 "You cannot send IM Images in AIM chats."),
5842 PURPLE_MESSAGE_ERROR
, time(NULL
));
5844 purple_plugin_oscar_convert_to_best_encoding(gc
, NULL
, buf
, &buf2
, &len
, &charset
, &charsubset
);
5846 * Evan S. suggested that maxvis really does mean "number of
5847 * visible characters" and not "number of bytes"
5849 if ((len
> c
->maxlen
) || (len
> c
->maxvis
)) {
5850 /* If the length was too long, try stripping the HTML and then running it back through
5851 * purple_strdup_withhtml() and the encoding process. The result may be shorter. */
5854 buf3
= purple_markup_strip_html(buf
);
5857 buf
= purple_strdup_withhtml(buf3
);
5860 purple_plugin_oscar_convert_to_best_encoding(gc
, NULL
, buf
, &buf2
, &len
, &charset
, &charsubset
);
5862 if ((len
> c
->maxlen
) || (len
> c
->maxvis
)) {
5863 purple_debug_warning("oscar", "Could not send %s because (%i > maxlen %i) or (%i > maxvis %i)\n",
5864 buf2
, len
, c
->maxlen
, len
, c
->maxvis
);
5870 purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
5874 if (charset
== AIM_CHARSET_ASCII
)
5875 charsetstr
= "us-ascii";
5876 else if (charset
== AIM_CHARSET_UNICODE
)
5877 charsetstr
= "unicode-2-0";
5878 else if (charset
== AIM_CHARSET_CUSTOM
)
5879 charsetstr
= "iso-8859-1";
5880 aim_chat_send_im(od
, c
->conn
, 0, buf2
, len
, charsetstr
, "en");
5887 const char *oscar_list_icon_icq(PurpleAccount
*a
, PurpleBuddy
*b
)
5889 const char *name
= b
? purple_buddy_get_name(b
) : NULL
;
5890 if ((b
== NULL
) || (name
== NULL
) || oscar_util_valid_name_sms(name
))
5892 if (a
== NULL
|| oscar_util_valid_name_icq(purple_account_get_username(a
)))
5898 if (oscar_util_valid_name_icq(name
))
5903 const char *oscar_list_icon_aim(PurpleAccount
*a
, PurpleBuddy
*b
)
5905 const char *name
= b
? purple_buddy_get_name(b
) : NULL
;
5906 if ((b
== NULL
) || (name
== NULL
) || oscar_util_valid_name_sms(name
))
5908 if (a
!= NULL
&& oscar_util_valid_name_icq(purple_account_get_username(a
)))
5914 if (oscar_util_valid_name_icq(name
))
5919 const char *oscar_list_emblem(PurpleBuddy
*b
)
5921 PurpleConnection
*gc
= NULL
;
5922 OscarData
*od
= NULL
;
5923 PurpleAccount
*account
= NULL
;
5924 PurplePresence
*presence
;
5925 PurpleStatus
*status
;
5926 const char *status_id
;
5927 aim_userinfo_t
*userinfo
= NULL
;
5930 account
= purple_buddy_get_account(b
);
5931 name
= purple_buddy_get_name(b
);
5932 if (account
!= NULL
)
5933 gc
= purple_account_get_connection(account
);
5935 od
= purple_connection_get_protocol_data(gc
);
5937 userinfo
= aim_locate_finduserinfo(od
, name
);
5939 presence
= purple_buddy_get_presence(b
);
5940 status
= purple_presence_get_active_status(presence
);
5941 status_id
= purple_status_get_id(status
);
5943 if (purple_presence_is_online(presence
) == FALSE
) {
5945 if ((name
) && (od
) && (od
->ssi
.received_data
) &&
5946 (gname
= aim_ssi_itemlist_findparentname(od
->ssi
.local
, name
)) &&
5947 (aim_ssi_waitingforauth(od
->ssi
.local
, gname
, name
))) {
5948 return "not-authorized";
5952 if (userinfo
!= NULL
) {
5953 if (userinfo
->flags
& AIM_FLAG_ADMINISTRATOR
)
5955 if (userinfo
->flags
& AIM_FLAG_ACTIVEBUDDY
)
5957 if (userinfo
->capabilities
& OSCAR_CAPABILITY_HIPTOP
)
5959 if (userinfo
->capabilities
& OSCAR_CAPABILITY_SECUREIM
)
5961 if (userinfo
->icqinfo
.status
& AIM_ICQ_STATE_BIRTHDAY
)
5967 void oscar_tooltip_text(PurpleBuddy
*b
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
5969 PurpleConnection
*gc
;
5970 PurpleAccount
*account
;
5972 aim_userinfo_t
*userinfo
;
5974 if (!PURPLE_BUDDY_IS_ONLINE(b
))
5977 account
= purple_buddy_get_account(b
);
5978 gc
= purple_account_get_connection(account
);
5979 od
= purple_connection_get_protocol_data(gc
);
5980 userinfo
= aim_locate_finduserinfo(od
, purple_buddy_get_name(b
));
5982 oscar_user_info_append_status(gc
, user_info
, b
, userinfo
, /* strip_html_tags */ TRUE
);
5985 oscar_user_info_append_extra_info(gc
, user_info
, b
, userinfo
);
5988 char *oscar_status_text(PurpleBuddy
*b
)
5990 PurpleConnection
*gc
;
5991 PurpleAccount
*account
;
5993 const PurplePresence
*presence
;
5994 const PurpleStatus
*status
;
5996 const char *message
;
5999 gc
= purple_account_get_connection(purple_buddy_get_account(b
));
6000 account
= purple_connection_get_account(gc
);
6001 od
= purple_connection_get_protocol_data(gc
);
6002 presence
= purple_buddy_get_presence(b
);
6003 status
= purple_presence_get_active_status(presence
);
6004 id
= purple_status_get_id(status
);
6006 if ((od
!= NULL
) && !purple_presence_is_online(presence
))
6008 const char *name
= purple_buddy_get_name(b
);
6009 char *gname
= aim_ssi_itemlist_findparentname(od
->ssi
.local
, name
);
6010 if (aim_ssi_waitingforauth(od
->ssi
.local
, gname
, name
))
6011 ret
= g_strdup(_("Not Authorized"));
6013 ret
= g_strdup(_("Offline"));
6017 message
= purple_status_get_attr_string(status
, "message");
6018 if (message
!= NULL
)
6021 tmp1
= purple_markup_strip_html(message
);
6022 purple_util_chrreplace(tmp1
, '\n', ' ');
6023 tmp2
= g_markup_escape_text(tmp1
, -1);
6024 ret
= purple_str_sub_away_formatters(tmp2
, purple_account_get_username(account
));
6028 else if (purple_status_is_available(status
))
6030 /* Don't show "Available" as status message in case buddy doesn't have a status message */
6034 ret
= g_strdup(purple_status_get_name(status
));
6042 static int oscar_icon_req(OscarData
*od
, FlapConnection
*conn
, FlapFrame
*fr
, ...) {
6043 PurpleConnection
*gc
= od
->gc
;
6046 guint8 flags
= 0, length
= 0;
6050 type
= va_arg(ap
, int);
6055 flags
= va_arg(ap
, int);
6056 length
= va_arg(ap
, int);
6057 md5
= va_arg(ap
, guchar
*);
6059 if ((flags
== 0x00) || (flags
== 0x41)) {
6060 if (!flap_connection_getbytype(od
, SNAC_FAMILY_BART
) && !od
->iconconnecting
) {
6061 od
->iconconnecting
= TRUE
;
6062 od
->set_icon
= TRUE
;
6063 aim_srv_requestnew(od
, SNAC_FAMILY_BART
);
6065 PurpleAccount
*account
= purple_connection_get_account(gc
);
6066 PurpleStoredImage
*img
= purple_buddy_icons_find_account_icon(account
);
6068 aim_ssi_delicon(od
);
6071 purple_debug_info("oscar",
6072 "Uploading icon to icon server\n");
6073 aim_bart_upload(od
, purple_imgstore_get_data(img
),
6074 purple_imgstore_get_size(img
));
6075 purple_imgstore_unref(img
);
6078 } else if (flags
== 0x81) {
6079 PurpleAccount
*account
= purple_connection_get_account(gc
);
6080 PurpleStoredImage
*img
= purple_buddy_icons_find_account_icon(account
);
6082 aim_ssi_delicon(od
);
6084 aim_ssi_seticon(od
, md5
, length
);
6085 purple_imgstore_unref(img
);
6090 case 0x0002: { /* We just set an "available" message? */
6099 void oscar_set_permit_deny(PurpleConnection
*gc
) {
6100 PurpleAccount
*account
= purple_connection_get_account(gc
);
6101 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6102 PurplePrivacyType perm_deny
;
6105 * For ICQ the permit/deny setting controls who you can see you
6106 * online when you set your status to "invisible." If we're ICQ
6107 * and we're invisible then we need to use one of
6108 * PURPLE_PRIVACY_ALLOW_USERS or PURPLE_PRIVACY_ALLOW_BUDDYLIST or
6109 * PURPLE_PRIVACY_DENY_USERS if we actually want to be invisible
6112 * These three permit/deny settings correspond to:
6113 * 1. Invisible to everyone except the people on my "permit" list
6114 * 2. Invisible to everyone except the people on my buddy list
6115 * 3. Invisible only to the people on my "deny" list
6117 * It would be nice to allow cases 2 and 3, but our UI doesn't have
6118 * a nice way to do it. For now we just force case 1.
6120 if (od
->icq
&& purple_account_is_status_active(account
, OSCAR_STATUS_ID_INVISIBLE
))
6121 perm_deny
= PURPLE_PRIVACY_ALLOW_USERS
;
6123 perm_deny
= account
->perm_deny
;
6125 if (od
->ssi
.received_data
)
6127 * Conveniently there is a one-to-one mapping between the
6128 * values of libpurple's PurplePrivacyType and the values used
6129 * by the oscar protocol.
6131 aim_ssi_setpermdeny(od
, perm_deny
, 0xffffffff);
6134 void oscar_add_permit(PurpleConnection
*gc
, const char *who
) {
6135 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6136 purple_debug_info("oscar", "ssi: About to add a permit\n");
6137 if (od
->ssi
.received_data
)
6138 aim_ssi_addpermit(od
, who
);
6141 void oscar_add_deny(PurpleConnection
*gc
, const char *who
) {
6142 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6143 purple_debug_info("oscar", "ssi: About to add a deny\n");
6144 if (od
->ssi
.received_data
)
6145 aim_ssi_adddeny(od
, who
);
6148 void oscar_rem_permit(PurpleConnection
*gc
, const char *who
) {
6149 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6150 purple_debug_info("oscar", "ssi: About to delete a permit\n");
6151 if (od
->ssi
.received_data
)
6152 aim_ssi_delpermit(od
, who
);
6155 void oscar_rem_deny(PurpleConnection
*gc
, const char *who
) {
6156 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6157 purple_debug_info("oscar", "ssi: About to delete a deny\n");
6158 if (od
->ssi
.received_data
)
6159 aim_ssi_deldeny(od
, who
);
6163 oscar_status_types(PurpleAccount
*account
)
6166 GList
*status_types
= NULL
;
6167 PurpleStatusType
*type
;
6169 g_return_val_if_fail(account
!= NULL
, NULL
);
6171 /* Used to flag some statuses as "user settable" or not */
6172 is_icq
= oscar_util_valid_name_icq(purple_account_get_username(account
));
6174 /* Common status types */
6175 /* Really the available message should only be settable for AIM accounts */
6176 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE
,
6177 OSCAR_STATUS_ID_AVAILABLE
,
6178 NULL
, TRUE
, TRUE
, FALSE
,
6179 "message", _("Message"),
6180 purple_value_new(PURPLE_TYPE_STRING
),
6181 "itmsurl", _("iTunes Music Store Link"),
6182 purple_value_new(PURPLE_TYPE_STRING
), NULL
);
6183 status_types
= g_list_prepend(status_types
, type
);
6185 type
= purple_status_type_new_full(PURPLE_STATUS_AVAILABLE
,
6186 OSCAR_STATUS_ID_FREE4CHAT
,
6187 _("Free For Chat"), TRUE
, is_icq
, FALSE
);
6188 status_types
= g_list_prepend(status_types
, type
);
6190 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY
,
6191 OSCAR_STATUS_ID_AWAY
,
6192 NULL
, TRUE
, TRUE
, FALSE
,
6193 "message", _("Message"),
6194 purple_value_new(PURPLE_TYPE_STRING
), NULL
);
6195 status_types
= g_list_prepend(status_types
, type
);
6197 type
= purple_status_type_new_full(PURPLE_STATUS_INVISIBLE
,
6198 OSCAR_STATUS_ID_INVISIBLE
,
6199 NULL
, TRUE
, TRUE
, FALSE
);
6200 status_types
= g_list_prepend(status_types
, type
);
6202 type
= purple_status_type_new_full(PURPLE_STATUS_MOBILE
, OSCAR_STATUS_ID_MOBILE
, NULL
, FALSE
, FALSE
, TRUE
);
6203 status_types
= g_list_prepend(status_types
, type
);
6205 /* ICQ-specific status types */
6206 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE
,
6207 OSCAR_STATUS_ID_OCCUPIED
,
6208 _("Occupied"), TRUE
, is_icq
, FALSE
,
6209 "message", _("Message"),
6210 purple_value_new(PURPLE_TYPE_STRING
), NULL
);
6211 status_types
= g_list_prepend(status_types
, type
);
6213 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE
,
6214 OSCAR_STATUS_ID_DND
,
6215 _("Do Not Disturb"), TRUE
, is_icq
, FALSE
,
6216 "message", _("Message"),
6217 purple_value_new(PURPLE_TYPE_STRING
), NULL
);
6218 status_types
= g_list_prepend(status_types
, type
);
6220 type
= purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY
,
6222 _("Not Available"), TRUE
, is_icq
, FALSE
,
6223 "message", _("Message"),
6224 purple_value_new(PURPLE_TYPE_STRING
), NULL
);
6225 status_types
= g_list_prepend(status_types
, type
);
6227 type
= purple_status_type_new_full(PURPLE_STATUS_OFFLINE
,
6228 OSCAR_STATUS_ID_OFFLINE
,
6229 NULL
, TRUE
, TRUE
, FALSE
);
6230 status_types
= g_list_prepend(status_types
, type
);
6232 status_types
= g_list_reverse(status_types
);
6234 return status_types
;
6237 static void oscar_ssi_editcomment(struct name_data
*data
, const char *text
) {
6238 PurpleConnection
*gc
;
6239 PurpleAccount
*account
;
6243 const char *username
;
6246 od
= purple_connection_get_protocol_data(gc
);
6247 account
= purple_connection_get_account(gc
);
6249 b
= purple_find_buddy(account
, data
->name
);
6251 oscar_free_name_data(data
);
6255 g
= purple_buddy_get_group(b
);
6257 oscar_free_name_data(data
);
6261 aim_ssi_editcomment(od
, purple_group_get_name(g
), data
->name
, text
);
6263 username
= purple_account_get_username(account
);
6264 if (!oscar_util_name_compare(data
->name
, username
))
6265 purple_check_comment(od
, text
);
6267 oscar_free_name_data(data
);
6270 static void oscar_buddycb_edit_comment(PurpleBlistNode
*node
, gpointer ignore
) {
6273 PurpleConnection
*gc
;
6275 struct name_data
*data
;
6278 gchar
*comment_utf8
;
6280 PurpleAccount
*account
;
6283 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6285 buddy
= (PurpleBuddy
*) node
;
6286 name
= purple_buddy_get_name(buddy
);
6287 account
= purple_buddy_get_account(buddy
);
6288 gc
= purple_account_get_connection(account
);
6289 od
= purple_connection_get_protocol_data(gc
);
6291 if (!(g
= purple_buddy_get_group(buddy
)))
6294 data
= g_new(struct name_data
, 1);
6296 comment
= aim_ssi_getcomment(od
->ssi
.local
, purple_group_get_name(g
), name
);
6297 comment_utf8
= comment
? oscar_utf8_try_convert(account
, comment
) : NULL
;
6300 data
->name
= g_strdup(name
);
6301 data
->nick
= g_strdup(purple_buddy_get_alias_only(buddy
));
6303 title
= g_strdup_printf(_("Buddy Comment for %s"), data
->name
);
6304 purple_request_input(gc
, title
, _("Buddy Comment:"), NULL
,
6305 comment_utf8
, TRUE
, FALSE
, NULL
,
6306 _("_OK"), G_CALLBACK(oscar_ssi_editcomment
),
6307 _("_Cancel"), G_CALLBACK(oscar_free_name_data
),
6308 account
, data
->name
, NULL
,
6313 g_free(comment_utf8
);
6317 oscar_ask_directim_yes_cb(struct oscar_ask_directim_data
*data
)
6319 peer_connection_propose(data
->od
, OSCAR_CAPABILITY_DIRECTIM
, data
->who
);
6325 oscar_ask_directim_no_cb(struct oscar_ask_directim_data
*data
)
6331 /* This is called from right-click menu on a buddy node. */
6333 oscar_ask_directim(gpointer object
, gpointer ignored
)
6335 PurpleBlistNode
*node
;
6337 PurpleConnection
*gc
;
6339 struct oscar_ask_directim_data
*data
;
6340 PurpleAccount
*account
;
6344 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6346 buddy
= (PurpleBuddy
*)node
;
6347 account
= purple_buddy_get_account(buddy
);
6348 gc
= purple_account_get_connection(account
);
6350 data
= g_new0(struct oscar_ask_directim_data
, 1);
6351 data
->who
= g_strdup(purple_buddy_get_name(buddy
));
6352 data
->od
= purple_connection_get_protocol_data(gc
);
6353 buf
= g_strdup_printf(_("You have selected to open a Direct IM connection with %s."),
6356 purple_request_action(gc
, NULL
, buf
,
6357 _("Because this reveals your IP address, it "
6358 "may be considered a security risk. Do you "
6359 "wish to continue?"),
6360 0, /* Default action is "connect" */
6361 account
, data
->who
, NULL
,
6363 _("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb
),
6364 _("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb
));
6369 oscar_close_directim(gpointer object
, gpointer ignored
)
6371 PurpleBlistNode
*node
;
6373 PurpleAccount
*account
;
6374 PurpleConnection
*gc
;
6375 PurpleConversation
*conv
;
6377 PeerConnection
*conn
;
6382 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6384 buddy
= (PurpleBuddy
*)node
;
6385 name
= purple_buddy_get_name(buddy
);
6386 account
= purple_buddy_get_account(buddy
);
6387 gc
= purple_account_get_connection(account
);
6388 od
= gc
->proto_data
;
6389 conn
= peer_connection_find_by_type(od
, name
, OSCAR_CAPABILITY_DIRECTIM
);
6394 aim_im_sendch2_cancel(conn
);
6396 peer_connection_destroy(conn
, OSCAR_DISCONNECT_LOCAL_CLOSED
, NULL
);
6398 /* OSCAR_DISCONNECT_LOCAL_CLOSED doesn't write anything to the convo
6399 * window. Let the user know that we canceled the Direct IM. */
6400 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, account
, name
);
6401 purple_conversation_write(conv
, NULL
, _("You closed the connection."),
6402 PURPLE_MESSAGE_SYSTEM
, time(NULL
));
6407 oscar_get_aim_info_cb(PurpleBlistNode
*node
, gpointer ignore
)
6410 PurpleConnection
*gc
;
6412 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6414 buddy
= (PurpleBuddy
*)node
;
6415 gc
= purple_account_get_connection(purple_buddy_get_account(buddy
));
6417 aim_locate_getinfoshort(purple_connection_get_protocol_data(gc
),
6418 purple_buddy_get_name(buddy
), 0x00000003);
6422 oscar_buddy_menu(PurpleBuddy
*buddy
) {
6424 PurpleConnection
*gc
;
6427 PurpleMenuAction
*act
;
6428 aim_userinfo_t
*userinfo
;
6429 PurpleAccount
*account
;
6430 const char *bname
= purple_buddy_get_name(buddy
);
6432 account
= purple_buddy_get_account(buddy
);
6433 gc
= purple_account_get_connection(account
);
6434 od
= purple_connection_get_protocol_data(gc
);
6435 userinfo
= aim_locate_finduserinfo(od
, bname
);
6438 if (od
->icq
&& oscar_util_valid_name_icq(bname
))
6440 act
= purple_menu_action_new(_("Get AIM Info"),
6441 PURPLE_CALLBACK(oscar_get_aim_info_cb
),
6443 menu
= g_list_prepend(menu
, act
);
6446 if (purple_buddy_get_group(buddy
) != NULL
)
6448 /* We only do this if the user is in our buddy list */
6449 act
= purple_menu_action_new(_("Edit Buddy Comment"),
6450 PURPLE_CALLBACK(oscar_buddycb_edit_comment
),
6452 menu
= g_list_prepend(menu
, act
);
6458 act
= purple_menu_action_new(_("Get Status Msg"),
6459 PURPLE_CALLBACK(oscar_get_icqstatusmsg
),
6461 menu
= g_list_prepend(menu
, act
);
6466 oscar_util_name_compare(purple_account_get_username(account
), bname
) &&
6467 PURPLE_BUDDY_IS_ONLINE(buddy
))
6469 PeerConnection
*conn
;
6470 conn
= peer_connection_find_by_type(od
, bname
, OSCAR_CAPABILITY_DIRECTIM
);
6472 if (userinfo
->capabilities
& OSCAR_CAPABILITY_DIRECTIM
)
6476 act
= purple_menu_action_new(_("End Direct IM Session"),
6477 PURPLE_CALLBACK(oscar_close_directim
),
6482 act
= purple_menu_action_new(_("Direct IM"),
6483 PURPLE_CALLBACK(oscar_ask_directim
),
6486 menu
= g_list_prepend(menu
, act
);
6489 /* TODO: This menu item should be added by the core */
6490 if (userinfo
->capabilities
& OSCAR_CAPABILITY_GETFILE
) {
6491 act
= purple_menu_action_new(_("Get File"),
6492 PURPLE_CALLBACK(oscar_ask_getfile
),
6494 menu
= g_list_prepend(menu
, act
);
6499 if (od
->ssi
.received_data
&& purple_buddy_get_group(buddy
) != NULL
)
6502 * We only do this if the user is in our buddy list and we're
6503 * waiting for authorization.
6506 gname
= aim_ssi_itemlist_findparentname(od
->ssi
.local
, bname
);
6507 if (gname
&& aim_ssi_waitingforauth(od
->ssi
.local
, gname
, bname
))
6509 act
= purple_menu_action_new(_("Re-request Authorization"),
6510 PURPLE_CALLBACK(purple_auth_sendrequest_menu
),
6512 menu
= g_list_prepend(menu
, act
);
6516 menu
= g_list_reverse(menu
);
6522 GList
*oscar_blist_node_menu(PurpleBlistNode
*node
) {
6523 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
6524 return oscar_buddy_menu((PurpleBuddy
*) node
);
6531 oscar_icq_privacy_opts(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6533 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6534 PurpleAccount
*account
= purple_connection_get_account(gc
);
6535 PurpleRequestField
*f
;
6536 gboolean auth
, web_aware
;
6538 f
= purple_request_fields_get_field(fields
, "authorization");
6539 auth
= purple_request_field_bool_get_value(f
);
6541 f
= purple_request_fields_get_field(fields
, "web_aware");
6542 web_aware
= purple_request_field_bool_get_value(f
);
6544 purple_account_set_bool(account
, "authorization", auth
);
6545 purple_account_set_bool(account
, "web_aware", web_aware
);
6547 oscar_set_extendedstatus(gc
);
6548 aim_icq_setsecurity(od
, auth
, web_aware
);
6552 oscar_show_icq_privacy_opts(PurplePluginAction
*action
)
6554 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6555 PurpleAccount
*account
= purple_connection_get_account(gc
);
6556 PurpleRequestFields
*fields
;
6557 PurpleRequestFieldGroup
*g
;
6558 PurpleRequestField
*f
;
6559 gboolean auth
, web_aware
;
6561 auth
= purple_account_get_bool(account
, "authorization", OSCAR_DEFAULT_AUTHORIZATION
);
6562 web_aware
= purple_account_get_bool(account
, "web_aware", OSCAR_DEFAULT_WEB_AWARE
);
6564 fields
= purple_request_fields_new();
6566 g
= purple_request_field_group_new(NULL
);
6568 f
= purple_request_field_bool_new("authorization", _("Require authorization"), auth
);
6569 purple_request_field_group_add_field(g
, f
);
6571 f
= purple_request_field_bool_new("web_aware", _("Web aware (enabling this will cause you to receive SPAM!)"), web_aware
);
6572 purple_request_field_group_add_field(g
, f
);
6574 purple_request_fields_add_group(fields
, g
);
6576 purple_request_fields(gc
, _("ICQ Privacy Options"), _("ICQ Privacy Options"),
6578 _("OK"), G_CALLBACK(oscar_icq_privacy_opts
),
6580 purple_connection_get_account(gc
), NULL
, NULL
,
6584 void oscar_format_username(PurpleConnection
*gc
, const char *nick
) {
6585 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6586 if (!oscar_util_name_compare(purple_account_get_username(purple_connection_get_account(gc
)), nick
)) {
6587 if (!flap_connection_getbytype(od
, SNAC_FAMILY_ADMIN
)) {
6589 g_free(od
->newformatting
);
6590 od
->newformatting
= g_strdup(nick
);
6591 aim_srv_requestnew(od
, SNAC_FAMILY_ADMIN
);
6593 aim_admin_setnick(od
, flap_connection_getbytype(od
, SNAC_FAMILY_ADMIN
), nick
);
6596 purple_notify_error(gc
, NULL
, _("The new formatting is invalid."),
6597 _("Username formatting can change only capitalization and whitespace."));
6601 static void oscar_confirm_account(PurplePluginAction
*action
)
6603 PurpleConnection
*gc
;
6605 FlapConnection
*conn
;
6607 gc
= (PurpleConnection
*)action
->context
;
6608 od
= purple_connection_get_protocol_data(gc
);
6610 conn
= flap_connection_getbytype(od
, SNAC_FAMILY_ADMIN
);
6612 aim_admin_reqconfirm(od
, conn
);
6615 aim_srv_requestnew(od
, SNAC_FAMILY_ADMIN
);
6619 static void oscar_show_email(PurplePluginAction
*action
)
6621 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6622 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6623 FlapConnection
*conn
= flap_connection_getbytype(od
, SNAC_FAMILY_ADMIN
);
6626 aim_admin_getinfo(od
, conn
, 0x11);
6628 od
->reqemail
= TRUE
;
6629 aim_srv_requestnew(od
, SNAC_FAMILY_ADMIN
);
6633 static void oscar_change_email(PurpleConnection
*gc
, const char *email
)
6635 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6636 FlapConnection
*conn
= flap_connection_getbytype(od
, SNAC_FAMILY_ADMIN
);
6639 aim_admin_setemail(od
, conn
, email
);
6641 od
->setemail
= TRUE
;
6642 od
->email
= g_strdup(email
);
6643 aim_srv_requestnew(od
, SNAC_FAMILY_ADMIN
);
6647 static void oscar_show_change_email(PurplePluginAction
*action
)
6649 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6650 purple_request_input(gc
, NULL
, _("Change Address To:"), NULL
, NULL
,
6652 _("_OK"), G_CALLBACK(oscar_change_email
),
6654 purple_connection_get_account(gc
), NULL
, NULL
,
6658 static void oscar_show_awaitingauth(PurplePluginAction
*action
)
6660 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6661 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6664 PurpleAccount
*account
;
6667 text
= g_strdup("");
6668 account
= purple_connection_get_account(gc
);
6670 buddies
= purple_find_buddies(account
, NULL
);
6673 const gchar
*bname
, *gname
;
6675 buddy
= buddies
->data
;
6676 bname
= purple_buddy_get_name(buddy
);
6677 gname
= purple_group_get_name(purple_buddy_get_group(buddy
));
6678 if (aim_ssi_waitingforauth(od
->ssi
.local
, gname
, bname
)) {
6679 const gchar
*alias
= purple_buddy_get_alias_only(buddy
);
6681 tmp
= g_strdup_printf("%s %s (%s)<br>", text
, bname
, alias
);
6683 tmp
= g_strdup_printf("%s %s<br>", text
, bname
);
6690 buddies
= g_slist_delete_link(buddies
, buddies
);
6695 text
= g_strdup(_("<i>you are not waiting for authorization</i>"));
6698 purple_notify_formatted(gc
, NULL
, _("You are awaiting authorization from "
6699 "the following buddies"), _("You can re-request "
6700 "authorization from these buddies by "
6701 "right-clicking on them and selecting "
6702 "\"Re-request Authorization.\""), text
, NULL
, NULL
);
6706 static void search_by_email_cb(PurpleConnection
*gc
, const char *email
)
6708 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6710 aim_search_address(od
, email
);
6713 static void oscar_show_find_email(PurplePluginAction
*action
)
6715 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6716 purple_request_input(gc
, _("Find Buddy by Email"),
6717 _("Search for a buddy by email address"),
6718 _("Type the email address of the buddy you are "
6720 NULL
, FALSE
, FALSE
, NULL
,
6721 _("_Search"), G_CALLBACK(search_by_email_cb
),
6723 purple_connection_get_account(gc
), NULL
, NULL
,
6727 static void oscar_show_set_info(PurplePluginAction
*action
)
6729 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6730 purple_account_request_change_user_info(purple_connection_get_account(gc
));
6733 static void oscar_show_set_info_icqurl(PurplePluginAction
*action
)
6735 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6736 purple_notify_uri(gc
, "http://www.icq.com/whitepages/user_details.php");
6739 static void oscar_change_pass(PurplePluginAction
*action
)
6741 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6742 purple_account_request_change_password(purple_connection_get_account(gc
));
6746 * Only used when connecting with the old-style BUCP login.
6748 static void oscar_show_chpassurl(PurplePluginAction
*action
)
6750 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6751 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6752 gchar
*substituted
= purple_strreplace(od
->authinfo
->chpassurl
, "%s", purple_account_get_username(purple_connection_get_account(gc
)));
6753 purple_notify_uri(gc
, substituted
);
6754 g_free(substituted
);
6757 static void oscar_show_imforwardingurl(PurplePluginAction
*action
)
6759 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
6760 purple_notify_uri(gc
, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1");
6763 void oscar_set_icon(PurpleConnection
*gc
, PurpleStoredImage
*img
)
6765 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6768 aim_ssi_delicon(od
);
6770 PurpleCipherContext
*context
;
6772 gconstpointer data
= purple_imgstore_get_data(img
);
6773 size_t len
= purple_imgstore_get_size(img
);
6775 context
= purple_cipher_context_new_by_name("md5", NULL
);
6776 purple_cipher_context_append(context
, data
, len
);
6777 purple_cipher_context_digest(context
, 16, md5
, NULL
);
6778 purple_cipher_context_destroy(context
);
6780 aim_ssi_seticon(od
, md5
, 16);
6785 * Called by the Purple core to determine whether or not we're
6786 * allowed to send a file to this user.
6789 oscar_can_receive_file(PurpleConnection
*gc
, const char *who
)
6792 PurpleAccount
*account
;
6794 od
= purple_connection_get_protocol_data(gc
);
6795 account
= purple_connection_get_account(gc
);
6799 aim_userinfo_t
*userinfo
;
6800 userinfo
= aim_locate_finduserinfo(od
, who
);
6803 * Don't allowing sending a file to a user that does not support
6804 * file transfer, and don't allow sending to ourselves.
6806 if (((userinfo
== NULL
) ||
6807 (userinfo
->capabilities
& OSCAR_CAPABILITY_SENDFILE
)) &&
6808 oscar_util_name_compare(who
, purple_account_get_username(account
)))
6818 oscar_new_xfer(PurpleConnection
*gc
, const char *who
)
6822 PurpleAccount
*account
;
6823 PeerConnection
*conn
;
6825 od
= purple_connection_get_protocol_data(gc
);
6826 account
= purple_connection_get_account(gc
);
6828 xfer
= purple_xfer_new(account
, PURPLE_XFER_SEND
, who
);
6831 purple_xfer_ref(xfer
);
6832 purple_xfer_set_init_fnc(xfer
, peer_oft_sendcb_init
);
6833 purple_xfer_set_cancel_send_fnc(xfer
, peer_oft_cb_generic_cancel
);
6834 purple_xfer_set_request_denied_fnc(xfer
, peer_oft_cb_generic_cancel
);
6835 purple_xfer_set_ack_fnc(xfer
, peer_oft_sendcb_ack
);
6837 conn
= peer_connection_new(od
, OSCAR_CAPABILITY_SENDFILE
, who
);
6838 conn
->flags
|= PEER_CONNECTION_FLAG_INITIATED_BY_ME
;
6839 conn
->flags
|= PEER_CONNECTION_FLAG_APPROVED
;
6840 aim_icbm_makecookie(conn
->cookie
);
6849 * Called by the Purple core when the user indicates that a
6850 * file is to be sent to a special someone.
6853 oscar_send_file(PurpleConnection
*gc
, const char *who
, const char *file
)
6857 xfer
= oscar_new_xfer(gc
, who
);
6860 purple_xfer_request_accepted(xfer
, file
);
6862 purple_xfer_request(xfer
);
6866 oscar_actions(PurplePlugin
*plugin
, gpointer context
)
6868 PurpleConnection
*gc
= (PurpleConnection
*) context
;
6869 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6871 PurplePluginAction
*act
;
6873 act
= purple_plugin_action_new(_("Set User Info..."),
6874 oscar_show_set_info
);
6875 menu
= g_list_prepend(menu
, act
);
6879 act
= purple_plugin_action_new(_("Set User Info (web)..."),
6880 oscar_show_set_info_icqurl
);
6881 menu
= g_list_prepend(menu
, act
);
6884 act
= purple_plugin_action_new(_("Change Password..."),
6886 menu
= g_list_prepend(menu
, act
);
6888 if (od
->authinfo
!= NULL
&& od
->authinfo
->chpassurl
!= NULL
)
6890 /* This only happens when connecting with the old-style BUCP login */
6891 act
= purple_plugin_action_new(_("Change Password (web)"),
6892 oscar_show_chpassurl
);
6893 menu
= g_list_prepend(menu
, act
);
6898 act
= purple_plugin_action_new(_("Configure IM Forwarding (web)"),
6899 oscar_show_imforwardingurl
);
6900 menu
= g_list_prepend(menu
, act
);
6903 menu
= g_list_prepend(menu
, NULL
);
6908 act
= purple_plugin_action_new(_("Set Privacy Options..."),
6909 oscar_show_icq_privacy_opts
);
6910 menu
= g_list_prepend(menu
, act
);
6915 act
= purple_plugin_action_new(_("Confirm Account"),
6916 oscar_confirm_account
);
6917 menu
= g_list_prepend(menu
, act
);
6919 act
= purple_plugin_action_new(_("Display Currently Registered Email Address"),
6921 menu
= g_list_prepend(menu
, act
);
6923 act
= purple_plugin_action_new(_("Change Currently Registered Email Address..."),
6924 oscar_show_change_email
);
6925 menu
= g_list_prepend(menu
, act
);
6928 menu
= g_list_prepend(menu
, NULL
);
6930 act
= purple_plugin_action_new(_("Show Buddies Awaiting Authorization"),
6931 oscar_show_awaitingauth
);
6932 menu
= g_list_prepend(menu
, act
);
6934 menu
= g_list_prepend(menu
, NULL
);
6936 act
= purple_plugin_action_new(_("Search for Buddy by Email Address..."),
6937 oscar_show_find_email
);
6938 menu
= g_list_prepend(menu
, act
);
6941 act
= purple_plugin_action_new(_("Search for Buddy by Information"),
6943 menu
= g_list_prepend(menu
, act
);
6946 menu
= g_list_reverse(menu
);
6951 void oscar_change_passwd(PurpleConnection
*gc
, const char *old
, const char *new)
6953 OscarData
*od
= purple_connection_get_protocol_data(gc
);
6956 aim_icq_changepasswd(od
, new);
6958 FlapConnection
*conn
;
6959 conn
= flap_connection_getbytype(od
, SNAC_FAMILY_ADMIN
);
6961 aim_admin_changepasswd(od
, conn
, new, old
);
6964 od
->oldp
= g_strdup(old
);
6965 od
->newp
= g_strdup(new);
6966 aim_srv_requestnew(od
, SNAC_FAMILY_ADMIN
);
6972 oscar_convo_closed(PurpleConnection
*gc
, const char *who
)
6975 PeerConnection
*conn
;
6977 od
= purple_connection_get_protocol_data(gc
);
6978 conn
= peer_connection_find_by_type(od
, who
, OSCAR_CAPABILITY_DIRECTIM
);
6983 aim_im_sendch2_cancel(conn
);
6985 peer_connection_destroy(conn
, OSCAR_DISCONNECT_LOCAL_CLOSED
, NULL
);
6990 oscar_normalize(const PurpleAccount
*account
, const char *str
)
6992 static char buf
[BUF_LEN
];
6996 g_return_val_if_fail(str
!= NULL
, NULL
);
6998 /* copy str to buf and skip all blanks */
7000 for (j
= 0; str
[j
]; j
++) {
7001 if (str
[j
] != ' ') {
7003 if (i
>= BUF_LEN
- 1)
7009 tmp1
= g_utf8_strdown(buf
, -1);
7010 tmp2
= g_utf8_normalize(tmp1
, -1, G_NORMALIZE_DEFAULT
);
7019 oscar_offline_message(const PurpleBuddy
*buddy
)
7024 /* TODO: Find somewhere to put this instead of including it in a bunch of places.
7025 * Maybe just change purple_accounts_find() to return anything for the prpl if there is no acct_id.
7027 static PurpleAccount
*find_acct(const char *prpl
, const char *acct_id
)
7029 PurpleAccount
*acct
= NULL
;
7031 /* If we have a specific acct, use it */
7033 acct
= purple_accounts_find(acct_id
, prpl
);
7034 if (acct
&& !purple_account_is_connected(acct
))
7036 } else { /* Otherwise find an active account for the protocol */
7037 GList
*l
= purple_accounts_get_all();
7039 if (!strcmp(prpl
, purple_account_get_protocol_id(l
->data
))
7040 && purple_account_is_connected(l
->data
)) {
7052 static gboolean
oscar_uri_handler(const char *proto
, const char *cmd
, GHashTable
*params
)
7054 char *acct_id
= g_hash_table_lookup(params
, "account");
7056 PurpleAccount
*acct
;
7058 if (g_ascii_strcasecmp(proto
, "aim") && g_ascii_strcasecmp(proto
, "icq"))
7061 g_snprintf(prpl
, sizeof(prpl
), "prpl-%s", proto
);
7063 acct
= find_acct(prpl
, acct_id
);
7068 /* aim:GoIM?screenname=SCREENNAME&message=MESSAGE */
7069 if (!g_ascii_strcasecmp(cmd
, "GoIM")) {
7070 char *bname
= g_hash_table_lookup(params
, "screenname");
7072 char *message
= g_hash_table_lookup(params
, "message");
7074 PurpleConversation
*conv
= purple_find_conversation_with_account(
7075 PURPLE_CONV_TYPE_IM
, bname
, acct
);
7077 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, bname
);
7078 purple_conversation_present(conv
);
7081 /* Spaces are encoded as '+' */
7082 g_strdelimit(message
, "+", ' ');
7083 purple_conv_send_confirm(conv
, message
);
7087 **If pidgindialogs_im() was in the core, we could use it here.
7088 * It is all purple_request_* based, but I'm not sure it really belongs in the core
7089 pidgindialogs_im();*/
7093 /* aim:GoChat?roomname=CHATROOMNAME&exchange=4 */
7094 else if (!g_ascii_strcasecmp(cmd
, "GoChat")) {
7095 char *rname
= g_hash_table_lookup(params
, "roomname");
7097 /* This is somewhat hacky, but the params aren't useful after this command */
7098 g_hash_table_insert(params
, g_strdup("exchange"), g_strdup("4"));
7099 g_hash_table_insert(params
, g_strdup("room"), g_strdup(rname
));
7100 serv_join_chat(purple_account_get_connection(acct
), params
);
7103 ** Same as above (except that this would have to be re-written using purple_request_*)
7104 pidgin_blist_joinchat_show(); */
7108 /* aim:AddBuddy?screenname=SCREENNAME&groupname=GROUPNAME*/
7109 else if (!g_ascii_strcasecmp(cmd
, "AddBuddy")) {
7110 char *bname
= g_hash_table_lookup(params
, "screenname");
7111 char *gname
= g_hash_table_lookup(params
, "groupname");
7112 purple_blist_request_add_buddy(acct
, bname
, gname
, NULL
);
7119 void oscar_init(PurplePluginProtocolInfo
*prpl_info
)
7121 PurpleAccountOption
*option
;
7122 static gboolean init
= FALSE
;
7124 option
= purple_account_option_string_new(_("Server"), "server", OSCAR_DEFAULT_LOGIN_SERVER
);
7125 prpl_info
->protocol_options
= g_list_append(prpl_info
->protocol_options
, option
);
7127 option
= purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT
);
7128 prpl_info
->protocol_options
= g_list_append(prpl_info
->protocol_options
, option
);
7130 option
= purple_account_option_bool_new(_("Use SSL"), "use_ssl",
7131 OSCAR_DEFAULT_USE_SSL
);
7132 prpl_info
->protocol_options
= g_list_append(prpl_info
->protocol_options
, option
);
7134 option
= purple_account_option_bool_new(_("Use clientLogin"), "use_clientlogin",
7135 OSCAR_DEFAULT_USE_CLIENTLOGIN
);
7136 prpl_info
->protocol_options
= g_list_append(prpl_info
->protocol_options
, option
);
7138 option
= purple_account_option_bool_new(
7139 _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy",
7140 OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY
);
7141 prpl_info
->protocol_options
= g_list_append(prpl_info
->protocol_options
, option
);
7143 option
= purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins",
7144 OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS
);
7145 prpl_info
->protocol_options
= g_list_append(prpl_info
->protocol_options
, option
);
7152 purple_prefs_add_none("/plugins/prpl/oscar");
7153 purple_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE
);
7155 purple_prefs_remove("/plugins/prpl/oscar/show_idle");
7156 purple_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy");
7158 /* protocol handler */
7159 /* TODO: figure out a good instance to use here */
7160 purple_signal_connect(purple_get_core(), "uri-handler", &init
,
7161 PURPLE_CALLBACK(oscar_uri_handler
), NULL
);