comparison libpurple/protocols/oscar/userinfo.c @ 30361:9881f18b95b1

Got rid of family_icq.c -> oscar.c callbacks. Now it will be possible to add an error handler for SNAC_FAMILY_ICQ right inside family_icq.c, since all the necessary functions are there. I made a pretty large refactoring along the way, moving the authorization- and userinfo-related functions to separate files and renaming some of them. Hopefully, this will make oscar.c less of mess.
author ivan.komarov@soc.pidgin.im
date Sun, 30 May 2010 13:53:45 +0000
parents
children 879baaf87aa2
comparison
equal deleted inserted replaced
30360:2f25002c9464 30361:9881f18b95b1
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.
5 *
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.
10 *
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.
15 *
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
19 */
20
21 /*
22 * Displaying various information about buddies.
23 */
24
25 #include "oscar.h"
26
27 static gchar *
28 oscar_caps_to_string(guint64 caps)
29 {
30 GString *str;
31 const gchar *tmp;
32 guint64 bit = 1;
33
34 str = g_string_new("");
35
36 if (!caps) {
37 return NULL;
38 } else while (bit <= OSCAR_CAPABILITY_LAST) {
39 if (bit & caps) {
40 switch (bit) {
41 case OSCAR_CAPABILITY_BUDDYICON:
42 tmp = _("Buddy Icon");
43 break;
44 case OSCAR_CAPABILITY_TALK:
45 tmp = _("Voice");
46 break;
47 case OSCAR_CAPABILITY_DIRECTIM:
48 tmp = _("AIM Direct IM");
49 break;
50 case OSCAR_CAPABILITY_CHAT:
51 tmp = _("Chat");
52 break;
53 case OSCAR_CAPABILITY_GETFILE:
54 tmp = _("Get File");
55 break;
56 case OSCAR_CAPABILITY_SENDFILE:
57 tmp = _("Send File");
58 break;
59 case OSCAR_CAPABILITY_GAMES:
60 case OSCAR_CAPABILITY_GAMES2:
61 tmp = _("Games");
62 break;
63 case OSCAR_CAPABILITY_XTRAZ:
64 case OSCAR_CAPABILITY_NEWCAPS:
65 tmp = _("ICQ Xtraz");
66 break;
67 case OSCAR_CAPABILITY_ADDINS:
68 tmp = _("Add-Ins");
69 break;
70 case OSCAR_CAPABILITY_SENDBUDDYLIST:
71 tmp = _("Send Buddy List");
72 break;
73 case OSCAR_CAPABILITY_ICQ_DIRECT:
74 tmp = _("ICQ Direct Connect");
75 break;
76 case OSCAR_CAPABILITY_APINFO:
77 tmp = _("AP User");
78 break;
79 case OSCAR_CAPABILITY_ICQRTF:
80 tmp = _("ICQ RTF");
81 break;
82 case OSCAR_CAPABILITY_EMPTY:
83 tmp = _("Nihilist");
84 break;
85 case OSCAR_CAPABILITY_ICQSERVERRELAY:
86 tmp = _("ICQ Server Relay");
87 break;
88 case OSCAR_CAPABILITY_UNICODEOLD:
89 tmp = _("Old ICQ UTF8");
90 break;
91 case OSCAR_CAPABILITY_TRILLIANCRYPT:
92 tmp = _("Trillian Encryption");
93 break;
94 case OSCAR_CAPABILITY_UNICODE:
95 tmp = _("ICQ UTF8");
96 break;
97 case OSCAR_CAPABILITY_HIPTOP:
98 tmp = _("Hiptop");
99 break;
100 case OSCAR_CAPABILITY_SECUREIM:
101 tmp = _("Security Enabled");
102 break;
103 case OSCAR_CAPABILITY_VIDEO:
104 tmp = _("Video Chat");
105 break;
106 /* Not actually sure about this one... WinAIM doesn't show anything */
107 case OSCAR_CAPABILITY_ICHATAV:
108 tmp = _("iChat AV");
109 break;
110 case OSCAR_CAPABILITY_LIVEVIDEO:
111 tmp = _("Live Video");
112 break;
113 case OSCAR_CAPABILITY_CAMERA:
114 tmp = _("Camera");
115 break;
116 case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
117 tmp = _("Screen Sharing");
118 break;
119 default:
120 tmp = NULL;
121 break;
122 }
123 if (tmp)
124 g_string_append_printf(str, "%s%s", (*(str->str) == '\0' ? "" : ", "), tmp);
125 }
126 bit <<= 1;
127 }
128
129 return g_string_free(str, FALSE);
130 }
131
132 static void
133 oscar_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *name, const char *value)
134 {
135 if (value && value[0]) {
136 purple_notify_user_info_add_pair(user_info, name, value);
137 }
138 }
139
140 static void
141 oscar_user_info_convert_and_add(PurpleAccount *account, OscarData *od, PurpleNotifyUserInfo *user_info,
142 const char *name, const char *value)
143 {
144 gchar *utf8;
145
146 if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, od, value))) {
147 purple_notify_user_info_add_pair(user_info, name, utf8);
148 g_free(utf8);
149 }
150 }
151
152 /**
153 * @brief Append the status information to a user_info struct
154 *
155 * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
156 *
157 * @param gc The PurpleConnection
158 * @param user_info A PurpleNotifyUserInfo object to which status information will be added
159 * @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().
160 * @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().
161 * @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.
162 */
163 void
164 oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags)
165 {
166 PurpleAccount *account = purple_connection_get_account(gc);
167 OscarData *od;
168 PurplePresence *presence = NULL;
169 PurpleStatus *status = NULL;
170 gchar *message = NULL, *itmsurl = NULL, *tmp;
171 gboolean is_away;
172
173 od = purple_connection_get_protocol_data(gc);
174
175 if (b == NULL && userinfo == NULL)
176 return;
177
178 if (b == NULL)
179 b = purple_find_buddy(purple_connection_get_account(gc), userinfo->bn);
180 else
181 userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
182
183 if (b) {
184 presence = purple_buddy_get_presence(b);
185 status = purple_presence_get_active_status(presence);
186 }
187
188 /* If we have both b and userinfo we favor userinfo, because if we're
189 viewing someone's profile then we want the HTML away message, and
190 the "message" attribute of the status contains only the plaintext
191 message. */
192 if (userinfo) {
193 if ((userinfo->flags & AIM_FLAG_AWAY)
194 && userinfo->away_len > 0
195 && userinfo->away != NULL
196 && userinfo->away_encoding != NULL)
197 {
198 /* Away message */
199 tmp = oscar_encoding_extract(userinfo->away_encoding);
200 message = oscar_encoding_to_utf8(account,
201 tmp, userinfo->away, userinfo->away_len);
202 g_free(tmp);
203 } else {
204 /*
205 * Available message or non-HTML away message (because that's
206 * all we have right now.
207 */
208 if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
209 message = oscar_encoding_to_utf8(account,
210 userinfo->status_encoding, userinfo->status,
211 userinfo->status_len);
212 }
213 #if defined (_WIN32) || defined (__APPLE__)
214 if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0'))
215 itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
216 userinfo->itmsurl, userinfo->itmsurl_len);
217 #endif
218 }
219 } else {
220 message = g_strdup(purple_status_get_attr_string(status, "message"));
221 itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
222 }
223
224 is_away = ((status && !purple_status_is_available(status)) ||
225 (userinfo && (userinfo->flags & AIM_FLAG_AWAY)));
226
227 if (strip_html_tags) {
228 /* Away messages are HTML, but available messages were originally plain text.
229 * We therefore need to strip away messages but not available messages if we're asked to remove HTML tags.
230 */
231 /*
232 * It seems like the above comment no longer applies. All messages need
233 * to be escaped.
234 */
235 if (message) {
236 gchar *tmp2;
237 tmp = purple_markup_strip_html(message);
238 g_free(message);
239 tmp2 = g_markup_escape_text(tmp, -1);
240 g_free(tmp);
241 message = tmp2;
242 }
243
244 } else {
245 if (itmsurl) {
246 tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
247 itmsurl, message);
248 g_free(message);
249 message = tmp;
250 }
251 }
252 g_free(itmsurl);
253
254 if (message) {
255 tmp = oscar_util_format_string(message, purple_account_get_username(account));
256 g_free(message);
257 message = tmp;
258 }
259
260 if (b) {
261 if (purple_presence_is_online(presence)) {
262 if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) {
263 /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
264 * If the status name and the message are the same, only show one. */
265 const char *status_name = purple_status_get_name(status);
266 if (status_name && message && !strcmp(status_name, message))
267 status_name = NULL;
268
269 tmp = g_strdup_printf("%s%s%s",
270 status_name ? status_name : "",
271 ((status_name && message) && *message) ? ": " : "",
272 (message && *message) ? message : "");
273 g_free(message);
274 message = tmp;
275 }
276
277 } else if (aim_ssi_waitingforauth(od->ssi.local,
278 aim_ssi_itemlist_findparentname(od->ssi.local, purple_buddy_get_name(b)),
279 purple_buddy_get_name(b)))
280 {
281 /* Note if an offline buddy is not authorized */
282 tmp = g_strdup_printf("%s%s%s",
283 _("Not Authorized"),
284 (message && *message) ? ": " : "",
285 (message && *message) ? message : "");
286 g_free(message);
287 message = tmp;
288 } else {
289 g_free(message);
290 message = g_strdup(_("Offline"));
291 }
292 }
293
294 if (presence) {
295 const char *mood;
296 const char *description;
297 status = purple_presence_get_status(presence, "mood");
298 mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
299 description = icq_get_custom_icon_description(mood);
300 if (description && *description)
301 purple_notify_user_info_add_pair(user_info, _("Mood"), _(description));
302 }
303
304 purple_notify_user_info_add_pair(user_info, _("Status"), message);
305 g_free(message);
306 }
307
308 void
309 oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
310 {
311 OscarData *od;
312 PurpleAccount *account;
313 PurplePresence *presence = NULL;
314 PurpleStatus *status = NULL;
315 PurpleGroup *g = NULL;
316 struct buddyinfo *bi = NULL;
317 char *tmp;
318 const char *bname = NULL, *gname = NULL;
319
320 od = purple_connection_get_protocol_data(gc);
321 account = purple_connection_get_account(gc);
322
323 if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
324 return;
325
326 if (userinfo == NULL)
327 userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
328
329 if (b == NULL)
330 b = purple_find_buddy(account, userinfo->bn);
331
332 if (b != NULL) {
333 bname = purple_buddy_get_name(b);
334 g = purple_buddy_get_group(b);
335 gname = purple_group_get_name(g);
336 presence = purple_buddy_get_presence(b);
337 status = purple_presence_get_active_status(presence);
338 }
339
340 if (userinfo != NULL)
341 bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
342
343 if ((bi != NULL) && (bi->ipaddr != 0)) {
344 tmp = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
345 (bi->ipaddr & 0xff000000) >> 24,
346 (bi->ipaddr & 0x00ff0000) >> 16,
347 (bi->ipaddr & 0x0000ff00) >> 8,
348 (bi->ipaddr & 0x000000ff));
349 oscar_user_info_add_pair(user_info, _("IP Address"), tmp);
350 g_free(tmp);
351 }
352
353 if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
354 tmp = g_strdup_printf("%d", (int)(userinfo->warnlevel/10.0 + .5));
355 oscar_user_info_add_pair(user_info, _("Warning Level"), tmp);
356 g_free(tmp);
357 }
358
359 if ((b != NULL) && (bname != NULL) && (g != NULL) && (gname != NULL)) {
360 tmp = aim_ssi_getcomment(od->ssi.local, gname, bname);
361 if (tmp != NULL) {
362 char *tmp2 = g_markup_escape_text(tmp, strlen(tmp));
363 g_free(tmp);
364
365 oscar_user_info_convert_and_add(account, od, user_info, _("Buddy Comment"), tmp2);
366 g_free(tmp2);
367 }
368 }
369 }
370
371 void
372 oscar_user_info_display_error(OscarData *od, guint16 error_reason, char *buddy)
373 {
374 PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
375 gchar *buf = g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason));
376 purple_notify_user_info_add_pair(user_info, NULL, buf);
377 purple_notify_userinfo(od->gc, buddy, user_info, NULL, NULL);
378 purple_notify_user_info_destroy(user_info);
379 purple_conv_present_error(buddy, purple_connection_get_account(od->gc), buf);
380 g_free(buf);
381 }
382
383 void
384 oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info)
385 {
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;
392 gchar *utf8;
393 gchar *buf;
394 const gchar *alias;
395
396 if (!info->uin)
397 return;
398
399 user_info = purple_notify_user_info_new();
400
401 g_snprintf(who, sizeof(who), "%u", info->uin);
402 buddy = purple_find_buddy(account, who);
403 if (buddy != NULL)
404 bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, purple_buddy_get_name(buddy)));
405 else
406 bi = NULL;
407
408 purple_notify_user_info_add_pair(user_info, _("UIN"), who);
409 oscar_user_info_convert_and_add(account, od, user_info, _("Nick"), info->nick);
410 if ((bi != NULL) && (bi->ipaddr != 0)) {
411 char *tstr = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
412 (bi->ipaddr & 0xff000000) >> 24,
413 (bi->ipaddr & 0x00ff0000) >> 16,
414 (bi->ipaddr & 0x0000ff00) >> 8,
415 (bi->ipaddr & 0x000000ff));
416 purple_notify_user_info_add_pair(user_info, _("IP Address"), tstr);
417 g_free(tstr);
418 }
419 oscar_user_info_convert_and_add(account, od, user_info, _("First Name"), info->first);
420 oscar_user_info_convert_and_add(account, od, user_info, _("Last Name"), info->last);
421 if (info->email && info->email[0] && (utf8 = oscar_utf8_try_convert(account, od, info->email))) {
422 buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
423 purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
424 g_free(buf);
425 g_free(utf8);
426 }
427 if (info->numaddresses && info->email2) {
428 int i;
429 for (i = 0; i < info->numaddresses; i++) {
430 if (info->email2[i] && info->email2[i][0] && (utf8 = oscar_utf8_try_convert(account, od, info->email2[i]))) {
431 buf = g_strdup_printf("<a href=\"mailto:%s\">%s</a>", utf8, utf8);
432 purple_notify_user_info_add_pair(user_info, _("Email Address"), buf);
433 g_free(buf);
434 g_free(utf8);
435 }
436 }
437 }
438 oscar_user_info_convert_and_add(account, od, user_info, _("Mobile Phone"), info->mobile);
439
440 if (info->gender != 0)
441 purple_notify_user_info_add_pair(user_info, _("Gender"), (info->gender == 1 ? _("Female") : _("Male")));
442
443 if ((info->birthyear > 1900) && (info->birthmonth > 0) && (info->birthday > 0)) {
444 /* Initialize the struct properly or strftime() will crash
445 * under some conditions (e.g. Debian sarge w/ LANG=en_HK). */
446 time_t t = time(NULL);
447 struct tm *tm = localtime(&t);
448
449 tm->tm_mday = (int)info->birthday;
450 tm->tm_mon = (int)info->birthmonth - 1;
451 tm->tm_year = (int)info->birthyear - 1900;
452
453 /* To be 100% sure that the fields are re-normalized.
454 * If you're sure strftime() ALWAYS does this EVERYWHERE,
455 * feel free to remove it. --rlaager */
456 mktime(tm);
457
458 oscar_user_info_convert_and_add(account, od, user_info, _("Birthday"), purple_date_format_short(tm));
459 }
460 if ((info->age > 0) && (info->age < 255)) {
461 char age[5];
462 snprintf(age, sizeof(age), "%hhd", info->age);
463 purple_notify_user_info_add_pair(user_info, _("Age"), age);
464 }
465 if (info->personalwebpage && info->personalwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->personalwebpage))) {
466 buf = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
467 purple_notify_user_info_add_pair(user_info, _("Personal Web Page"), buf);
468 g_free(buf);
469 g_free(utf8);
470 }
471
472 if (buddy != NULL)
473 oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* strip_html_tags */ FALSE);
474
475 oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info);
476 purple_notify_user_info_add_section_break(user_info);
477
478 if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
479 purple_notify_user_info_add_section_header(user_info, _("Home Address"));
480
481 oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->homeaddr);
482 oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->homecity);
483 oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->homestate);
484 oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->homezip);
485 }
486 if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
487 purple_notify_user_info_add_section_header(user_info, _("Work Address"));
488
489 oscar_user_info_convert_and_add(account, od, user_info, _("Address"), info->workaddr);
490 oscar_user_info_convert_and_add(account, od, user_info, _("City"), info->workcity);
491 oscar_user_info_convert_and_add(account, od, user_info, _("State"), info->workstate);
492 oscar_user_info_convert_and_add(account, od, user_info, _("Zip Code"), info->workzip);
493 }
494 if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
495 purple_notify_user_info_add_section_header(user_info, _("Work Information"));
496
497 oscar_user_info_convert_and_add(account, od, user_info, _("Company"), info->workcompany);
498 oscar_user_info_convert_and_add(account, od, user_info, _("Division"), info->workdivision);
499 oscar_user_info_convert_and_add(account, od, user_info, _("Position"), info->workposition);
500
501 if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(account, od, info->workwebpage))) {
502 char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
503 purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage);
504 g_free(webpage);
505 g_free(utf8);
506 }
507 }
508
509 if (buddy != NULL)
510 alias = purple_buddy_get_alias(buddy);
511 else
512 alias = who;
513 purple_notify_userinfo(gc, who, user_info, NULL, NULL);
514 purple_notify_user_info_destroy(user_info);
515 }
516
517 void
518 oscar_user_info_display_aim(OscarData *od, aim_userinfo_t *userinfo)
519 {
520 PurpleConnection *gc = od->gc;
521 PurpleAccount *account = purple_connection_get_account(gc);
522 PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
523 gchar *tmp = NULL, *info_utf8 = NULL, *base_profile_url = NULL;
524
525 oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* strip_html_tags */ FALSE);
526
527 if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) {
528 tmp = purple_str_seconds_to_string(userinfo->idletime*60);
529 oscar_user_info_add_pair(user_info, _("Idle"), tmp);
530 g_free(tmp);
531 }
532
533 oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo);
534
535 if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !oscar_util_valid_name_sms(userinfo->bn)) {
536 /* An SMS contact is always online; its Online Since value is not useful */
537 time_t t = userinfo->onlinesince;
538 oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
539 }
540
541 if (userinfo->present & AIM_USERINFO_PRESENT_MEMBERSINCE) {
542 time_t t = userinfo->membersince;
543 oscar_user_info_add_pair(user_info, _("Member Since"), purple_date_format_full(localtime(&t)));
544 }
545
546 if (userinfo->capabilities != 0) {
547 tmp = oscar_caps_to_string(userinfo->capabilities);
548 oscar_user_info_add_pair(user_info, _("Capabilities"), tmp);
549 g_free(tmp);
550 }
551
552 /* Info */
553 if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
554 tmp = oscar_encoding_extract(userinfo->info_encoding);
555 info_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->info,
556 userinfo->info_len);
557 g_free(tmp);
558 if (info_utf8 != NULL) {
559 tmp = oscar_util_format_string(info_utf8, purple_account_get_username(account));
560 purple_notify_user_info_add_section_break(user_info);
561 oscar_user_info_add_pair(user_info, _("Profile"), tmp);
562 g_free(tmp);
563 g_free(info_utf8);
564 }
565 }
566
567 purple_notify_user_info_add_section_break(user_info);
568 base_profile_url = oscar_util_valid_name_icq(userinfo->bn) ? "http://www.icq.com/people" : "http://profiles.aim.com";
569 tmp = g_strdup_printf("<a href=\"%s/%s\">%s</a>",
570 base_profile_url, purple_normalize(account, userinfo->bn), _("View web profile"));
571 purple_notify_user_info_add_pair(user_info, NULL, tmp);
572 g_free(tmp);
573
574 purple_notify_userinfo(gc, userinfo->bn, user_info, NULL, NULL);
575 purple_notify_user_info_destroy(user_info);
576 }