# HG changeset patch # User Tim Ringenbach # Date 1086745256 0 # Node ID fb517adf49725a31872327fcb437cf09d6e7dc4a # Parent 5e9018c81bd926b9c0fc48d3be927ca08f63aba2 [gaim-migrate @ 10041] wing improved yahoo profile support some more, and now even profile images show up in the get info dialog. Good work wing! committer: Tailor Script diff -r 5e9018c81bd9 -r fb517adf4972 src/protocols/yahoo/yahoo_profile.c --- a/src/protocols/yahoo/yahoo_profile.c Wed Jun 09 01:34:16 2004 +0000 +++ b/src/protocols/yahoo/yahoo_profile.c Wed Jun 09 01:40:56 2004 +0000 @@ -21,10 +21,15 @@ * */ +#define PHOTO_SUPPORT 1 + #include "internal.h" #include "debug.h" #include "notify.h" #include "util.h" +#if PHOTO_SUPPORT +#include "imgstore.h" +#endif #include "yahoo.h" @@ -76,6 +81,19 @@ char *dummy; } profile_strings_node_t; + +typedef struct { + YahooGetInfoData *info_data; + char *url_buffer; + GString *s; + char *photo_url_text; + char *profile_url_text; + char *tooltip_text; + const profile_strings_node_t *strings; + const char *last_updated_string; +} YahooGetInfoStepTwoData; + + /* Strings to determine the profile "language" (more accurately "locale"). * Strings in this list must be in the original charset in the profile. * The "Last Updated" string is used, but sometimes is not sufficient to @@ -613,20 +631,90 @@ return str; } +static char *yahoo_tooltip_info_text(YahooGetInfoData *info_data) { + GString *s = g_string_sized_new(80); /* wild guess */ + GaimBuddy *b; + + g_string_printf(s, _("%s: %s
"), _("Yahoo! ID"), info_data->name); + b = gaim_find_buddy(gaim_connection_get_account(info_data->gc), + info_data->name); + + if (b) { + char *statustext = yahoo_tooltip_text(b); + if(b->alias && b->alias[0]) { + char *aliastext = g_markup_escape_text(b->alias, -1); + g_string_append_printf(s, _("Alias: %s
"), aliastext); + g_free(aliastext); + } + if (b->idle > 0) { + char *idletime = gaim_str_seconds_to_string(time(NULL) - b->idle); + g_string_append_printf(s, _("%s: %s
"), _("Idle"), + idletime); + g_free(idletime); + } + if (statustext) { + g_string_append_printf(s, "%s
", statustext); + g_free(statustext); + } + } + + return g_string_free(s, FALSE); +} + +#if PHOTO_SUPPORT + +static char *yahoo_get_photo_url(const char *url_text, const char *name) { + GString *s = g_string_sized_new(strlen(name) + 8); + char *p; + char *it = NULL; + + g_string_printf(s, " alt=\"%s\">", name); + p = strstr(url_text, s->str); + + if (p) { + /* Search backwards for "http://". This is stupid, but it works. */ + for (; !it && p > url_text; p -= 1) { + if (strncmp(p, "\"http://", 8) == 0) { + char *q; + p += 1; /* skip only the " */ + q = strchr(p, '"'); + if (q) { + it = g_strndup(p, q - p); + } + } + } + } + + g_string_free(s, TRUE); + return it; +} + +static void yahoo_got_photo(void *data, const char *url_text, size_t len); + +#endif /* PHOTO_SUPPORT */ + static void yahoo_got_info(void *data, const char *url_text, size_t len) { YahooGetInfoData *info_data = (YahooGetInfoData *)data; - char *stripped, *p; + char *p; char buf[1024]; +#if PHOTO_SUPPORT + YahooGetInfoStepTwoData *info2_data; + char *photo_url_text = NULL; +#else gboolean found = FALSE; + char *stripped; + int stripped_len; + char *last_updated_utf8_string = NULL; +#endif + const char *last_updated_string = NULL; char *url_buffer; GString *s; - int stripped_len; - const char *last_updated_string = NULL; - char *last_updated_utf8_string; + char *tooltip_text = NULL; + char *profile_url_text = NULL; int lang, strid; - GaimBuddy *b; struct yahoo_data *yd; + const profile_strings_node_t *strings = NULL; if (!GAIM_CONNECTION_IS_VALID(info_data->gc)) { g_free(info_data->name); @@ -637,34 +725,60 @@ gaim_debug_info("yahoo", "In yahoo_got_info\n"); yd = info_data->gc->proto_data; + + /* Get the tooltip info string */ + tooltip_text = yahoo_tooltip_info_text(info_data); - /* we failed to grab the profile URL. this should never happen */ + /* We failed to grab the profile URL. This is not expected to actually + * happen except under unusual error conditions, as Yahoo is observed + * to send back HTML, with a 200 status code. + */ if (url_text == NULL || strcmp(url_text, "") == 0) { + g_snprintf(buf, 1024, "%s%s", + tooltip_text, _("Error retrieving profile")); + gaim_notify_formatted(info_data->gc, NULL, _("Buddy Information"), NULL, - _("Error retrieving profile"), - NULL, NULL); + buf, NULL, NULL); + g_free(profile_url_text); + g_free(tooltip_text); g_free(info_data->name); g_free(info_data); return; } - /* we don't yet support the multiple link level of the warning page for + /* Construct the correct profile URL */ + s = g_string_sized_new(80); /* wild guess */ + g_string_printf(s, "%s%s", (yd->jp? YAHOOJP_PROFILE_URL: YAHOO_PROFILE_URL), + info_data->name); + profile_url_text = g_string_free(s, FALSE); + s = NULL; + + /* We don't yet support the multiple link level of the warning page for * 'adult' profiles, not to mention the fact that yahoo wants you to be - * logged in (on the website) to be able to view an 'adult' profile. for + * logged in (on the website) to be able to view an 'adult' profile. For * now, just tell them that we can't help them, and provide a link to the * profile if they want to do the web browser thing. */ p = strstr(url_text, "Adult Profiles Warning Message"); + if (!p) { + p = strstr(url_text, "Adult Content Warning"); /* TITLE element */ + } if (p) { - g_snprintf(buf, 1024, "%s%s%s%s", - _("Sorry, profiles marked as containing adult content are not supported at this time.

