2 * Purple's oscar protocol plugin
3 * This file is the legal property of its developers.
4 * Please see the AUTHORS file distributed alongside this file.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 * Displaying various information about buddies.
29 oscar_caps_to_string(guint64 caps
)
39 str
= g_string_new("");
40 while (bit
<= OSCAR_CAPABILITY_LAST
) {
43 case OSCAR_CAPABILITY_BUDDYICON
:
44 tmp
= _("Buddy Icon");
46 case OSCAR_CAPABILITY_TALK
:
49 case OSCAR_CAPABILITY_DIRECTIM
:
50 tmp
= _("AIM Direct IM");
52 case OSCAR_CAPABILITY_CHAT
:
55 case OSCAR_CAPABILITY_GETFILE
:
58 case OSCAR_CAPABILITY_SENDFILE
:
61 case OSCAR_CAPABILITY_GAMES
:
62 case OSCAR_CAPABILITY_GAMES2
:
65 case OSCAR_CAPABILITY_XTRAZ
:
66 case OSCAR_CAPABILITY_NEWCAPS
:
69 case OSCAR_CAPABILITY_ADDINS
:
72 case OSCAR_CAPABILITY_SENDBUDDYLIST
:
73 tmp
= _("Send Buddy List");
75 case OSCAR_CAPABILITY_ICQ_DIRECT
:
76 tmp
= _("ICQ Direct Connect");
78 case OSCAR_CAPABILITY_APINFO
:
81 case OSCAR_CAPABILITY_ICQRTF
:
84 case OSCAR_CAPABILITY_EMPTY
:
87 case OSCAR_CAPABILITY_ICQSERVERRELAY
:
88 tmp
= _("ICQ Server Relay");
90 case OSCAR_CAPABILITY_UNICODEOLD
:
91 tmp
= _("Old ICQ UTF8");
93 case OSCAR_CAPABILITY_TRILLIANCRYPT
:
94 tmp
= _("Trillian Encryption");
96 case OSCAR_CAPABILITY_UNICODE
:
99 case OSCAR_CAPABILITY_HIPTOP
:
102 case OSCAR_CAPABILITY_SECUREIM
:
103 tmp
= _("Security Enabled");
105 case OSCAR_CAPABILITY_VIDEO
:
106 tmp
= _("Video Chat");
108 /* Not actually sure about this one... WinAIM doesn't show anything */
109 case OSCAR_CAPABILITY_ICHATAV
:
112 case OSCAR_CAPABILITY_LIVEVIDEO
:
113 tmp
= _("Live Video");
115 case OSCAR_CAPABILITY_CAMERA
:
118 case OSCAR_CAPABILITY_ICHAT_SCREENSHARE
:
119 tmp
= _("Screen Sharing");
126 g_string_append_printf(str
, "%s%s", (*(str
->str
) == '\0' ? "" : ", "), tmp
);
131 return g_string_free(str
, FALSE
);
135 oscar_user_info_convert_and_add(PurpleAccount
*account
, OscarData
*od
, PurpleNotifyUserInfo
*user_info
,
136 const char *name
, const char *value
)
140 if (value
&& value
[0] && (utf8
= oscar_utf8_try_convert(account
, od
, value
))) {
141 /* TODO: Check whether it's correct to call add_pair_html,
142 or if we should be using add_pair_plaintext. Will
143 need to check callers of this function. */
144 purple_notify_user_info_add_pair_html(user_info
, name
, utf8
);
150 oscar_user_info_convert_and_add_hyperlink(PurpleAccount
*account
, OscarData
*od
, PurpleNotifyUserInfo
*user_info
,
151 const char *name
, const char *value
, const char *url_prefix
)
155 if (value
&& value
[0] && (utf8
= oscar_utf8_try_convert(account
, od
, value
))) {
156 gchar
*tmp
= g_strdup_printf("<a href=\"%s%s\">%s</a>", url_prefix
, utf8
, utf8
);
157 /* TODO: Check whether it's correct to call add_pair_html,
158 or if we should be using add_pair_plaintext. Will
159 need to check callers of this function. */
160 purple_notify_user_info_add_pair_html(user_info
, name
, tmp
);
167 * @brief Append the status information to a user_info struct
169 * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
171 * @param gc The PurpleConnection
172 * @param user_info A PurpleNotifyUserInfo object to which status information will be added
173 * @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().
174 * @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().
175 * @param use_html_status If TRUE, prefer HTML-formatted away message over plaintext available message.
178 oscar_user_info_append_status(PurpleConnection
*gc
, PurpleNotifyUserInfo
*user_info
, PurpleBuddy
*b
, aim_userinfo_t
*userinfo
, gboolean use_html_status
)
180 PurpleAccount
*account
= purple_connection_get_account(gc
);
182 PurplePresence
*presence
= NULL
;
183 PurpleStatus
*status
= NULL
;
184 gchar
*message
= NULL
, *itmsurl
= NULL
, *tmp
;
185 gboolean escaping_needed
= TRUE
;
187 od
= purple_connection_get_protocol_data(gc
);
189 if (b
== NULL
&& userinfo
== NULL
)
193 b
= purple_blist_find_buddy(purple_connection_get_account(gc
), userinfo
->bn
);
195 userinfo
= aim_locate_finduserinfo(od
, purple_buddy_get_name(b
));
198 presence
= purple_buddy_get_presence(b
);
199 status
= purple_presence_get_active_status(presence
);
202 /* If we have both b and userinfo we favor userinfo, because if we're
203 viewing someone's profile then we want the HTML away message, and
204 the "message" attribute of the status contains only the plaintext
207 if ((userinfo
->flags
& AIM_FLAG_AWAY
) && use_html_status
&& userinfo
->away_len
> 0 && userinfo
->away
!= NULL
&& userinfo
->away_encoding
!= NULL
) {
209 message
= oscar_encoding_to_utf8(userinfo
->away_encoding
, userinfo
->away
, userinfo
->away_len
);
210 escaping_needed
= FALSE
;
213 * Available message or non-HTML away message (because that's
214 * all we have right now.
216 if ((userinfo
->status
!= NULL
) && userinfo
->status
[0] != '\0') {
217 message
= oscar_encoding_to_utf8(userinfo
->status_encoding
, userinfo
->status
, userinfo
->status_len
);
219 #if defined (_WIN32) || defined (__APPLE__)
220 if (userinfo
->itmsurl
&& (userinfo
->itmsurl
[0] != '\0')) {
221 itmsurl
= oscar_encoding_to_utf8(userinfo
->itmsurl_encoding
, userinfo
->itmsurl
, userinfo
->itmsurl_len
);
226 message
= g_strdup(purple_status_get_attr_string(status
, "message"));
227 itmsurl
= g_strdup(purple_status_get_attr_string(status
, "itmsurl"));
231 tmp
= oscar_util_format_string(message
, purple_account_get_username(account
));
234 if (escaping_needed
) {
235 tmp
= purple_markup_escape_text(message
, -1);
241 if (use_html_status
&& itmsurl
) {
242 tmp
= g_strdup_printf("<a href=\"%s\">%s</a>", itmsurl
, message
);
248 if (purple_presence_is_online(presence
)) {
249 gboolean is_away
= ((status
&& !purple_status_is_available(status
)) || (userinfo
&& (userinfo
->flags
& AIM_FLAG_AWAY
)));
250 if (oscar_util_valid_name_icq(purple_buddy_get_name(b
)) || is_away
|| !message
|| !(*message
)) {
251 /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
252 * If the status name and the message are the same, only show one. */
253 const char *status_name
= purple_status_get_name(status
);
254 if (status_name
&& message
&& purple_strequal(status_name
, message
))
257 tmp
= g_strdup_printf("%s%s%s",
258 status_name
? status_name
: "",
259 ((status_name
&& message
) && *message
) ? ": " : "",
260 (message
&& *message
) ? message
: "");
265 } else if (aim_ssi_waitingforauth(&od
->ssi
.local
,
266 aim_ssi_itemlist_findparentname(&od
->ssi
.local
, purple_buddy_get_name(b
)),
267 purple_buddy_get_name(b
)))
269 /* Note if an offline buddy is not authorized */
270 tmp
= g_strdup_printf("%s%s%s",
272 (message
&& *message
) ? ": " : "",
273 (message
&& *message
) ? message
: "");
278 message
= g_strdup(_("Offline"));
286 status
= purple_presence_get_status(presence
, "mood");
287 mood
= icq_get_custom_icon_description(purple_status_get_attr_string(status
, PURPLE_MOOD_NAME
));
289 comment
= purple_status_get_attr_string(status
, PURPLE_MOOD_COMMENT
);
291 char *escaped_comment
= purple_markup_escape_text(comment
, -1);
292 description
= g_strdup_printf("%s (%s)", _(mood
), escaped_comment
);
293 g_free(escaped_comment
);
295 description
= g_strdup(_(mood
));
297 purple_notify_user_info_add_pair_html(user_info
, _("Mood"), description
);
302 purple_notify_user_info_add_pair_html(user_info
, _("Status"), message
);
308 oscar_user_info_append_extra_info(PurpleConnection
*gc
, PurpleNotifyUserInfo
*user_info
, PurpleBuddy
*b
, aim_userinfo_t
*userinfo
)
311 PurpleAccount
*account
;
312 PurpleGroup
*g
= NULL
;
313 struct buddyinfo
*bi
= NULL
;
315 const char *bname
= NULL
, *gname
= NULL
;
317 od
= purple_connection_get_protocol_data(gc
);
318 account
= purple_connection_get_account(gc
);
320 if ((user_info
== NULL
) || ((b
== NULL
) && (userinfo
== NULL
)))
323 if (userinfo
== NULL
)
324 userinfo
= aim_locate_finduserinfo(od
, purple_buddy_get_name(b
));
327 b
= purple_blist_find_buddy(account
, userinfo
->bn
);
330 bname
= purple_buddy_get_name(b
);
331 g
= purple_buddy_get_group(b
);
332 gname
= purple_group_get_name(g
);
335 if (userinfo
!= NULL
)
336 bi
= g_hash_table_lookup(od
->buddyinfo
, purple_normalize(account
, userinfo
->bn
));
338 if ((bi
!= NULL
) && (bi
->ipaddr
!= 0)) {
340 sprintf(tmp2
, "%u.%u.%u.%u",
341 0xFF & ((bi
->ipaddr
& 0xff000000) >> 24),
342 0xFF & ((bi
->ipaddr
& 0x00ff0000) >> 16),
343 0xFF & ((bi
->ipaddr
& 0x0000ff00) >> 8),
344 0xFF & (bi
->ipaddr
& 0x000000ff));
345 purple_notify_user_info_add_pair_plaintext(user_info
, _("IP Address"), tmp2
);
348 if ((userinfo
!= NULL
) && (userinfo
->warnlevel
!= 0)) {
350 sprintf(tmp2
, "%d", (int)(userinfo
->warnlevel
/10.0 + .5));
351 purple_notify_user_info_add_pair_plaintext(user_info
, _("Warning Level"), tmp2
);
354 if ((b
!= NULL
) && (bname
!= NULL
) && (g
!= NULL
) && (gname
!= NULL
)) {
355 tmp
= aim_ssi_getcomment(&od
->ssi
.local
, gname
, bname
);
357 char *tmp2
= g_markup_escape_text(tmp
, strlen(tmp
));
360 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Buddy Comment"), tmp2
);
367 oscar_user_info_display_error(OscarData
*od
, guint16 error_reason
, gchar
*buddy
)
369 PurpleNotifyUserInfo
*user_info
= purple_notify_user_info_new();
370 gchar
*buf
= g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason
));
371 purple_notify_user_info_add_pair_plaintext(user_info
, NULL
, buf
);
372 purple_notify_userinfo(od
->gc
, buddy
, user_info
, NULL
, NULL
);
373 purple_notify_user_info_destroy(user_info
);
374 if (!purple_conversation_present_error(buddy
,
375 purple_connection_get_account(od
->gc
), buf
))
377 purple_notify_error(od
->gc
, NULL
, buf
, NULL
,
378 purple_request_cpar_from_connection(od
->gc
));
384 oscar_user_info_display_icq(OscarData
*od
, struct aim_icq_info
*info
)
386 PurpleConnection
*gc
= od
->gc
;
387 PurpleAccount
*account
= purple_connection_get_account(gc
);
389 struct buddyinfo
*bi
;
391 PurpleNotifyUserInfo
*user_info
;
396 user_info
= purple_notify_user_info_new();
398 g_snprintf(who
, sizeof(who
), "%u", info
->uin
);
399 buddy
= purple_blist_find_buddy(account
, who
);
401 bi
= g_hash_table_lookup(od
->buddyinfo
, purple_normalize(account
, purple_buddy_get_name(buddy
)));
405 purple_notify_user_info_add_pair_plaintext(user_info
, _("UIN"), who
);
406 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Nick"), info
->nick
);
407 if ((bi
!= NULL
) && (bi
->ipaddr
!= 0)) {
409 sprintf(tstr
, "%u.%u.%u.%u",
410 0xFF & ((bi
->ipaddr
& 0xff000000) >> 24),
411 0xFF & ((bi
->ipaddr
& 0x00ff0000) >> 16),
412 0xFF & ((bi
->ipaddr
& 0x0000ff00) >> 8),
413 0xFF & (bi
->ipaddr
& 0x000000ff));
414 purple_notify_user_info_add_pair_plaintext(user_info
, _("IP Address"), tstr
);
416 oscar_user_info_convert_and_add(account
, od
, user_info
, _("First Name"), info
->first
);
417 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Last Name"), info
->last
);
418 oscar_user_info_convert_and_add_hyperlink(account
, od
, user_info
, _("Email Address"), info
->email
, "mailto:");
419 if (info
->numaddresses
&& info
->email2
) {
421 for (i
= 0; i
< info
->numaddresses
; i
++) {
422 oscar_user_info_convert_and_add_hyperlink(account
, od
, user_info
, _("Email Address"), info
->email2
[i
], "mailto:");
425 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Mobile Phone"), info
->mobile
);
427 if (info
->gender
!= 0)
428 purple_notify_user_info_add_pair_plaintext(user_info
, _("Gender"), (info
->gender
== 1 ? _("Female") : _("Male")));
430 if ((info
->birthyear
> 1900) && (info
->birthmonth
> 0) && (info
->birthday
> 0)) {
431 /* Initialize the struct properly or strftime() will crash
432 * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
433 time_t t
= time(NULL
);
434 struct tm
*tm
= localtime(&t
);
436 tm
->tm_mday
= (int)info
->birthday
;
437 tm
->tm_mon
= (int)info
->birthmonth
- 1;
438 tm
->tm_year
= (int)info
->birthyear
- 1900;
440 /* Ignore dst setting of today to avoid timezone shift between
441 * dates in summer and winter time. */
444 /* To be 100% sure that the fields are re-normalized.
445 * If you're sure strftime() ALWAYS does this EVERYWHERE,
446 * feel free to remove it. --rlaager */
449 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Birthday"), purple_date_format_short(tm
));
451 if ((info
->age
> 0) && (info
->age
< 255)) {
453 snprintf(age
, sizeof(age
), "%hhd", info
->age
);
454 purple_notify_user_info_add_pair_plaintext(user_info
, _("Age"), age
);
456 /* TODO: Is it correct to pass info->email here...? */
457 oscar_user_info_convert_and_add_hyperlink(account
, od
, user_info
, _("Personal Web Page"), info
->email
, "");
459 oscar_user_info_append_status(gc
, user_info
, buddy
, /* aim_userinfo_t */ NULL
, /* use_html_status */ TRUE
);
461 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Additional Information"), info
->info
);
462 purple_notify_user_info_add_section_break(user_info
);
464 if ((info
->homeaddr
&& (info
->homeaddr
[0])) || (info
->homecity
&& info
->homecity
[0]) || (info
->homestate
&& info
->homestate
[0]) || (info
->homezip
&& info
->homezip
[0])) {
465 purple_notify_user_info_add_section_header(user_info
, _("Home Address"));
467 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Address"), info
->homeaddr
);
468 oscar_user_info_convert_and_add(account
, od
, user_info
, _("City"), info
->homecity
);
469 oscar_user_info_convert_and_add(account
, od
, user_info
, _("State"), info
->homestate
);
470 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Zip Code"), info
->homezip
);
472 if ((info
->workaddr
&& info
->workaddr
[0]) || (info
->workcity
&& info
->workcity
[0]) || (info
->workstate
&& info
->workstate
[0]) || (info
->workzip
&& info
->workzip
[0])) {
473 purple_notify_user_info_add_section_header(user_info
, _("Work Address"));
475 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Address"), info
->workaddr
);
476 oscar_user_info_convert_and_add(account
, od
, user_info
, _("City"), info
->workcity
);
477 oscar_user_info_convert_and_add(account
, od
, user_info
, _("State"), info
->workstate
);
478 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Zip Code"), info
->workzip
);
480 if ((info
->workcompany
&& info
->workcompany
[0]) || (info
->workdivision
&& info
->workdivision
[0]) || (info
->workposition
&& info
->workposition
[0]) || (info
->workwebpage
&& info
->workwebpage
[0])) {
481 purple_notify_user_info_add_section_header(user_info
, _("Work Information"));
483 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Company"), info
->workcompany
);
484 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Division"), info
->workdivision
);
485 oscar_user_info_convert_and_add(account
, od
, user_info
, _("Position"), info
->workposition
);
486 /* TODO: Is it correct to pass info->email here...? */
487 oscar_user_info_convert_and_add_hyperlink(account
, od
, user_info
, _("Web Page"), info
->email
, "");
490 purple_notify_userinfo(gc
, who
, user_info
, NULL
, NULL
);
491 purple_notify_user_info_destroy(user_info
);
495 oscar_user_info_display_aim(OscarData
*od
, aim_userinfo_t
*userinfo
)
497 PurpleConnection
*gc
= od
->gc
;
498 PurpleAccount
*account
= purple_connection_get_account(gc
);
499 PurpleNotifyUserInfo
*user_info
= purple_notify_user_info_new();
500 gchar
*tmp
= NULL
, *info_utf8
= NULL
, *base_profile_url
= NULL
;
502 oscar_user_info_append_status(gc
, user_info
, /* PurpleBuddy */ NULL
, userinfo
, /* use_html_status */ TRUE
);
504 if ((userinfo
->present
& AIM_USERINFO_PRESENT_IDLE
) && userinfo
->idletime
!= 0) {
505 tmp
= purple_str_seconds_to_string(userinfo
->idletime
*60);
506 purple_notify_user_info_add_pair_plaintext(user_info
, _("Idle"), tmp
);
510 oscar_user_info_append_extra_info(gc
, user_info
, NULL
, userinfo
);
512 if ((userinfo
->present
& AIM_USERINFO_PRESENT_ONLINESINCE
) && !oscar_util_valid_name_sms(userinfo
->bn
)) {
513 /* An SMS contact is always online; its Online Since value is not useful */
514 time_t t
= userinfo
->onlinesince
;
515 purple_notify_user_info_add_pair_plaintext(user_info
, _("Online Since"), purple_date_format_full(localtime(&t
)));
518 if (userinfo
->present
& AIM_USERINFO_PRESENT_MEMBERSINCE
) {
519 time_t t
= userinfo
->membersince
;
520 purple_notify_user_info_add_pair_plaintext(user_info
, _("Member Since"), purple_date_format_full(localtime(&t
)));
523 if (userinfo
->capabilities
!= 0) {
524 tmp
= oscar_caps_to_string(userinfo
->capabilities
);
526 purple_notify_user_info_add_pair_plaintext(user_info
, _("Capabilities"), tmp
);
531 if ((userinfo
->info_len
> 0) && (userinfo
->info
!= NULL
) && (userinfo
->info_encoding
!= NULL
)) {
532 info_utf8
= oscar_encoding_to_utf8(userinfo
->info_encoding
, userinfo
->info
, userinfo
->info_len
);
533 tmp
= oscar_util_format_string(info_utf8
, purple_account_get_username(account
));
534 purple_notify_user_info_add_section_break(user_info
);
536 /* TODO: Check whether it's correct to call add_pair_html,
537 or if we should be using add_pair_plaintext */
538 purple_notify_user_info_add_pair_html(user_info
, _("Profile"), tmp
);
544 purple_notify_user_info_add_section_break(user_info
);
545 base_profile_url
= oscar_util_valid_name_icq(userinfo
->bn
) ? "http://www.icq.com/people" : "http://profiles.aim.com";
546 tmp
= g_strdup_printf("<a href=\"%s/%s\">%s</a>",
547 base_profile_url
, purple_normalize(account
, userinfo
->bn
), _("View web profile"));
548 purple_notify_user_info_add_pair_html(user_info
, NULL
, tmp
);
551 purple_notify_userinfo(gc
, userinfo
->bn
, user_info
, NULL
, NULL
);
552 purple_notify_user_info_destroy(user_info
);