Mercurial > pidgin-twitter
view icon.c @ 264:b7ac562d758c
- changed the pattern for twitter icons so that it may match to protected user's icon link too.
- when pixbuf is null, pidgin-twitter will renew icon even if new and old URLs are same.
- will fall back to default icon if pixbuf couldn't be made from retrieved data.
- code cleanup.
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Thu, 04 Dec 2008 18:24:06 +0900 |
parents | b2c64cd550da |
children | c2944685ac8e |
line wrap: on
line source
#include "pidgin-twitter.h" extern GHashTable *icon_hash[]; extern GRegex *regp[]; /* prototypes */ static void insert_icon_at_mark(GtkTextMark *requested_mark, gpointer user_data); static void insert_requested_icon(const gchar *user_name, gint service); static void got_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message); static GdkPixbuf *make_scaled_pixbuf(const gchar *url_text, gsize len); static void got_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message); /* functions */ static void insert_icon_at_mark(GtkTextMark *requested_mark, gpointer user_data) { got_icon_data *gotdata = (got_icon_data *)user_data; gchar *user_name = gotdata->user_name; gint service = gotdata->service; GList *win_list; GtkIMHtml *target_imhtml = NULL; GtkTextBuffer *target_buffer = NULL; GtkTextIter insertion_point; icon_data *data = NULL; GHashTable *hash = NULL; twitter_debug("called: service = %d\n", service); /* find the conversation that contains the mark */ for(win_list = pidgin_conv_windows_get_list(); win_list; win_list = win_list->next) { PidginWindow *win = win_list->data; GList *conv_list; for(conv_list = pidgin_conv_window_get_gtkconvs(win); conv_list; conv_list = conv_list->next) { PidginConversation *conv = conv_list->data; PurpleConversation *purple_conv = conv->active_conv; gint service = get_service_type(purple_conv); if(service != unknown_service) { GtkIMHtml *current_imhtml = GTK_IMHTML(conv->imhtml); GtkTextBuffer *current_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(current_imhtml)); if(current_buffer == gtk_text_mark_get_buffer(requested_mark)) { target_imhtml = current_imhtml; target_buffer = current_buffer; break; } } } } if(!(target_imhtml && target_buffer)) { return; } /* insert icon to the mark */ gtk_text_buffer_get_iter_at_mark(target_buffer, &insertion_point, requested_mark); /* insert icon */ switch(service) { case twitter_service: case wassr_service: case identica_service: case jisko_service: hash = icon_hash[service]; break; default: twitter_debug("unknown service\n"); } if(hash) data = (icon_data *)g_hash_table_lookup(hash, user_name); /* in this function, we put an icon for pending marks. we should * not invalidate the icon here, otherwise it may result in * thrashing. --yaz */ if(!data || !data->pixbuf) { return; } /* insert icon actually */ if(purple_prefs_get_bool(OPT_SHOW_ICON)) { gtk_text_buffer_insert_pixbuf(target_buffer, &insertion_point, data->pixbuf); data->use_count++; } gtk_text_buffer_delete_mark(target_buffer, requested_mark); requested_mark = NULL; } static void insert_requested_icon(const gchar *user_name, gint service) { icon_data *data = NULL; GList *mark_list = NULL; GHashTable *hash = NULL; twitter_debug("called\n"); switch(service) { case twitter_service: case wassr_service: case identica_service: case jisko_service: hash = icon_hash[service]; break; default: twitter_debug("unknown service\n"); break; } if(hash) data = (icon_data *)g_hash_table_lookup(hash, user_name); if(!data) return; mark_list = data->request_list; got_icon_data *gotdata = g_new0(got_icon_data, 1); gotdata->user_name = g_strdup(user_name); gotdata->service = service; twitter_debug("about to insert icon for pending requests\n"); if(mark_list) { g_list_foreach(mark_list, (GFunc) insert_icon_at_mark, gotdata); mark_list = g_list_remove_all(mark_list, NULL); g_list_free(mark_list); data->request_list = NULL; } g_free(gotdata->user_name); g_free(gotdata); } /* this function will be called when profile page has been retrieved */ static void got_page_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) { got_icon_data *gotdata = (got_icon_data *)user_data; gchar *user_name = gotdata->user_name; gint service = gotdata->service; GMatchInfo *match_info = NULL; icon_data *data = NULL; gchar *url = NULL; gint regp_id = -1; twitter_debug("called\n"); if(service == twitter_service) { data = (icon_data *)g_hash_table_lookup( icon_hash[service], user_name); regp_id = IMAGE_TWITTER; } else if(service == wassr_service) { data = (icon_data *)g_hash_table_lookup( icon_hash[service], user_name); regp_id = IMAGE_WASSR; } else if(service == identica_service) { data = (icon_data *)g_hash_table_lookup( icon_hash[service], user_name); regp_id = IMAGE_IDENTICA; } else if(service == jisko_service) { data = (icon_data *)g_hash_table_lookup( icon_hash[service], user_name); regp_id = IMAGE_JISKO; } /* retrieved nothing or got a bad response */ if(!url_text || (!strstr(url_text, "HTTP/1.1 200 OK") && !strstr(url_text, "HTTP/1.0 200 OK"))) { if(data) { data->requested = FALSE; data->fetch_data = NULL; } g_free(gotdata->user_name); g_free(gotdata); return; } /* setup image url */ g_regex_match(regp[regp_id], url_text, 0, &match_info); if(!g_match_info_matches(match_info)) { g_match_info_free(match_info); if(service == twitter_service) { twitter_debug("fall back to twitter default icon: %s\n", gotdata->user_name); url = g_strdup(TWITTER_DEFAULT_ICON_URL); } else if(service == identica_service) { twitter_debug("fall back to identica default icon: %s\n", gotdata->user_name); url = g_strdup(IDENTICA_DEFAULT_ICON_URL); } else if(service == jisko_service) { twitter_debug("fall back to jisko default icon: %s\n", gotdata->user_name); url = g_strdup(JISKO_DEFAULT_ICON_URL); } else { twitter_debug("no image url found\n"); if(data) { data->requested = FALSE; data->fetch_data = NULL; } g_free(gotdata->user_name); g_free(gotdata); return; } } else { url = g_match_info_fetch(match_info, 1); g_match_info_free(match_info); } /* find out basename */ gchar *slash = strrchr(url, '/'); *slash = '\0'; gchar *lower = g_ascii_strdown(slash+1, -1); if(strstr(lower, ".png")) data->img_type = "png"; else if(strstr(lower, ".gif")) data->img_type = "gif"; else if(strstr(lower, ".jpg") || strstr(lower, ".jpeg")) data->img_type = "jpg"; g_free(lower); gchar *tmp; /* url encode basename. twitter needs this. */ if(service == twitter_service) tmp = g_strdup_printf("%s/%s", url, purple_url_encode(slash+1)); else if(service == wassr_service) { gchar *tmp0 = NULL; tmp0 = g_regex_replace(regp[SIZE_128_WASSR], slash+1, -1, 0, ".64.", 0, NULL); tmp = g_strdup_printf("http://wassr.jp%s/%s", url, tmp0 ? tmp0 : slash+1); g_free(tmp0); } else { tmp = g_strdup_printf("%s/%s", url, slash+1); } g_free(url); url = tmp; /* if requesting icon url is the same as old, return. */ if(data && data->pixbuf && url && data->icon_url && !strcmp(data->icon_url, url)) { twitter_debug("old url = %s new url = %s\n", data->icon_url, url); data->requested = FALSE; data->fetch_data = NULL; g_free(url); return; } if(data && data->pixbuf) { gdk_pixbuf_unref(data->pixbuf); data->pixbuf = NULL; } g_free(data->icon_url); data->icon_url = g_strdup(url); data->use_count = 0; data->mtime = time(NULL); /* xxx is there a better way? */ twitter_debug("requested url=%s\n", url); /* request fetch image */ if(url) { /* reuse gotdata. just pass given one */ /* gotdata will be released in got_icon_cb */ data->fetch_data = purple_util_fetch_url_request(url, TRUE, NULL, TRUE, NULL, FALSE, got_icon_cb, gotdata); twitter_debug("request %s's icon\n", user_name); g_free(url); } } static GdkPixbuf * make_scaled_pixbuf(const gchar *url_text, gsize len) { /* make pixbuf */ GdkPixbufLoader *loader; GdkPixbuf *src = NULL, *dest = NULL; gint size; g_return_val_if_fail(url_text != NULL, NULL); g_return_val_if_fail(len > 0, NULL); loader = gdk_pixbuf_loader_new(); gdk_pixbuf_loader_write(loader, (guchar *)url_text, len, NULL); gdk_pixbuf_loader_close(loader, NULL); src = gdk_pixbuf_loader_get_pixbuf(loader); if(!src) return NULL; size = purple_prefs_get_int(OPT_ICON_SIZE); if(size == 0) size = DEFAULT_ICON_SIZE; dest = gdk_pixbuf_scale_simple(src, size, size, GDK_INTERP_HYPER); gdk_pixbuf_unref(src); return dest; } static gchar *ext_list[] = { "png", "gif", "jpg", NULL }; /* this function will be called when requested icon has been retrieved */ static void got_icon_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) { got_icon_data *gotdata = (got_icon_data *)user_data; gchar *user_name = gotdata->user_name; gint service = gotdata->service; icon_data *data = NULL; GHashTable *hash = NULL; GdkPixbuf *pixbuf = NULL; const gchar *dirname = NULL; twitter_debug("called: service = %d\n", service); switch(service) { case twitter_service: case wassr_service: case identica_service: case jisko_service: hash = icon_hash[service]; break; default: twitter_debug("unknown service\n"); } if(hash) data = (icon_data *)g_hash_table_lookup(hash, user_name); if(!data) { twitter_debug("cannot retrieve icon_data from hash (should not be called)\n"); goto fin_got_icon_cb; } /* return if download failed */ if(!url_text) { twitter_debug("downloading %s's icon failed : %s\n", user_name, error_message); data->requested = FALSE; goto fin_got_icon_cb; } /* remove download request */ data->requested = FALSE; data->fetch_data = NULL; /* return if user's icon has been downloaded */ if(data->pixbuf) { twitter_debug("%s's icon has already been downloaded\n", user_name); goto fin_got_icon_cb; } pixbuf = make_scaled_pixbuf(url_text, len); if(!pixbuf) { twitter_debug("cannot make pixbuf from downloaded data\n"); /* ask to download default icon instead */ got_icon_data *gotdata2 = g_new0(got_icon_data, 1); const gchar *url = NULL; gotdata2->user_name = g_strdup(gotdata->user_name); gotdata2->service = service; switch(service) { case twitter_service: url = TWITTER_DEFAULT_ICON_URL; break; case identica_service: url = IDENTICA_DEFAULT_ICON_URL; break; case jisko_service: url = JISKO_DEFAULT_ICON_URL; break; } g_free(data->icon_url); data->icon_url = g_strdup(url); data->requested = TRUE; data->fetch_data = purple_util_fetch_url_request(url, TRUE, NULL, TRUE, NULL, FALSE, got_icon_cb, gotdata2); goto fin_got_icon_cb; } data->pixbuf = pixbuf; twitter_debug("new icon pixbuf = %p size = %d\n", pixbuf, gdk_pixbuf_get_rowstride(pixbuf) * gdk_pixbuf_get_height(pixbuf)); if(hash) g_hash_table_insert(hash, g_strdup(user_name), data); dirname = purple_prefs_get_string(OPT_ICON_DIR); /* store retrieved image to a file in icon dir */ if(ensure_path_exists(dirname)) { gchar *filename = NULL; gchar *path = NULL; const gchar *suffix = NULL; gchar **extp; switch(service) { case twitter_service: suffix = "twitter"; break; case wassr_service: suffix = "wassr"; break; case identica_service: suffix = "identica"; break; case jisko_service: suffix = "jisko"; break; default: twitter_debug("unknown service\n"); break; } /* remove old file first */ for(extp = ext_list; *extp; extp++) { filename = g_strdup_printf("%s_%s.%s", user_name, suffix, *extp); path = g_build_filename(dirname, filename, NULL); g_remove(path); g_free(filename); g_free(path); } /* setup path */ filename = g_strdup_printf("%s_%s.%s", user_name, suffix, data->img_type); path = g_build_filename(dirname, filename, NULL); g_free(filename); filename = NULL; g_file_set_contents(path, url_text, len, NULL); g_free(path); path = NULL; data->mtime = time(NULL); } twitter_debug("Downloading %s's icon has been complete.\n", user_name); /* Insert the icon to messages that has been received. */ insert_requested_icon(user_name, service); fin_got_icon_cb: g_free(gotdata->user_name); g_free(gotdata); } void request_icon(const char *user_name, gint service, gboolean renew) { gchar *url = NULL; /* look local icon cache for the requested icon */ gchar *path = NULL; icon_data *data = NULL; GHashTable *hash = NULL; const gchar *suffix = NULL; switch(service) { case twitter_service: hash = icon_hash[service]; suffix = "twitter"; break; case wassr_service: hash = icon_hash[service]; suffix = "wassr"; break; case identica_service: hash = icon_hash[service]; suffix = "identica"; break; case jisko_service: hash = icon_hash[service]; suffix = "jisko"; break; default: twitter_debug("unknown service\n"); break; } if(!hash) return; /* since this function is called after mark_icon_for_user(), data * must exist here. */ data = (icon_data *)g_hash_table_lookup(hash, user_name); /* if the image is in a hash, just return */ if(data && data->pixbuf && !renew) return; /* check if saved file exists */ if(suffix && !renew) { gchar *filename = NULL; gchar **extp; for(extp = ext_list; *extp; extp++) { filename = g_strdup_printf("%s_%s.%s", user_name, suffix, *extp); path = g_build_filename(purple_prefs_get_string(OPT_ICON_DIR), filename, NULL); g_free(filename); twitter_debug("path = %s\n", path); /* build image from file, if file exists */ if(g_file_test(path, G_FILE_TEST_EXISTS)) { gchar *imgdata = NULL; size_t len; GError *err = NULL; GdkPixbuf *pixbuf = NULL; struct stat buf; if (!g_file_get_contents(path, &imgdata, &len, &err)) { twitter_debug("Error reading %s: %s\n", path, err->message); g_error_free(err); } if(stat(path, &buf)) data->mtime = buf.st_mtime; pixbuf = make_scaled_pixbuf(imgdata, len); g_free(imgdata); if(pixbuf) { data->pixbuf = pixbuf; twitter_debug("new icon pixbuf = %p size = %d\n", pixbuf, gdk_pixbuf_get_rowstride(pixbuf) * gdk_pixbuf_get_height(pixbuf)); data->img_type = *extp; twitter_debug("icon data has been loaded from file\n"); insert_requested_icon(user_name, service); } g_free(path); return; } g_free(path); } /* for */ } /* suffix */ /* Return if user's icon has been requested already. */ if(data->requested) return; else data->requested = TRUE; /* Create the URL for an user's icon. */ switch(service) { case twitter_service: url = g_strdup_printf("http://twitter.com/%s", user_name); break; case wassr_service: url = g_strdup_printf("http://wassr.jp/user/%s", user_name); break; case identica_service: url = g_strdup_printf("http://identi.ca/%s", user_name); break; case jisko_service: url = g_strdup_printf("http://jisko.net/%s", user_name); break; default: twitter_debug("unknown service\n"); break; } if(url) { got_icon_data *gotdata = g_new0(got_icon_data, 1); gotdata->user_name = g_strdup(user_name); gotdata->service = service; /* gotdata will be released in got_icon_cb */ if(service == twitter_service || service == wassr_service || service == identica_service || service == jisko_service) { data->fetch_data = purple_util_fetch_url_request(url, TRUE, NULL, TRUE, NULL, TRUE, got_page_cb, gotdata); } else { /* unused */ data->fetch_data = purple_util_fetch_url_request(url, TRUE, NULL, TRUE, NULL, FALSE, got_icon_cb, gotdata); } g_free(url); url = NULL; twitter_debug("request %s's icon\n", user_name); } } void mark_icon_for_user(GtkTextMark *mark, const gchar *user_name, gint service) { icon_data *data = NULL; GHashTable *hash = NULL; twitter_debug("called\n"); switch(service) { case twitter_service: hash = icon_hash[twitter_service]; break; case wassr_service: hash = icon_hash[wassr_service]; break; case identica_service: hash = icon_hash[identica_service]; break; case jisko_service: hash = icon_hash[jisko_service]; break; default: twitter_debug("unknown service\n"); break; } if(hash) data = (icon_data *)g_hash_table_lookup(hash, user_name); /* proper place to allocate icon_data */ if(!data) { data = g_new0(icon_data, 1); g_hash_table_insert(hash, g_strdup(user_name), data); } data->request_list = g_list_prepend(data->request_list, mark); } void invalidate_icon_data_func(gpointer key, gpointer value, gpointer user_data) { icon_data *data = (icon_data *)value; g_return_if_fail(data != NULL); g_object_unref(data->pixbuf); data->pixbuf = NULL; }