\n"), - _("If you wish to view this profile, you will need to visit this link in your web browser
"), - YAHOO_PROFILE_URL, info_data->name, YAHOO_PROFILE_URL, info_data->name); + g_snprintf(buf, 1024, "%s%s

\n" + "%s
%s", + tooltip_text, + _("Sorry, profiles marked as containing adult content " + "are not supported at this time."), + _("If you wish to view this profile, " + "you will need to visit this link in your web browser"), + profile_url_text, profile_url_text); gaim_notify_formatted(info_data->gc, NULL, _("Buddy Information"), NULL, buf, NULL, NULL); + g_free(profile_url_text); + g_free(tooltip_text); g_free(info_data->name); g_free(info_data); return; @@ -684,32 +798,48 @@ for (strid = 0; profile_strings[strid].lang != XX; strid += 1) { if (profile_strings[strid].lang == profile_langs[lang].lang) break; } + strings = profile_strings + strid; gaim_debug_info("yahoo", "detected profile lang = %s (%d)\n", profile_strings[strid].lang_string, lang); } /* Every user may choose his/her own profile language, and this language * has nothing to do with the preferences of the user which looks at the * profile. We try to support all languages, but nothing is guaranteed. + * If we cannot determine the language, it means either (1) the profile + * is written in an unsupported language, (2) our language support is + * out of date, or (3) the user is not found. */ - if (!p || profile_strings[strid].lang == XX) { - if (strstr(url_text, "was not found on this server.") == NULL && strstr(url_text, "Yahoo! Member Directory - User not found") == NULL) { - g_snprintf(buf, 1024, "%s%s%s%s", - _("Sorry, this profile seems to be in a language " - "that is not supported at this time.

\n"), - _("If you wish to view this profile, you will need to visit this link in your web browser
"), - YAHOO_PROFILE_URL, info_data->name, YAHOO_PROFILE_URL, info_data->name); + if (!p || strings->lang == XX) { + if (!strstr(url_text, "Yahoo! Member Directory - User not found") + && !strstr(url_text, "was not found on this server.") + && !strstr(url_text, "\xb8\xf8\xb3\xab\xa5\xd7\xa5\xed\xa5\xd5\xa5\xa3\xa1\xbc\xa5\xeb\xa4\xac\xb8\xab\xa4\xc4\xa4\xab\xa4\xea\xa4\xde\xa4\xbb\xa4\xf3")) { + g_snprintf(buf, 1024, "%s%s

