diff options
author | Tomasz Wasilczyk <tomkiewicz@cpw.pidgin.im> | 2012-07-06 14:54:00 +0200 |
---|---|---|
committer | Tomasz Wasilczyk <tomkiewicz@cpw.pidgin.im> | 2012-07-06 14:54:00 +0200 |
commit | 7ee95edf88533667cf19b1d0b6e6b63f395b18df (patch) | |
tree | f10a4bfc0d3a263d6a870dcaa5b0390766f70810 | |
parent | e217fe038c3a1af9de719875fedee2f1b49e1044 (diff) | |
download | pidgin-7ee95edf88533667cf19b1d0b6e6b63f395b18df.tar.gz |
Gadu-Gadu: refactoring of buddy avatars handling. Fixes #13739, #14305
-rw-r--r-- | libpurple/protocols/gg/Makefile.am | 6 | ||||
-rw-r--r-- | libpurple/protocols/gg/avatar.c | 254 | ||||
-rw-r--r-- | libpurple/protocols/gg/avatar.h | 21 | ||||
-rw-r--r-- | libpurple/protocols/gg/gg.c | 169 | ||||
-rw-r--r-- | libpurple/protocols/gg/gg.h | 2 | ||||
-rw-r--r-- | libpurple/protocols/gg/image.c | 2 | ||||
-rw-r--r-- | libpurple/protocols/gg/image.h | 2 | ||||
-rw-r--r-- | libpurple/protocols/gg/libgadu-events.c | 47 | ||||
-rw-r--r-- | libpurple/protocols/gg/libgadu-events.h | 10 |
9 files changed, 347 insertions, 166 deletions
diff --git a/libpurple/protocols/gg/Makefile.am b/libpurple/protocols/gg/Makefile.am index b46cd40709..080820e139 100644 --- a/libpurple/protocols/gg/Makefile.am +++ b/libpurple/protocols/gg/Makefile.am @@ -67,7 +67,11 @@ GGSOURCES = \ purplew.h \ purplew.c \ libgaduw.h \ - libgaduw.c + libgaduw.c \ + avatar.h \ + avatar.c \ + libgadu-events.h \ + libgadu-events.c AM_CFLAGS = $(st) diff --git a/libpurple/protocols/gg/avatar.c b/libpurple/protocols/gg/avatar.c new file mode 100644 index 0000000000..ba4401e5cc --- /dev/null +++ b/libpurple/protocols/gg/avatar.c @@ -0,0 +1,254 @@ +#include "avatar.h" + +#include <debug.h> + +#include "gg.h" +#include "utils.h" + +// Common + +static inline ggp_avatar_session_data * +ggp_avatar_get_avdata(PurpleConnection *gc); + +static gboolean ggp_avatar_timer_cb(gpointer _gc); + +#define GGP_AVATAR_USERAGENT "GG Client build 11.0.0.7562" +#define GGP_AVATAR_SIZE_MAX 1048576 + +// Buddy avatars updating + +typedef struct +{ + uin_t uin; + time_t timestamp; + + PurpleConnection *gc; + PurpleUtilFetchUrlData *request; +} ggp_avatar_buddy_update_req; + +static gboolean ggp_avatar_buddy_update_next(PurpleConnection *gc); +static void ggp_avatar_buddy_update_received(PurpleUtilFetchUrlData *url_data, + gpointer _pending_update, const gchar *url_text, gsize len, + const gchar *error_message); + +#define GGP_AVATAR_BUDDY_URL "http://avatars.gg.pl/%u/s,big" + +/******************************************************************************* + * Common. + ******************************************************************************/ + +void ggp_avatar_setup(PurpleConnection *gc) +{ + ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc); + + avdata->pending_updates = NULL; + avdata->current_update = NULL; + + avdata->timer = purple_timeout_add_seconds(1, ggp_avatar_timer_cb, gc); +} + +void ggp_avatar_cleanup(PurpleConnection *gc) +{ + ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc); + + purple_timeout_remove(avdata->timer); + + if (avdata->current_update != NULL) + { + ggp_avatar_buddy_update_req *current_update = + avdata->current_update; + + purple_util_fetch_url_cancel(current_update->request); + g_free(current_update); + } + avdata->current_update = NULL; + + g_list_free_full(avdata->pending_updates, &g_free); + avdata->pending_updates = NULL; +} + +static inline ggp_avatar_session_data * +ggp_avatar_get_avdata(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + return &accdata->avatar_data; +} + +static gboolean ggp_avatar_timer_cb(gpointer _gc) +{ + PurpleConnection *gc = _gc; + ggp_avatar_session_data *avdata; + + g_return_val_if_fail(PURPLE_CONNECTION_IS_VALID(gc), FALSE); + + avdata = ggp_avatar_get_avdata(gc); + if (avdata->current_update != NULL) + { + //TODO: verbose mode + //purple_debug_misc("gg", "ggp_avatar_timer_cb(%p): there is " + // "already an update running\n", gc); + return TRUE; + } + + while (!ggp_avatar_buddy_update_next(gc)); + + return TRUE; +} + +/******************************************************************************* + * Buddy avatars updating. + ******************************************************************************/ + +void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp) +{ + ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc); + ggp_avatar_buddy_update_req *pending_update = + g_new(ggp_avatar_buddy_update_req, 1); + + purple_debug_misc("gg", "ggp_avatar_buddy_update(%p, %u, %lu)\n", gc, + uin, timestamp); + + pending_update->uin = uin; + pending_update->timestamp = timestamp; + + avdata->pending_updates = g_list_append(avdata->pending_updates, + pending_update); +} + +void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin) +{ + //TODO: verbose mode + //purple_debug_misc("gg", "ggp_avatar_buddy_remove(%p, %u) - " + // "probably not necessary, thus not implemented\n", gc, uin); +} + +/* return TRUE if avatar update was performed or there is no new requests, + FALSE if we can request another one immediately */ +static gboolean ggp_avatar_buddy_update_next(PurpleConnection *gc) +{ + ggp_avatar_session_data *avdata = ggp_avatar_get_avdata(gc); + GList *pending_update_it; + ggp_avatar_buddy_update_req *pending_update; + PurpleBuddy *buddy; + PurpleAccount *account = purple_connection_get_account(gc); + time_t old_timestamp; + const char *old_timestamp_str; + gchar *avatar_url; + + pending_update_it = g_list_first(avdata->pending_updates); + if (pending_update_it == NULL) + return TRUE; + + pending_update = pending_update_it->data; + avdata->pending_updates = g_list_remove(avdata->pending_updates, + pending_update); + buddy = purple_find_buddy(account, ggp_uin_to_str(pending_update->uin)); + + if (!buddy) + { + if (ggp_str_to_uin(purple_account_get_username(account)) == + pending_update->uin) + { + purple_debug_misc("gg", + "ggp_avatar_buddy_update_next(%p): own " + "avatar update requested, but we don't have " + "ourselves on buddy list\n", gc); + } + else + { + purple_debug_warning("gg", + "ggp_avatar_buddy_update_next(%p): " + "%u update requested, but he's not on buddy " + "list\n", gc, pending_update->uin); + } + return FALSE; + } + + old_timestamp_str = purple_buddy_icons_get_checksum_for_user(buddy); + old_timestamp = old_timestamp_str ? g_ascii_strtoull( + old_timestamp_str, NULL, 10) : 0; + if (old_timestamp == pending_update->timestamp) + { + purple_debug_misc("gg", + "ggp_avatar_buddy_update_next(%p): " + "%u have up to date avatar with ts=%lu\n", gc, + pending_update->uin, pending_update->timestamp); + return FALSE; + } + if (old_timestamp > pending_update->timestamp) + { + purple_debug_warning("gg", + "ggp_avatar_buddy_update_next(%p): " + "saved timestamp for %u is newer than received " + "(%lu > %lu)\n", gc, pending_update->uin, old_timestamp, + pending_update->timestamp); + } + + purple_debug_info("gg", + "ggp_avatar_buddy_update_next(%p): " + "updating %u with ts=%lu...\n", gc, pending_update->uin, + pending_update->timestamp); + + pending_update->gc = gc; + avdata->current_update = pending_update; + avatar_url = g_strdup_printf(GGP_AVATAR_BUDDY_URL, pending_update->uin); + pending_update->request = purple_util_fetch_url_request(account, + avatar_url, FALSE, GGP_AVATAR_USERAGENT, TRUE, NULL, FALSE, + GGP_AVATAR_SIZE_MAX, ggp_avatar_buddy_update_received, + pending_update); + g_free(avatar_url); + + return TRUE; +} + +static void ggp_avatar_buddy_update_received(PurpleUtilFetchUrlData *url_data, + gpointer _pending_update, const gchar *url_text, gsize len, + const gchar *error_message) +{ + ggp_avatar_buddy_update_req *pending_update = _pending_update; + PurpleBuddy *buddy; + PurpleAccount *account; + PurpleConnection *gc = pending_update->gc; + ggp_avatar_session_data *avdata; + gchar timestamp_str[20]; + + if (!PURPLE_CONNECTION_IS_VALID(gc)) + { + g_free(pending_update); + return; + } + + avdata = ggp_avatar_get_avdata(gc); + g_assert(pending_update == avdata->current_update); + avdata->current_update = NULL; + + if (len == 0) + { + purple_debug_error("gg", "ggp_avatar_buddy_update_received: bad" + " response while getting avatar for %u: %s\n", + pending_update->uin, error_message); + g_free(pending_update); + return; + } + + account = purple_connection_get_account(gc); + buddy = purple_find_buddy(account, ggp_uin_to_str(pending_update->uin)); + + if (!buddy) + { + purple_debug_warning("gg", "ggp_avatar_buddy_update_received: " + "buddy %u disappeared\n", pending_update->uin); + g_free(pending_update); + return; + } + + g_snprintf(timestamp_str, sizeof(timestamp_str), "%lu", + pending_update->timestamp); + purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy), + g_memdup(url_text, len), len, timestamp_str); + + purple_debug_info("gg", "ggp_avatar_buddy_update_received: " + "got avatar for buddy %u [ts=%lu]\n", pending_update->uin, + pending_update->timestamp); + g_free(pending_update); +} diff --git a/libpurple/protocols/gg/avatar.h b/libpurple/protocols/gg/avatar.h new file mode 100644 index 0000000000..310f37115b --- /dev/null +++ b/libpurple/protocols/gg/avatar.h @@ -0,0 +1,21 @@ +#ifndef _GGP_AVATAR_H +#define _GGP_AVATAR_H + +#include <internal.h> +#include <libgadu.h> + +typedef struct +{ + guint timer; + GList *pending_updates; + + gpointer current_update; +} ggp_avatar_session_data; + +void ggp_avatar_setup(PurpleConnection *gc); +void ggp_avatar_cleanup(PurpleConnection *gc); + +void ggp_avatar_buddy_update(PurpleConnection *gc, uin_t uin, time_t timestamp); +void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin); + +#endif /* _GGP_AVATAR_H */ diff --git a/libpurple/protocols/gg/gg.c b/libpurple/protocols/gg/gg.c index a6acd39cf2..fd8ab55f9f 100644 --- a/libpurple/protocols/gg/gg.c +++ b/libpurple/protocols/gg/gg.c @@ -48,6 +48,7 @@ #include "account.h" #include "deprecated.h" #include "purplew.h" +#include "libgadu-events.h" /* Prototypes */ static void ggp_set_status(PurpleAccount *account, PurpleStatus *status); @@ -481,164 +482,6 @@ static void ggp_rem_deny(PurpleConnection *gc, const char *who) /* ----- INTERNAL CALLBACKS --------------------------------------------- */ /* ---------------------------------------------------------------------- */ -struct gg_fetch_avatar_data -{ - PurpleConnection *gc; - gchar *uin; - gchar *avatar_url; -}; - - -static void gg_fetch_avatar_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *data, size_t len, const gchar *error_message) { - struct gg_fetch_avatar_data *d = user_data; - PurpleAccount *account; - PurpleBuddy *buddy; - gpointer buddy_icon_data; - - purple_debug_info("gg", "gg_fetch_avatar_cb: got avatar image for %s\n", - d->uin); - - /* FIXME: This shouldn't be necessary */ - if (!PURPLE_CONNECTION_IS_VALID(d->gc)) { - g_free(d->uin); - g_free(d->avatar_url); - g_free(d); - g_return_if_reached(); - } - - account = purple_connection_get_account(d->gc); - buddy = purple_find_buddy(account, d->uin); - - if (buddy == NULL) - goto out; - - buddy_icon_data = g_memdup(data, len); - - purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy), - buddy_icon_data, len, d->avatar_url); - purple_debug_info("gg", "gg_fetch_avatar_cb: UIN %s should have avatar " - "now\n", d->uin); - -out: - g_free(d->uin); - g_free(d->avatar_url); - g_free(d); -} - -static void gg_get_avatar_url_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *url_text, size_t len, const gchar *error_message) { - struct gg_fetch_avatar_data *data; - PurpleConnection *gc = user_data; - PurpleAccount *account; - PurpleBuddy *buddy; - const char *uin; - const char *is_blank; - const char *checksum; - - gchar *bigavatar = NULL; - xmlnode *xml = NULL; - xmlnode *xmlnode_users; - xmlnode *xmlnode_user; - xmlnode *xmlnode_avatars; - xmlnode *xmlnode_avatar; - xmlnode *xmlnode_bigavatar; - - g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); - account = purple_connection_get_account(gc); - - if (error_message != NULL) - purple_debug_error("gg", "gg_get_avatars_cb error: %s\n", error_message); - else if (len > 0 && url_text && *url_text) { - xml = xmlnode_from_str(url_text, -1); - if (xml == NULL) - goto out; - - xmlnode_users = xmlnode_get_child(xml, "users"); - if (xmlnode_users == NULL) - goto out; - - xmlnode_user = xmlnode_get_child(xmlnode_users, "user"); - if (xmlnode_user == NULL) - goto out; - - uin = xmlnode_get_attrib(xmlnode_user, "uin"); - - xmlnode_avatars = xmlnode_get_child(xmlnode_user, "avatars"); - if (xmlnode_avatars == NULL) - goto out; - - xmlnode_avatar = xmlnode_get_child(xmlnode_avatars, "avatar"); - if (xmlnode_avatar == NULL) - goto out; - - xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "originBigAvatar"); - if (xmlnode_bigavatar == NULL) - goto out; - - is_blank = xmlnode_get_attrib(xmlnode_avatar, "blank"); - bigavatar = xmlnode_get_data(xmlnode_bigavatar); - - purple_debug_info("gg", "gg_get_avatar_url_cb: UIN %s, IS_BLANK %s, " - "URL %s\n", - uin ? uin : "(null)", is_blank ? is_blank : "(null)", - bigavatar ? bigavatar : "(null)"); - - if (uin != NULL && bigavatar != NULL) { - buddy = purple_find_buddy(account, uin); - if (buddy == NULL) - goto out; - - checksum = purple_buddy_icons_get_checksum_for_user(buddy); - - if (purple_strequal(is_blank, "1")) { - purple_buddy_icons_set_for_user(account, - purple_buddy_get_name(buddy), NULL, 0, NULL); - } else if (!purple_strequal(checksum, bigavatar)) { - data = g_new0(struct gg_fetch_avatar_data, 1); - data->gc = gc; - data->uin = g_strdup(uin); - data->avatar_url = g_strdup(bigavatar); - - purple_debug_info("gg", "gg_get_avatar_url_cb: " - "requesting avatar for %s\n", uin); - /* FIXME: This should be cancelled somewhere if not needed. */ - url_data = purple_util_fetch_url_request(account, - bigavatar, TRUE, "Mozilla/4.0 (compatible; MSIE 5.0)", - FALSE, NULL, FALSE, -1, gg_fetch_avatar_cb, data); - } - } - } - -out: - if (xml) - xmlnode_free(xml); - g_free(bigavatar); -} - -/** - * Try to update avatar of the buddy. - * - * @param gc PurpleConnection - * @param uin UIN of the buddy. - */ -static void ggp_update_buddy_avatar(PurpleConnection *gc, uin_t uin) -{ - gchar *avatarurl; - PurpleUtilFetchUrlData *url_data; - - purple_debug_info("gg", "ggp_update_buddy_avatar(gc, %u)\n", uin); - - avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%u/0.xml", uin); - - /* FIXME: This should be cancelled somewhere if not needed. */ - url_data = purple_util_fetch_url_request( - purple_connection_get_account(gc), avatarurl, TRUE, - "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1, - gg_get_avatar_url_cb, gc); - - g_free(avatarurl); -} /** * Handle change of the status of the buddy. @@ -655,8 +498,6 @@ static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin, const char *st; char *status_msg = NULL; - ggp_update_buddy_avatar(gc, uin); - from = g_strdup_printf("%u", uin); switch (status) { @@ -1170,6 +1011,7 @@ static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int * @param data Raw XML contents. * * @see http://toxygen.net/libgadu/protocol/#ch1.13 + * @todo: this may not be necessary anymore */ static void ggp_xml_event_handler(PurpleConnection *gc, char *data) { @@ -1218,7 +1060,6 @@ static void ggp_xml_event_handler(PurpleConnection *gc, char *data) purple_debug_info("gg", "ggp_xml_event_handler: avatar updated (uid: %u)\n", event_sender); - ggp_update_buddy_avatar(gc, event_sender); break; default: purple_debug_error("gg", @@ -1354,7 +1195,7 @@ static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond) ggp_xml_event_handler(gc, ev->event.xml_event.data); break; case GG_EVENT_USER_DATA: - purple_debug_misc("gg", "GG_EVENT_USER_DATA\n"); + ggp_events_user_data(gc, &ev->event.user_data); break; default: purple_debug_error("gg", @@ -1726,6 +1567,7 @@ static void ggp_login(PurpleAccount *account) purple_connection_set_protocol_data(gc, info); ggp_image_setup(gc); + ggp_avatar_setup(gc); glp->uin = ggp_str_to_uin(purple_account_get_username(account)); glp->password = ggp_convert_to_cp1250(purple_account_get_password(account)); @@ -1839,7 +1681,8 @@ static void ggp_close(PurpleConnection *gc) purple_notify_close_with_handle(gc); ggp_search_destroy(info->searches); - ggp_image_free(gc); + ggp_image_cleanup(gc); + ggp_avatar_cleanup(gc); if (info->inpa > 0) purple_input_remove(info->inpa); diff --git a/libpurple/protocols/gg/gg.h b/libpurple/protocols/gg/gg.h index cd1718ddac..f931da6731 100644 --- a/libpurple/protocols/gg/gg.h +++ b/libpurple/protocols/gg/gg.h @@ -30,6 +30,7 @@ #include "connection.h" #include "image.h" +#include "avatar.h" #include "account.h" @@ -55,6 +56,7 @@ typedef struct { gboolean status_broadcasting; //When TRUE status is visible to all, when FALSE status is visible only to friends. ggp_image_connection_data image_data; + ggp_avatar_session_data avatar_data; } GGPInfo; #endif /* _PURPLE_GG_H */ diff --git a/libpurple/protocols/gg/image.c b/libpurple/protocols/gg/image.c index 39fb7a38b0..f74eed5c99 100644 --- a/libpurple/protocols/gg/image.c +++ b/libpurple/protocols/gg/image.c @@ -52,7 +52,7 @@ void ggp_image_setup(PurpleConnection *gc) ggp_image_pending_image_free); } -void ggp_image_free(PurpleConnection *gc) +void ggp_image_cleanup(PurpleConnection *gc) { ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); diff --git a/libpurple/protocols/gg/image.h b/libpurple/protocols/gg/image.h index 1404952e80..50732ac69a 100644 --- a/libpurple/protocols/gg/image.h +++ b/libpurple/protocols/gg/image.h @@ -20,7 +20,7 @@ typedef enum } ggp_image_prepare_result; void ggp_image_setup(PurpleConnection *gc); -void ggp_image_free(PurpleConnection *gc); +void ggp_image_cleanup(PurpleConnection *gc); const char * ggp_image_pending_placeholder(uint32_t id); diff --git a/libpurple/protocols/gg/libgadu-events.c b/libpurple/protocols/gg/libgadu-events.c new file mode 100644 index 0000000000..19e31ad7bf --- /dev/null +++ b/libpurple/protocols/gg/libgadu-events.c @@ -0,0 +1,47 @@ +#include "libgadu-events.h" + +#include <debug.h> + +#include "avatar.h" + +void ggp_events_user_data(PurpleConnection *gc, struct gg_event_user_data *data) +{ + int user_idx; + gboolean is_update; + + purple_debug_info("gg", "GG_EVENT_USER_DATA [type=%d, user_count=%d]\n", + data->type, data->user_count); + + /* + type = + 1, 3: user information sent after connecting (divided by + 20 contacts; 3 - last one; 1 - rest of them) + 0: data update + */ + is_update = (data->type == 0); + + for (user_idx = 0; user_idx < data->user_count; user_idx++) + { + struct gg_event_user_data_user *data_user = + &data->users[user_idx]; + uin_t uin = data_user->uin; + int attr_idx; + gboolean got_avatar = FALSE; + for (attr_idx = 0; attr_idx < data_user->attr_count; attr_idx++) + { + struct gg_event_user_data_attr *data_attr = + &data_user->attrs[attr_idx]; + if (strcmp(data_attr->key, "avatar") == 0) + { + time_t timestamp = atoi(data_attr->value); + if (timestamp <= 0) + continue; + got_avatar = TRUE; + ggp_avatar_buddy_update(gc, uin, timestamp); + } + } + + if (!is_update && !got_avatar) + ggp_avatar_buddy_remove(gc, uin); + } +} diff --git a/libpurple/protocols/gg/libgadu-events.h b/libpurple/protocols/gg/libgadu-events.h new file mode 100644 index 0000000000..d7ae8b9f83 --- /dev/null +++ b/libpurple/protocols/gg/libgadu-events.h @@ -0,0 +1,10 @@ +#ifndef _GGP_LIBGADU_EVENTS_H +#define _GGP_LIBGADU_EVENTS_H + +#include <internal.h> +#include <libgadu.h> + +void ggp_events_user_data(PurpleConnection *gc, + struct gg_event_user_data *data); + +#endif /* _GGP_LIBGADU_EVENTS_H */ |