Merged pidgin/main into default
[pidgin-git.git] / libpurple / protocols / oscar / userinfo.c
blob27a6758c8890e73a7a960886242f1a99ee5dfe3f
1 /*
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.
25 #include "encoding.h"
26 #include "oscar.h"
28 static gchar *
29 oscar_caps_to_string(guint64 caps)
31 GString *str;
32 const gchar *tmp;
33 guint64 bit = 1;
35 str = g_string_new("");
37 if (!caps) {
38 return NULL;
39 } else while (bit <= OSCAR_CAPABILITY_LAST) {
40 if (bit & caps) {
41 switch (bit) {
42 case OSCAR_CAPABILITY_BUDDYICON:
43 tmp = _("Buddy Icon");
44 break;
45 case OSCAR_CAPABILITY_TALK:
46 tmp = _("Voice");
47 break;
48 case OSCAR_CAPABILITY_DIRECTIM:
49 tmp = _("AIM Direct IM");
50 break;
51 case OSCAR_CAPABILITY_CHAT:
52 tmp = _("Chat");
53 break;
54 case OSCAR_CAPABILITY_GETFILE:
55 tmp = _("Get File");
56 break;
57 case OSCAR_CAPABILITY_SENDFILE:
58 tmp = _("Send File");
59 break;
60 case OSCAR_CAPABILITY_GAMES:
61 case OSCAR_CAPABILITY_GAMES2:
62 tmp = _("Games");
63 break;
64 case OSCAR_CAPABILITY_XTRAZ:
65 case OSCAR_CAPABILITY_NEWCAPS:
66 tmp = _("ICQ Xtraz");
67 break;
68 case OSCAR_CAPABILITY_ADDINS:
69 tmp = _("Add-Ins");
70 break;
71 case OSCAR_CAPABILITY_SENDBUDDYLIST:
72 tmp = _("Send Buddy List");
73 break;
74 case OSCAR_CAPABILITY_ICQ_DIRECT:
75 tmp = _("ICQ Direct Connect");
76 break;
77 case OSCAR_CAPABILITY_APINFO:
78 tmp = _("AP User");
79 break;
80 case OSCAR_CAPABILITY_ICQRTF:
81 tmp = _("ICQ RTF");
82 break;
83 case OSCAR_CAPABILITY_EMPTY:
84 tmp = _("Nihilist");
85 break;
86 case OSCAR_CAPABILITY_ICQSERVERRELAY:
87 tmp = _("ICQ Server Relay");
88 break;
89 case OSCAR_CAPABILITY_UNICODEOLD:
90 tmp = _("Old ICQ UTF8");
91 break;
92 case OSCAR_CAPABILITY_TRILLIANCRYPT:
93 tmp = _("Trillian Encryption");
94 break;
95 case OSCAR_CAPABILITY_UNICODE:
96 tmp = _("ICQ UTF8");
97 break;
98 case OSCAR_CAPABILITY_HIPTOP:
99 tmp = _("Hiptop");
100 break;
101 case OSCAR_CAPABILITY_SECUREIM:
102 tmp = _("Security Enabled");
103 break;
104 case OSCAR_CAPABILITY_VIDEO:
105 tmp = _("Video Chat");
106 break;
107 /* Not actually sure about this one... WinAIM doesn't show anything */
108 case OSCAR_CAPABILITY_ICHATAV:
109 tmp = _("iChat AV");
110 break;
111 case OSCAR_CAPABILITY_LIVEVIDEO:
112 tmp = _("Live Video");
113 break;
114 case OSCAR_CAPABILITY_CAMERA:
115 tmp = _("Camera");
116 break;
117 case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
118 tmp = _("Screen Sharing");
119 break;
120 default:
121 tmp = NULL;
122 break;
124 if (tmp)
125 g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
127 bit <<= 1;
130 return g_string_free(str, FALSE);
133 static void
134 oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
135 const char *name, const char *value)
137 gchar *utf8;
139 if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
140 /* TODO: Check whether it's correct to call add_pair_html,
141 or if we should be using add_pair_plaintext. Will
142 need to check callers of this function. */
143 purple_notify_user_info_add_pair_html(user_info, name, utf8);
144 g_free(utf8);
148 static void
149 oscar_user_info_convert_and_add_hyperlink(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
150 const char *name, const char *value, const char *url_prefix)
152 gchar *utf8;
154 if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
155 gchar *tmp = g_strdup_printf("<a href=\"%s%s\">%s</a>", url_prefix, utf8, utf8);
156 /* TODO: Check whether it's correct to call add_pair_html,
157 or if we should be using add_pair_plaintext. Will
158 need to check callers of this function. */
159 purple_notify_user_info_add_pair_html(user_info, name, tmp);
160 g_free(utf8);
161 g_free(tmp);
166 * @brief Append the status information to a user_info struct
168 * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
170 * @param gc The PurpleConnection
171 * @param user_info A PurpleNotifyUserInfo object to which status information will be added
172 * @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().
173 * @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().
174 * @param use_html_status If TRUE, prefer HTML-formatted away message over plaintext available message.
176 void
177 oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status)
179 PurpleAccount *account = purple_connection_get_account(gc);
180 OscarData *od;
181 PurplePresence *presence = NULL;
182 PurpleStatus *status = NULL;
183 gchar *message = NULL, *itmsurl = NULL, *tmp;
184 gboolean escaping_needed = TRUE;
186 od = purple_connection_get_protocol_data(gc);
188 if (b == NULL && userinfo == NULL)
189 return;
191 if (b == NULL)
192 b = purple_blist_find_buddy(purple_connection_get_account(gc), userinfo->bn);
193 else
194 userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
196 if (b) {
197 presence = purple_buddy_get_presence(b);
198 status = purple_presence_get_active_status(presence);
201 /* If we have both b and userinfo we favor userinfo, because if we're
202 viewing someone's profile then we want the HTML away message, and
203 the "message" attribute of the status contains only the plaintext
204 message. */
205 if (userinfo) {
206 if ((userinfo->flags & AIM_FLAG_AWAY) && use_html_status && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) {
207 /* Away message */
208 message = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len);
209 escaping_needed = FALSE;
210 } else {
212 * Available message or non-HTML away message (because that's
213 * all we have right now.
215 if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
216 message = oscar_encoding_to_utf8(userinfo->status_encoding, userinfo->status, userinfo->status_len);
218 #if defined (_WIN32) || defined (__APPLE__)
219 if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
220 itmsurl = oscar_encoding_to_utf8(userinfo->itmsurl_encoding, userinfo->itmsurl, userinfo->itmsurl_len);
222 #endif
224 } else {
225 message = g_strdup(purple_status_get_attr_string(status, "message"));
226 itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
229 if (message) {
230 tmp = oscar_util_format_string(message, purple_account_get_username(account));
231 g_free(message);
232 message = tmp;
233 if (escaping_needed) {
234 tmp = purple_markup_escape_text(message, -1);
235 g_free(message);
236 message = tmp;
240 if (use_html_status && itmsurl) {
241 tmp = g_strdup_printf("<a href=\"%s\">%s</a>", itmsurl, message);
242 g_free(message);
243 message = tmp;
246 if (b) {
247 if (purple_presence_is_online(presence)) {
248 gboolean is_away = ((status && !purple_status_is_available(status)) || (userinfo && (userinfo->flags & AIM_FLAG_AWAY)));
249 if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) {
250 /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
251 * If the status name and the message are the same, only show one. */
252 const char *status_name = purple_status_get_name(status);
253 if (status_name && message && purple_strequal(status_name, message))
254 status_name = NULL;
256 tmp = g_strdup_printf("%s%s%s",
257 status_name ? status_name : "",
258 ((status_name && message) && *message) ? ": " : "",
259 (message && *message) ? message : "");
260 g_free(message);
261 message = tmp;
264 } else if (aim_ssi_waitingforauth(&od->ssi.local,
265 aim_ssi_itemlist_findparentname(&od->ssi.local, purple_buddy_get_name(b)),
266 purple_buddy_get_name(b)))
268 /* Note if an offline buddy is not authorized */
269 tmp = g_strdup_printf("%s%s%s",
270 _("Not Authorized"),
271 (message && *message) ? ": " : "",
272 (message && *message) ? message : "");
273 g_free(message);
274 message = tmp;
275 } else {
276 g_free(message);
277 message = g_strdup(_("Offline"));
281 if (presence) {
282 const char *mood;
283 const char *comment;
284 char *description;
285 status = purple_presence_get_status(presence, "mood");
286 mood = icq_get_custom_icon_description(purple_status_get_attr_string(status, PURPLE_MOOD_NAME));
287 if (mood) {
288 comment = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT);
289 if (comment) {
290 char *escaped_comment = purple_markup_escape_text(comment, -1);
291 description = g_strdup_printf("%s (%s)", _(mood), escaped_comment);
292 g_free(escaped_comment);
293 } else {
294 description = g_strdup(_(mood));
296 purple_notify_user_info_add_pair_html(user_info, _("Mood"), description);
297 g_free(description);
301 purple_notify_user_info_add_pair_html(user_info, _("Status"), message);
302 g_free(message);
305 void
306 oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
308 OscarData *od;
309 PurpleAccount *account;
310 PurpleGroup *g = NULL;
311 struct buddyinfo *bi = NULL;
312 char *tmp;
313 const char *bname = NULL, *gname = NULL;
315 od = purple_connection_get_protocol_data(gc);
316 account = purple_connection_get_account(gc);
318 if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
319 return;
321 if (userinfo == NULL)
322 userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
324 if (b == NULL)
325 b = purple_blist_find_buddy(account, userinfo->bn);
327 if (b != NULL) {
328 bname = purple_buddy_get_name(b);
329 g = purple_buddy_get_group(b);
330 gname = purple_group_get_name(g);
333 if (userinfo != NULL)
334 bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
336 if ((bi != NULL) && (bi->ipaddr != 0)) {
337 char tmp2[40];
338 sprintf(tmp2, "%u.%u.%u.%u",
339 0xFF & ((bi->ipaddr & 0xff000000) >> 24),
340 0xFF & ((bi->ipaddr & 0x00ff0000) >> 16),
341 0xFF & ((bi->ipaddr & 0x0000ff00) >> 8),
342 0xFF & (bi->ipaddr & 0x000000ff));
343 purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), tmp2);
346 if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
347 char tmp2[12];
348 sprintf(tmp2, "%d", (int)(userinfo->warnlevel/10.0 + .5));
349 purple_notify_user_info_add_pair_plaintext(user_info, _("Warning Level"), tmp2);
352 if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) {
353 tmp = aim_ssi_getcomment(&od->ssi.local, gname, bname);
354 if (tmp != NULL) {
355 char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
356 g_free(tmp);
358 oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2);
359 g_free(tmp2);
364 void
365 oscar_user_info_display_error(OscarData *od, guint16 error_reason, gchar *buddy)
367 PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
368 gchar *buf = g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason));
369 purple_notify_user_info_add_pair_plaintext(user_info, NULL, buf);
370 purple_notify_userinfo(od->gc, buddy, user_info, NULL, NULL);
371 purple_notify_user_info_destroy(user_info);
372 if (!purple_conversation_present_error(buddy,
373 purple_connection_get_account(od->gc), buf))
375 purple_notify_error(od->gc, NULL, buf, NULL,
376 purple_request_cpar_from_connection(od->gc));
378 g_free(buf);
381 void
382 oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
384 PurpleConnection *gc = od->gc;
385 PurpleAccount *account = purple_connection_get_account(gc);
386 PurpleBuddy *buddy;
387 struct buddyinfo *bi;
388 gchar who[16];
389 PurpleNotifyUserInfo *user_info;
391 if (!info->uin)
392 return;
394 user_info = purple_notify_user_info_new();
396 g_snprintf(who, sizeof(who), "%u", info->uin);
397 buddy = purple_blist_find_buddy(account, who);
398 if (buddy != NULL)
399 bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy)));
400 else
401 bi = NULL;
403 purple_notify_user_info_add_pair_plaintext(user_info, _("UIN"), who);
404 oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
405 if ((bi != NULL) && (bi->ipaddr != 0)) {
406 char tstr[40];
407 sprintf(tstr, "%u.%u.%u.%u",
408 0xFF & ((bi->ipaddr & 0xff000000) >> 24),
409 0xFF & ((bi->ipaddr & 0x00ff0000) >> 16),
410 0xFF & ((bi->ipaddr & 0x0000ff00) >> 8),
411 0xFF & (bi->ipaddr & 0x000000ff));
412 purple_notify_user_info_add_pair_plaintext(user_info, _("IP Address"), tstr);
414 oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first);
415 oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last);
416 oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email, "mailto:");
417 if (info->numaddresses && info->email2) {
418 int i;
419 for (i = 0; i < info->numaddresses; i++) {
420 oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Email Address"), info->email2[i], "mailto:");
423 oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile);
425 if (info->gender != 0)
426 purple_notify_user_info_add_pair_plaintext(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
428 if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
429 /* Initialize the struct properly or strftime() will crash
430 * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
431 time_t t = time(NULL);
432 struct tm *tm = localtime(&t);
434 tm->tm_mday = (int)info->birthday;
435 tm->tm_mon = (int)info->birthmonth - 1;
436 tm->tm_year = (int)info->birthyear - 1900;
438 /* Ignore dst setting of today to avoid timezone shift between
439 * dates in summer and winter time. */
440 tm->tm_isdst = -1;
442 /* To be 100% sure that the fields are re-normalized.
443 * If you're sure strftime() ALWAYS does this EVERYWHERE,
444 * feel free to remove it. --rlaager */
445 mktime(tm);
447 oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
449 if ((info->age > 0) && (info->age < 255)) {
450 char age[5];
451 snprintf(age, sizeof(age), "%hhd", info->age);
452 purple_notify_user_info_add_pair_plaintext(user_info, _("Age"), age);
454 /* TODO: Is it correct to pass info->email here...? */
455 oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Personal Web Page"), info->email, "");
456 if (buddy != NULL)
457 oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* use_html_status */ TRUE);
459 oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info);
460 purple_notify_user_info_add_section_break(user_info);
462 if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
463 purple_notify_user_info_add_section_header(user_info, _("Home Address"));
465 oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr);
466 oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity);
467 oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate);
468 oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip);
470 if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
471 purple_notify_user_info_add_section_header(user_info, _("Work Address"));
473 oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr);
474 oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity);
475 oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate);
476 oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip);
478 if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
479 purple_notify_user_info_add_section_header(user_info, _("Work Information"));
481 oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany);
482 oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision);
483 oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition);
484 /* TODO: Is it correct to pass info->email here...? */
485 oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Web Page"), info->email, "");
488 purple_notify_userinfo(gc, who, user_info, NULL, NULL);
489 purple_notify_user_info_destroy(user_info);
492 void
493 oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
495 PurpleConnection *gc = od->gc;
496 PurpleAccount *account = purple_connection_get_account(gc);
497 PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
498 gchar *tmp = NULL, *info_utf8 = NULL, *base_profile_url = NULL;
500 oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* use_html_status */ TRUE);
502 if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) {
503 tmp = purple_str_seconds_to_string(userinfo->idletime*60);
504 purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), tmp);
505 g_free(tmp);
508 oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo);
510 if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !oscar_util_valid_name_sms(userinfo->bn)) {
511 /* An SMS contact is always online; its Online Since value is not useful */
512 time_t t = userinfo->onlinesince;
513 purple_notify_user_info_add_pair_plaintext(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
516 if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
517 time_t t = userinfo->membersince;
518 purple_notify_user_info_add_pair_plaintext(user_info, _("Member Since"), purple_date_format_full(localtime(&t)));
521 if (userinfo->capabilities != 0) {
522 tmp = oscar_caps_to_string(userinfo->capabilities);
523 if (tmp && *tmp)
524 purple_notify_user_info_add_pair_plaintext(user_info, _("Capabilities"), tmp);
525 g_free(tmp);
528 /* Info */
529 if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
530 info_utf8 = oscar_encoding_to_utf8(userinfo->info_encoding, userinfo->info, userinfo->info_len);
531 tmp = oscar_util_format_string(info_utf8, purple_account_get_username(account));
532 purple_notify_user_info_add_section_break(user_info);
533 if (tmp && *tmp) {
534 /* TODO: Check whether it's correct to call add_pair_html,
535 or if we should be using add_pair_plaintext */
536 purple_notify_user_info_add_pair_html(user_info, _("Profile"), tmp);
538 g_free(tmp);
539 g_free(info_utf8);
542 purple_notify_user_info_add_section_break(user_info);
543 base_profile_url = oscar_util_valid_name_icq(userinfo->bn) ? "http://www.icq.com/people" : "http://profiles.aim.com";
544 tmp = g_strdup_printf("<a href=\"%s/%s\">%s</a>",
545 base_profile_url, purple_normalize(account, userinfo->bn), _("View web profile"));
546 purple_notify_user_info_add_pair_html(user_info, NULL, tmp);
547 g_free(tmp);
549 purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
550 purple_notify_user_info_destroy(user_info);