\n" + "%s
%s", + tooltip_text, + _("Sorry, this profile seems to be in a language " + "that is not supported at this time."), + _("If you wish to view this profile, " + "you will need to visit this link in your web browser"), + profile_url_text, profile_url_text); } else { - g_snprintf(buf, 1024, "Error retrieving profile"); + g_snprintf(buf, 1024, "%s%s", + tooltip_text, _("Error retrieving profile")); } gaim_notify_formatted(info_data->gc, NULL, _("Buddy Information"), NULL, buf, NULL, NULL); + g_free(profile_url_text); + g_free(tooltip_text); g_free(info_data->name); g_free(info_data); return; } +#if PHOTO_SUPPORT + photo_url_text = yahoo_get_photo_url(url_text, info_data->name); +#endif + url_buffer = g_strdup(url_text); /* @@ -725,10 +855,59 @@ #endif /* nuke the nasty \r's */ - while ((p = strchr(url_buffer, '\r')) != NULL) { - memmove(p, p + 1, strlen(p + 1)); - url_buffer[strlen(url_buffer) - 1] = '\0'; + gaim_str_strip_cr(url_buffer); + +#if PHOTO_SUPPORT + + /* Marshall the existing state */ + info2_data = g_malloc(sizeof(YahooGetInfoStepTwoData)); + info2_data->info_data = info_data; + info2_data->url_buffer = url_buffer; + info2_data->s = s; + info2_data->photo_url_text = photo_url_text; + info2_data->profile_url_text = profile_url_text; + info2_data->tooltip_text = tooltip_text; + info2_data->strings = strings; + info2_data->last_updated_string = last_updated_string; + + /* Try to put the photo in there too, if there's one */ + if (photo_url_text) { + /* User-uploaded photos use a different server that requires the Host + * header, but Yahoo Japan will use the "chunked" content encoding if + * we specify HTTP 1.1. So we have to specify 1.0 & fix gaim_url_fetch + */ + gaim_url_fetch(photo_url_text, FALSE, NULL, FALSE, yahoo_got_photo, + info2_data); + } else { + /* Emulate a callback */ + yahoo_got_photo(info2_data, NULL, 0); } +} + +static void yahoo_got_photo(void *data, const char *url_text, size_t len) +{ + YahooGetInfoStepTwoData *info2_data = (YahooGetInfoStepTwoData *)data; + gboolean found = FALSE; + int id = -1; + + /* Temporary variables */ + char *p = NULL; + char *stripped; + int stripped_len; + char *last_updated_utf8_string = NULL; + + /* Unmarshall the saved state */ + YahooGetInfoData *info_data = info2_data->info_data; + char *url_buffer = info2_data->url_buffer; + GString *s = info2_data->s; + char *photo_url_text = info2_data->photo_url_text; + char *profile_url_text = info2_data->profile_url_text; + char *tooltip_text = info2_data->tooltip_text; + const profile_strings_node_t *strings = info2_data->strings; + const char *last_updated_string = info2_data->last_updated_string; + + /* We continue here from yahoo_got_info, as if nothing has happened */ +#endif /* PHOTO_SUPPORT */ /* nuke the html, it's easier than trying to parse the horrid stuff */ stripped = gaim_markup_strip_html(url_buffer); @@ -738,7 +917,7 @@ gaim_debug_misc("yahoo", "url_buffer = %p\n", url_buffer); /* convert to utf8 */ - p = g_convert(stripped, -1, "utf-8", profile_strings[strid].charset, NULL, NULL, NULL); + p = g_convert(stripped, -1, "utf-8", strings->charset, NULL, NULL, NULL); if (!p) { p = g_locale_to_utf8(stripped, -1, NULL, NULL, NULL); if (!p) { @@ -755,7 +934,8 @@ /* FIXME need error dialog here */ /* "Last updated" should also be converted to utf8 and with   killed */ - last_updated_utf8_string = g_convert(last_updated_string, -1, "utf-8", profile_strings[strid].charset, NULL, NULL, NULL); + last_updated_utf8_string = g_convert(last_updated_string, -1, "utf-8", + strings->charset, NULL, NULL, NULL); yahoo_remove_nonbreaking_spaces(last_updated_utf8_string); gaim_debug_misc("yahoo", "after utf8 conversion: stripped = (%s)\n", stripped); @@ -765,79 +945,96 @@ s = g_string_sized_new(strlen(url_buffer)); g_string_append(s, "\n"); +#if 0 /* extract their Yahoo! ID and put it in. Don't bother marking has_info as * true, since the Yahoo! ID will always be there */ - if (!gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].yahoo_id_string, 2, "\n", 0, + if (!gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->yahoo_id_string, 2, "\n", 0, NULL, _("Yahoo! ID"), 0, NULL)) - g_string_append_printf(s, _("%s: %s
"), _("Yahoo! ID"), info_data->name); + ; +#endif + + /* Put the Yahoo! ID, nickname, idle time, and status message in */ + g_string_append(s, tooltip_text); + +#if PHOTO_SUPPORT - /* Display the alias, idle time, and status message below the Yahoo! ID */ - b = gaim_find_buddy(gaim_connection_get_account(info_data->gc), info_data->name); - if (b) { - char *statustext = yahoo_tooltip_text(b); - if(b->alias && b->alias[0]) { - char *aliastext = g_markup_escape_text(b->alias, -1); - g_string_append_printf(s, _("Alias: %s
"), aliastext); - g_free(aliastext); - } - if (b->idle > 0) { - char *idletime = gaim_str_seconds_to_string(time(NULL) - b->idle); - g_string_append_printf(s, _("%s: %s
"), _("Idle"), idletime); - g_free(idletime); - } - if (statustext) { - g_string_append_printf(s, "%s
", statustext); - g_free(statustext); + /* Try to put the photo in there too, if there's one and is readable */ + if (data && url_text && len != 0) { + if (strstr(url_text, "400 Bad Request") + || strstr(url_text, "403 Forbidden") + || strstr(url_text, "404 Not Found")) { + + gaim_debug_info("yahoo", "Error getting %s: %s\n", + photo_url_text, url_text); + } else { + gaim_debug_info("yahoo", "%s is %d bytes\n", photo_url_text, len); + id = gaim_imgstore_add(url_text, len, NULL); + g_string_append_printf(s, "
", id); } } +#endif /* PHOTO_SUPPORT */ + /* extract their Email address and put it in */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].my_email_string, 5, "\n", 0, - profile_strings[strid].private_string, _("Email"), 0, NULL); + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->my_email_string, 5, "\n", 0, + strings->private_string, _("Email"), 0, NULL); /* extract the Nickname if it exists */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, "Nickname:", 1, "\n", '\n', + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + "Nickname:", 1, "\n", '\n', NULL, _("Nickname"), 0, NULL); /* extract their RealName and put it in */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].realname_string, 1, "\n", '\n', + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->realname_string, 1, "\n", '\n', NULL, _("Realname"), 0, NULL); /* extract their Location and put it in */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].location_string, 2, "\n", '\n', + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->location_string, 2, "\n", '\n', NULL, _("Location"), 0, NULL); /* extract their Age and put it in */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].age_string, 3, "\n", '\n', + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->age_string, 3, "\n", '\n', NULL, _("Age"), 0, NULL); /* extract their MaritalStatus and put it in */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].maritalstatus_string, 3, "\n", '\n', - profile_strings[strid].no_answer_string, _("Marital Status"), 0, NULL); + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->maritalstatus_string, 3, "\n", '\n', + strings->no_answer_string, _("Marital Status"), 0, NULL); /* extract their Gender and put it in */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].gender_string, 3, "\n", '\n', - profile_strings[strid].no_answer_string, _("Gender"), 0, NULL); + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->gender_string, 3, "\n", '\n', + strings->no_answer_string, _("Gender"), 0, NULL); /* extract their Occupation and put it in */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].occupation_string, 2, "\n", '\n', + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->occupation_string, 2, "\n", '\n', NULL, _("Occupation"), 0, NULL); - /* Hobbies, Latest News, and Favorite Quote are a bit different, since the - * values can contain embedded newlines... but any or all of them can also - * not appear. The way we delimit them is to successively look for the next - * one that _could_ appear, and if all else fails, we end the section by - * looking for the 'Links' heading, which is the next thing to follow this - * bunch. + /* Hobbies, Latest News, and Favorite Quote are a bit different, since + * the values can contain embedded newlines... but any or all of them + * can also not appear. The way we delimit them is to successively + * look for the next one that _could_ appear, and if all else fails, + * we end the section by looking for the 'Links' heading, which is the + * next thing to follow this bunch. (For Yahoo Japan, we check for + * the "Description" ("Self PR") heading instead of "Links".) */ - if (!gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].hobbies_string, 1, profile_strings[strid].latest_news_string, + if (!gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->hobbies_string, 1, strings->latest_news_string, '\n', NULL, _("Hobbies"), 0, NULL)) { - if (!gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].hobbies_string, 1, profile_strings[strid].favorite_quote_string, + if (!gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->hobbies_string, 1, strings->favorite_quote_string, '\n', NULL, _("Hobbies"), 0, NULL)) { - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].hobbies_string, 1, profile_strings[strid].links_string, + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->hobbies_string, 1, strings->links_string, '\n', NULL, _("Hobbies"), 0, NULL); } else @@ -846,66 +1043,76 @@ else found = TRUE; - if (!gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].latest_news_string, 1, profile_strings[strid].favorite_quote_string, + if (!gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->latest_news_string, 1, strings->favorite_quote_string, '\n', NULL, _("Latest News"), 0, NULL)) { - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].latest_news_string, 1, profile_strings[strid].links_string, + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->latest_news_string, 1, strings->links_string, '\n', NULL, _("Latest News"), 0, NULL); } else found = TRUE; - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].favorite_quote_string, 1, profile_strings[strid].links_string, + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->favorite_quote_string, 1, strings->links_string, '\n', NULL, _("Favorite Quote"), 0, NULL); /* Home Page will either be "No home page specified", * or "Home Page: " and a link. - * For non-English profiles, there might be no "Home Page:" string at all, - * in which case we probably can do nothing about it. + * For Yahoo! Japan, if there is no home page specified, + * neither "No home page specified" nor "Home Page:" is shown. */ - if (profile_strings[strid].home_page_string) { - p = !profile_strings[strid].no_home_page_specified_string? NULL: - strstr(stripped, profile_strings[strid].no_home_page_specified_string); + if (strings->home_page_string) { + p = !strings->no_home_page_specified_string? NULL: + strstr(stripped, strings->no_home_page_specified_string); if(!p) { - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].home_page_string, 1, "\n", 0, NULL, + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->home_page_string, 1, "\n", 0, NULL, _("Home Page"), 1, NULL); } } - /* Cool Link {1,2,3} is also different. If "No cool link specified" exists, - * then we have none. If we have one however, we'll need to check and see - * if we have a second one. If we have a second one, we have to check to - * see if we have a third one. + /* Cool Link {1,2,3} is also different. If "No cool link specified" + * exists, then we have none. If we have one however, we'll need to + * check and see if we have a second one. If we have a second one, + * we have to check to see if we have a third one. */ - p = !profile_strings[strid].no_cool_link_specified_string? NULL: - strstr(stripped,profile_strings[strid].no_cool_link_specified_string); + p = !strings->no_cool_link_specified_string? NULL: + strstr(stripped,strings->no_cool_link_specified_string); if (!p) { - if (gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].cool_link_1_string, 1, "\n", 0, NULL, + if (gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->cool_link_1_string, 1, "\n", 0, NULL, _("Cool Link 1"), 1, NULL)) { found = TRUE; - if (gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].cool_link_2_string, 1, "\n", 0, NULL, + if (gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->cool_link_2_string, 1, "\n", 0, NULL, _("Cool Link 2"), 1, NULL)) - gaim_markup_extract_info_field(stripped, stripped_len, s, profile_strings[strid].cool_link_3_string, 1, "\n", 0, NULL, + { + gaim_markup_extract_info_field(stripped, stripped_len, s, + strings->cool_link_3_string, 1, "\n", 0, NULL, _("Cool Link 3"), 1, NULL); + } } } /* see if Member Since is there, and if so, extract it. */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, "Member Since:", 1, last_updated_utf8_string, + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + "Member Since:", 1, last_updated_utf8_string, '\n', NULL, _("Member Since"), 0, NULL); /* extract the Last Updated date and put it in */ - found |= gaim_markup_extract_info_field(stripped, stripped_len, s, last_updated_utf8_string, 0, "\n", '\n', NULL, + found |= gaim_markup_extract_info_field(stripped, stripped_len, s, + last_updated_utf8_string, 0, "\n", '\n', NULL, _("Last Updated"), 0, NULL); /* put a link to the actual profile URL */ g_string_append_printf(s, _("%s: "), _("Profile URL")); - g_string_append_printf(s, "%s%s
", - (yd->jp? YAHOOJP_PROFILE_URL: YAHOO_PROFILE_URL), info_data->name, - (yd->jp? YAHOOJP_PROFILE_URL: YAHOO_PROFILE_URL), info_data->name); + g_string_append_printf(s, "
%s
", + profile_url_text, profile_url_text); /* finish off the html */ g_string_append(s, "\n"); @@ -929,8 +1136,17 @@ g_free(last_updated_utf8_string); g_free(url_buffer); g_string_free(s, TRUE); + g_free(profile_url_text); + g_free(tooltip_text); g_free(info_data->name); g_free(info_data); + +#if PHOTO_SUPPORT + g_free(photo_url_text); + g_free(info2_data); + if (id != -1) + gaim_imgstore_unref(id); +#endif } void yahoo_get_info(GaimConnection *gc, const char *name)