rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / oscar / userinfo.c
blobab537d24b5138acb2e416e5a1b31c89dfc467c5b
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 if (!caps) {
36 return NULL;
39 str = g_string_new("");
40 while (bit <= OSCAR_CAPABILITY_LAST) {
41 if (bit & caps) {
42 switch (bit) {
43 case OSCAR_CAPABILITY_BUDDYICON:
44 tmp = _("Buddy Icon");
45 break;
46 case OSCAR_CAPABILITY_TALK:
47 tmp = _("Voice");
48 break;
49 case OSCAR_CAPABILITY_DIRECTIM:
50 tmp = _("AIM Direct IM");
51 break;
52 case OSCAR_CAPABILITY_CHAT:
53 tmp = _("Chat");
54 break;
55 case OSCAR_CAPABILITY_GETFILE:
56 tmp = _("Get File");
57 break;
58 case OSCAR_CAPABILITY_SENDFILE:
59 tmp = _("Send File");
60 break;
61 case OSCAR_CAPABILITY_GAMES:
62 case OSCAR_CAPABILITY_GAMES2:
63 tmp = _("Games");
64 break;
65 case OSCAR_CAPABILITY_XTRAZ:
66 case OSCAR_CAPABILITY_NEWCAPS:
67 tmp = _("ICQ Xtraz");
68 break;
69 case OSCAR_CAPABILITY_ADDINS:
70 tmp = _("Add-Ins");
71 break;
72 case OSCAR_CAPABILITY_SENDBUDDYLIST:
73 tmp = _("Send Buddy List");
74 break;
75 case OSCAR_CAPABILITY_ICQ_DIRECT:
76 tmp = _("ICQ Direct Connect");
77 break;
78 case OSCAR_CAPABILITY_APINFO:
79 tmp = _("AP User");
80 break;
81 case OSCAR_CAPABILITY_ICQRTF:
82 tmp = _("ICQ RTF");
83 break;
84 case OSCAR_CAPABILITY_EMPTY:
85 tmp = _("Nihilist");
86 break;
87 case OSCAR_CAPABILITY_ICQSERVERRELAY:
88 tmp = _("ICQ Server Relay");
89 break;
90 case OSCAR_CAPABILITY_UNICODEOLD:
91 tmp = _("Old ICQ UTF8");
92 break;
93 case OSCAR_CAPABILITY_TRILLIANCRYPT:
94 tmp = _("Trillian Encryption");
95 break;
96 case OSCAR_CAPABILITY_UNICODE:
97 tmp = _("ICQ UTF8");
98 break;
99 case OSCAR_CAPABILITY_HIPTOP:
100 tmp = _("Hiptop");
101 break;
102 case OSCAR_CAPABILITY_SECUREIM:
103 tmp = _("Security Enabled");
104 break;
105 case OSCAR_CAPABILITY_VIDEO:
106 tmp = _("Video Chat");
107 break;
108 /* Not actually sure about this one... WinAIM doesn't show anything */
109 case OSCAR_CAPABILITY_ICHATAV:
110 tmp = _("iChat AV");
111 break;
112 case OSCAR_CAPABILITY_LIVEVIDEO:
113 tmp = _("Live Video");
114 break;
115 case OSCAR_CAPABILITY_CAMERA:
116 tmp = _("Camera");
117 break;
118 case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
119 tmp = _("Screen Sharing");
120 break;
121 default:
122 tmp = NULL;
123 break;
125 if (tmp)
126 g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
128 bit <<= 1;
131 return g_string_free(str, FALSE);
134 static void
135 oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
136 const char *name, const char *value)
138 gchar *utf8;
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);
145 g_free(utf8);
149 static void
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)
153 gchar *utf8;
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);
161 g_free(utf8);
162 g_free(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.
177 void
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);
181 OscarData *od;
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)
190 return;
192 if (b == NULL)
193 b = purple_blist_find_buddy(purple_connection_get_account(gc), userinfo->bn);
194 else
195 userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
197 if (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
205 message. */
206 if (userinfo) {
207 if ((userinfo->flags & AIM_FLAG_AWAY) && use_html_status && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) {
208 /* Away message */
209 message = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len);
210 escaping_needed = FALSE;
211 } else {
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);
223 #endif
225 } else {
226 message = g_strdup(purple_status_get_attr_string(status, "message"));
227 itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
230 if (message) {
231 tmp = oscar_util_format_string(message, purple_account_get_username(account));
232 g_free(message);
233 message = tmp;
234 if (escaping_needed) {
235 tmp = purple_markup_escape_text(message, -1);
236 g_free(message);
237 message = tmp;
241 if (use_html_status && itmsurl) {
242 tmp = g_strdup_printf("<a href=\"%s\">%s</a>", itmsurl, message);
243 g_free(message);
244 message = tmp;
247 if (b) {
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))
255 status_name = NULL;
257 tmp = g_strdup_printf("%s%s%s",
258 status_name ? status_name : "",
259 ((status_name && message) && *message) ? ": " : "",
260 (message && *message) ? message : "");
261 g_free(message);
262 message = tmp;
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",
271 _("Not Authorized"),
272 (message && *message) ? ": " : "",
273 (message && *message) ? message : "");
274 g_free(message);
275 message = tmp;
276 } else {
277 g_free(message);
278 message = g_strdup(_("Offline"));
282 if (presence) {
283 const char *mood;
284 const char *comment;
285 char *description;
286 status = purple_presence_get_status(presence, "mood");
287 mood = icq_get_custom_icon_description(purple_status_get_attr_string(status, PURPLE_MOOD_NAME));
288 if (mood) {
289 comment = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT);
290 if (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);
294 } else {
295 description = g_strdup(_(mood));
297 purple_notify_user_info_add_pair_html(user_info, _("Mood"), description);
298 g_free(description);
302 purple_notify_user_info_add_pair_html(user_info, _("Status"), message);
303 g_free(message);
304 g_free(itmsurl);
307 void
308 oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
310 OscarData *od;
311 PurpleAccount *account;
312 PurpleGroup *g = NULL;
313 struct buddyinfo *bi = NULL;
314 char *tmp;
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)))
321 return;
323 if (userinfo == NULL)
324 userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
326 if (b == NULL)
327 b = purple_blist_find_buddy(account, userinfo->bn);
329 if (b != NULL) {
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)) {
339 char tmp2[40];
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)) {
349 char tmp2[12];
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);
356 if (tmp != NULL) {
357 char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
358 g_free(tmp);
360 oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2);
361 g_free(tmp2);
366 void
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));
380 g_free(buf);
383 void
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);
388 PurpleBuddy *buddy;
389 struct buddyinfo *bi;
390 gchar who[16];
391 PurpleNotifyUserInfo *user_info;
393 if (!info->uin)
394 return;
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);
400 if (buddy != NULL)
401 bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy)));
402 else
403 bi = NULL;
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)) {
408 char tstr[40];
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) {
420 int i;
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. */
442 tm->tm_isdst = -1;
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 */
447 mktime(tm);
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)) {
452 char age[5];
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, "");
458 if (buddy != NULL)
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);
494 void
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);
507 g_free(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);
525 if (tmp && *tmp)
526 purple_notify_user_info_add_pair_plaintext(user_info, _("Capabilities"), tmp);
527 g_free(tmp);
530 /* Info */
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);
535 if (tmp && *tmp) {
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);
540 g_free(tmp);
541 g_free(info_utf8);
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);
549 g_free(tmp);
551 purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
552 purple_notify_user_info_destroy(user_info);