Mon, 16 Sep 2013 18:08:19 +0530
Merged soc.2013.gobjectification branch
| libpurple/protocols/gg/Makefile.am | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/gg/confer.c | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/gg/confer.h | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/gg/gg.c | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/gg/gg.h | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/gg/image.c | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/gg/servconn.c | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/gg/status.c | file | annotate | diff | comparison | revisions |
--- a/libpurple/Makefile.mingw Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/Makefile.mingw Mon Sep 16 18:08:19 2013 +0530 @@ -156,6 +156,7 @@ -lnss3 \ -lnspr4 \ -lgnutls \ + -lz \ $(VV_LIBS) include $(PIDGIN_COMMON_RULES)
--- a/libpurple/plugins/perl/common/Request.xs Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/plugins/perl/common/Request.xs Mon Sep 16 18:08:19 2013 +0530 @@ -287,41 +287,48 @@ purple_request_field_choice_new(class, id, text, default_value = 0) const char *id const char *text - int default_value + gpointer default_value C_ARGS: id, text, default_value void -purple_request_field_choice_add(field, label) +purple_request_field_choice_add(field, label, value) Purple::Request::Field field const char *label + gpointer value -int +gpointer purple_request_field_choice_get_default_value(field) Purple::Request::Field field -void -purple_request_field_choice_get_labels(field) - Purple::Request::Field field -PREINIT: - GList *l; -PPCODE: - for (l = purple_request_field_choice_get_labels(field); l != NULL; l = l->next) { - XPUSHs(sv_2mortal(newSVpv(l->data, 0))); - } + # I'm not sure, if this is the correct implementation - if anyone will need it, + # he will add this back to API. + #void + #purple_request_field_choice_get_elements(field) + # Purple::Request::Field field + #PREINIT: + # GList *l; + #PPCODE: + # for (l = purple_request_field_choice_get_elements(field); l != NULL; l = l->next) { + # XPUSHs(sv_2mortal(newSVpv(l->data, 0))); + # l = l->next; + # if (l == NULL) + # break; + # XPUSHs(l->data); + # } -int +gpointer purple_request_field_choice_get_value(field) Purple::Request::Field field void purple_request_field_choice_set_default_value(field, default_value) Purple::Request::Field field - int default_value + gpointer default_value void purple_request_field_choice_set_value(field, value) Purple::Request::Field field - int value + gpointer value MODULE = Purple::Request PACKAGE = Purple::Request::Field PREFIX = purple_request_field_ PROTOTYPES: ENABLE @@ -604,7 +611,7 @@ Purple::Request::Fields fields const char *id -int +gpointer purple_request_fields_get_choice(fields, id) Purple::Request::Fields fields const char *id
--- a/libpurple/protocols/gg/Makefile.am Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/Makefile.am Mon Sep 16 18:08:19 2013 +0530 @@ -1,5 +1,7 @@ #V=0 -#GADU_EXTRA_WARNINGS = -Wall -Wextra -Werror +#CFLAGS = -g -O0 +GADU_EXTRA = -Wall -Wextra -fno-inline +#GADU_EXTRA += -Werror pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) @@ -49,46 +51,56 @@ GADU_CFLAGS += $(GNUTLS_CFLAGS) endif +GADU_LIBS += $(JSON_LIBS) + GGSOURCES = \ $(INTGGSOURCES) \ - utils.h \ - utils.c \ - confer.h \ - confer.c \ - blist.h \ + account.c \ + account.h \ + avatar.c \ + avatar.h \ blist.c \ - gg.h \ + blist.h \ + chat.c \ + chat.h \ + deprecated.c \ + deprecated.h \ + edisc.c \ + edisc.h \ gg.c \ - resolver-purple.h \ - resolver-purple.c \ - image.h \ + gg.h \ + html.c \ + html.h \ image.c \ - account.h \ - account.c \ - deprecated.h \ - deprecated.c \ - purplew.h \ + image.h \ + libgadu-events.c \ + libgadu-events.h \ + libgaduw.c \ + libgaduw.h \ + message-prpl.c \ + message-prpl.h \ + multilogon.c \ + multilogon.h \ + pubdir-prpl.c \ + pubdir-prpl.h \ purplew.c \ - libgaduw.h \ - libgaduw.c \ - avatar.h \ - avatar.c \ - libgadu-events.h \ - libgadu-events.c \ + purplew.h \ + resolver-purple.c \ + resolver-purple.h \ roster.c \ roster.h \ + servconn.c \ + servconn.h \ + status.c \ + status.h \ + tcpsocket.c \ + tcpsocket.h \ + utils.c \ + utils.h \ validator.c \ validator.h \ xml.c \ xml.h \ - multilogon.c \ - multilogon.h \ - status.c \ - status.h \ - servconn.c \ - servconn.h \ - pubdir-prpl.c \ - pubdir-prpl.h \ oauth/oauth.c \ oauth/oauth.h \ oauth/oauth-parameter.c \ @@ -118,10 +130,11 @@ endif AM_CPPFLAGS = \ - $(GADU_EXTRA_WARNINGS) \ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(INTGG_CFLAGS) \ $(GLIB_CFLAGS) \ + $(JSON_CFLAGS) \ $(GPLUGIN_CFLAGS) \ - $(DEBUG_CFLAGS) + $(DEBUG_CFLAGS) \ + $(GADU_EXTRA)
--- a/libpurple/protocols/gg/Makefile.mingw Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/Makefile.mingw Mon Sep 16 18:08:19 2013 +0530 @@ -32,11 +32,13 @@ -I$(GTK_TOP)/include \ -I$(GTK_TOP)/include/glib-2.0 \ -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(GNUTLS_TOP)/include + -I$(GNUTLS_TOP)/include \ + -I$(JSON_GLIB_TOP)/include/json-glib-1.0 LIB_PATHS += \ -L$(GTK_TOP)/lib \ -L$(GNUTLS_TOP)/lib \ + -L$(JSON_GLIB_TOP)/lib \ -L$(PURPLE_TOP) ## @@ -46,9 +48,11 @@ account.c \ avatar.c \ buddylist.c \ - confer.c \ + chat.c \ deprecated.c \ + edisc.c \ gg.c \ + html.c \ image.c \ lib/common.c \ lib/dcc.c \ @@ -68,6 +72,7 @@ lib/sha1.c \ libgadu-events.c \ libgaduw.c \ + message-prpl.c \ multilogon.c \ oauth/oauth.c \ oauth/oauth-parameter.c \ @@ -78,6 +83,7 @@ roster.c \ servconn.c \ status.c \ + tcpsocket.c \ utils.c \ validator.c \ xml.c @@ -89,8 +95,10 @@ ## LIBS = \ -lglib-2.0 \ + -lgobject-2.0 \ -lgnutls \ -lintl \ + -ljson-glib-1.0 \ -lpurple \ -lws2_32 \ -lz
--- a/libpurple/protocols/gg/avatar.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/avatar.c Mon Sep 16 18:08:19 2013 +0530 @@ -151,10 +151,12 @@ { 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); + g_new(ggp_avatar_buddy_update_req, 1); //TODO: leak? - purple_debug_misc("gg", "ggp_avatar_buddy_update(%p, %u, %lu)\n", gc, - uin, timestamp); + if (purple_debug_is_verbose()) { + purple_debug_misc("gg", "ggp_avatar_buddy_update(%p, %u, %lu)\n", gc, + uin, timestamp); + } pending_update->uin = uin; pending_update->timestamp = timestamp; @@ -165,7 +167,9 @@ void ggp_avatar_buddy_remove(PurpleConnection *gc, uin_t uin) { - purple_debug_info("gg", "ggp_avatar_buddy_remove(%p, %u)\n", gc, uin); + if (purple_debug_is_verbose()) { + purple_debug_misc("gg", "ggp_avatar_buddy_remove(%p, %u)\n", gc, uin); + } purple_buddy_icons_set_for_user(purple_connection_get_account(gc), ggp_uin_to_str(uin), NULL, 0, NULL); @@ -218,10 +222,12 @@ 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); + if (purple_debug_is_verbose()) { + 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) @@ -322,10 +328,15 @@ void ggp_avatar_own_set(PurpleConnection *gc, PurpleStoredImage *img) { - ggp_avatar_own_data *own_data = ggp_avatar_get_avdata(gc)->own_data; + ggp_avatar_own_data *own_data; + + if (!PURPLE_CONNECTION_IS_VALID(gc) || !PURPLE_CONNECTION_IS_CONNECTED(gc)) + return; purple_debug_info("gg", "ggp_avatar_own_set(%p, %p)", gc, img); + own_data = ggp_avatar_get_avdata(gc)->own_data; + if (img == NULL) { purple_debug_warning("gg", "ggp_avatar_own_set: avatar removing"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/chat.c Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,619 @@ +#include "chat.h" + +#include <debug.h> + +#include "gg.h" +#include "utils.h" +#include "message-prpl.h" + +#if GGP_ENABLE_GG11 + +typedef struct _ggp_chat_local_info ggp_chat_local_info; + +struct _ggp_chat_session_data +{ + ggp_chat_local_info *chats; + int chats_count; + + gboolean got_all_chats_info; + GSList *pending_joins; +}; + +struct _ggp_chat_local_info +{ + int local_id; + uint64_t id; + + PurpleConversation *conv; + PurpleConnection *gc; + + gboolean left; + gboolean previously_joined; + + uin_t *participants; + int participants_count; +}; + +static ggp_chat_local_info * ggp_chat_new(PurpleConnection *gc, uint64_t id); +static ggp_chat_local_info * ggp_chat_get(PurpleConnection *gc, uint64_t id); +static void ggp_chat_open_conv(ggp_chat_local_info *chat); +static ggp_chat_local_info * ggp_chat_get_local(PurpleConnection *gc, + int local_id); +static void ggp_chat_joined(ggp_chat_local_info *chat, uin_t uin); +static void ggp_chat_left(ggp_chat_local_info *chat, uin_t uin); +static const gchar * ggp_chat_get_name_from_id(uint64_t id); +static uint64_t ggp_chat_get_id_from_name(const gchar * name); +static void ggp_chat_join_id(PurpleConnection *gc, uint64_t id); + +static inline ggp_chat_session_data * +ggp_chat_get_sdata(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + return accdata->chat_data; +} + +void ggp_chat_setup(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + ggp_chat_session_data *sdata = g_new0(ggp_chat_session_data, 1); + + accdata->chat_data = sdata; +} + +void ggp_chat_cleanup(PurpleConnection *gc) +{ + ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc); + int i; + + g_slist_free_full(sdata->pending_joins, g_free); + for (i = 0; i < sdata->chats_count; i++) + g_free(sdata->chats[i].participants); + g_free(sdata->chats); + g_free(sdata); +} + +static ggp_chat_local_info * ggp_chat_new(PurpleConnection *gc, uint64_t id) +{ + ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc); + int local_id; + ggp_chat_local_info *chat; + + if (NULL != (chat = ggp_chat_get(gc, id))) + return chat; + + local_id = sdata->chats_count++; + sdata->chats = g_realloc(sdata->chats, + sdata->chats_count * sizeof(ggp_chat_local_info)); + chat = &sdata->chats[local_id]; + + chat->local_id = local_id; + chat->id = id; + chat->conv = NULL; + chat->gc = gc; + chat->left = FALSE; + chat->previously_joined = FALSE; + chat->participants = NULL; + chat->participants_count = 0; + + return chat; +} + +static ggp_chat_local_info * ggp_chat_get(PurpleConnection *gc, uint64_t id) +{ + ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc); + int i; + + for (i = 0; i < sdata->chats_count; i++) + if (sdata->chats[i].id == id) + return &sdata->chats[i]; + + return NULL; +} + +static void ggp_chat_open_conv(ggp_chat_local_info *chat) +{ + PurpleConvChat *pcchat; + int i; + + if (chat->conv != NULL) + return; + + chat->conv = serv_got_joined_chat(chat->gc, chat->local_id, + ggp_chat_get_name_from_id(chat->id)); + if (chat->previously_joined) + { + purple_conversation_write(chat->conv, NULL, + _("You have re-joined the chat"), PURPLE_MESSAGE_SYSTEM, + time(NULL)); + } + chat->previously_joined = TRUE; + + pcchat = purple_conversation_get_chat_data(chat->conv); + purple_conv_chat_clear_users(pcchat); + for (i = 0; i < chat->participants_count; i++) + purple_conv_chat_add_user(pcchat, + ggp_uin_to_str(chat->participants[i]), NULL, + PURPLE_CBFLAGS_NONE, FALSE); +} + +static ggp_chat_local_info * ggp_chat_get_local(PurpleConnection *gc, + int local_id) +{ + ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc); + int i; + + for (i = 0; i < sdata->chats_count; i++) + if (sdata->chats[i].local_id == local_id) + return &sdata->chats[i]; + + return NULL; +} + +void ggp_chat_got_event(PurpleConnection *gc, const struct gg_event *ev) +{ + ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc); + ggp_chat_local_info *chat; + uint32_t i; + + if (ev->type == GG_EVENT_CHAT_INFO) + { + const struct gg_event_chat_info *eci = &ev->event.chat_info; + chat = ggp_chat_new(gc, eci->id); + for (i = 0; i < eci->participants_count; i++) + ggp_chat_joined(chat, eci->participants[i]); + } + else if (ev->type == GG_EVENT_CHAT_INFO_GOT_ALL) + { + GSList *it = sdata->pending_joins; + sdata->got_all_chats_info = TRUE; + while (it) + { + uint64_t *id_p = it->data; + ggp_chat_join_id(gc, *id_p); + it = g_slist_next(it); + } + g_slist_free_full(sdata->pending_joins, g_free); + sdata->pending_joins = NULL; + } + else if (ev->type == GG_EVENT_CHAT_INFO_UPDATE) + { + const struct gg_event_chat_info_update *eciu = + &ev->event.chat_info_update; + chat = ggp_chat_get(gc, eciu->id); + if (!chat) + { + purple_debug_error("gg", "ggp_chat_got_event: " + "chat %" G_GUINT64_FORMAT " not found\n", + eciu->id); + return; + } + if (eciu->type == GG_CHAT_INFO_UPDATE_ENTERED) + ggp_chat_joined(chat, eciu->participant); + else if (eciu->type == GG_CHAT_INFO_UPDATE_EXITED) + ggp_chat_left(chat, eciu->participant); + else + purple_debug_warning("gg", "ggp_chat_got_event: " + "unknown update type - %d", eciu->type); + } + else if (ev->type == GG_EVENT_CHAT_CREATED) + { + const struct gg_event_chat_created *ecc = + &ev->event.chat_created; + uin_t me = ggp_str_to_uin(purple_account_get_username( + purple_connection_get_account(gc))); + chat = ggp_chat_new(gc, ecc->id); + ggp_chat_joined(chat, me); + ggp_chat_open_conv(chat); + } + else if (ev->type == GG_EVENT_CHAT_INVITE_ACK) + { + /* ignore */ + } + else + { + purple_debug_fatal("gg", "ggp_chat_got_event: unexpected event " + "- %d\n", ev->type); + } +} + +static int ggp_chat_participant_find(ggp_chat_local_info *chat, uin_t uin) +{ + int i; + for (i = 0; i < chat->participants_count; i++) + if (chat->participants[i] == uin) + return i; + return -1; +} + +static void ggp_chat_joined(ggp_chat_local_info *chat, uin_t uin) +{ + int idx = ggp_chat_participant_find(chat, uin); + if (idx >= 0) + { + purple_debug_warning("gg", "ggp_chat_joined: " + "user %u is already present in chat %" G_GUINT64_FORMAT + "\n", uin, chat->id); + return; + } + chat->participants_count++; + chat->participants = g_realloc(chat->participants, + sizeof(uin) * chat->participants_count); + chat->participants[chat->participants_count - 1] = uin; + + if (!chat->conv) + return; + purple_conv_chat_add_user(purple_conversation_get_chat_data(chat->conv), + ggp_uin_to_str(uin), NULL, PURPLE_CBFLAGS_NONE, TRUE); +} + +static void ggp_chat_left(ggp_chat_local_info *chat, uin_t uin) +{ + uin_t me; + int idx = ggp_chat_participant_find(chat, uin); + + if (idx < 0) + { + purple_debug_warning("gg", "ggp_chat_joined: " + "user %u isn't present in chat %" G_GUINT64_FORMAT "\n", + uin, chat->id); + return; + } + chat->participants[idx] = + chat->participants[chat->participants_count - 1]; + chat->participants_count--; + chat->participants = g_realloc(chat->participants, + sizeof(uin) * chat->participants_count); + + if (chat->conv == NULL) + return; + + me = ggp_str_to_uin(purple_account_get_username( + purple_connection_get_account(chat->gc))); + + if (me == uin) + { + purple_conversation_write(chat->conv, NULL, + _("You have left the chat"), PURPLE_MESSAGE_SYSTEM, + time(NULL)); + serv_got_chat_left(chat->gc, chat->local_id); + chat->conv = NULL; + chat->left = TRUE; + } + purple_conv_chat_remove_user(purple_conversation_get_chat_data( + chat->conv), ggp_uin_to_str(uin), NULL); +} + +GList * ggp_chat_info(PurpleConnection *gc) +{ + GList *m = NULL; + struct proto_chat_entry *pce; + + pce = g_new0(struct proto_chat_entry, 1); + pce->label = _("_Conference identifier:"); + pce->identifier = "id"; + pce->required = FALSE; + m = g_list_append(m, pce); + + return m; +} + +GHashTable * ggp_chat_info_defaults(PurpleConnection *gc, const char *chat_name) +{ + GHashTable *defaults; + + defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); + + if (chat_name != NULL && ggp_chat_get_id_from_name(chat_name) != 0) + g_hash_table_insert(defaults, "id", g_strdup(chat_name)); + + return defaults; +} + +char * ggp_chat_get_name(GHashTable *components) +{ + return g_strdup((gchar*)g_hash_table_lookup(components, "id")); +} + +static const gchar * ggp_chat_get_name_from_id(uint64_t id) +{ + static gchar buff[30]; + g_snprintf(buff, sizeof(buff), "%" G_GUINT64_FORMAT, id); + return buff; +} + +static uint64_t ggp_chat_get_id_from_name(const gchar * name) +{ + uint64_t id; + gchar *endptr; + + if (name == NULL) + return 0; + + id = g_ascii_strtoull(name, &endptr, 10); + + if (*endptr != '\0' || id == G_MAXUINT64) + return 0; + + return id; +} + +void ggp_chat_join(PurpleConnection *gc, GHashTable *components) +{ + ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc); + GGPInfo *info = purple_connection_get_protocol_data(gc); + const gchar *id_cs; + gchar *id_s; + uint64_t id; + + id_cs = g_hash_table_lookup(components, "id"); + id_s = g_strdup(id_cs); + if (id_s) + g_strstrip(id_s); + if (id_s == NULL || id_s[0] == '\0') + { + g_free(id_s); + if (gg_chat_create(info->session) < 0) + { + purple_debug_error("gg", "ggp_chat_join; " + "cannot create\n"); + purple_serv_got_join_chat_failed(gc, components); + } + return; + } + id = ggp_chat_get_id_from_name(id_s); + g_free(id_s); + + if (!id) + { + char *buff = g_strdup_printf( + _("%s is not a valid room identifier"), id_cs); + purple_notify_error(gc, _("Invalid Room Identifier"), + _("Invalid Room Identifier"), buff); + g_free(buff); + purple_serv_got_join_chat_failed(gc, components); + return; + } + + if (sdata->got_all_chats_info) + ggp_chat_join_id(gc, id); + else + { + uint64_t *id_p = g_new(uint64_t, 1); + *id_p = id; + sdata->pending_joins = g_slist_append(sdata->pending_joins, id_p); + } + +} + +static void ggp_chat_join_id(PurpleConnection *gc, uint64_t id) +{ + GHashTable *components; + ggp_chat_local_info *chat = ggp_chat_get(gc, id); + + if (chat && !chat->left) + { + ggp_chat_open_conv(chat); + return; + } + + if (!chat) + { + char *id_s = g_strdup_printf("%" G_GUINT64_FORMAT, id); + char *buff = g_strdup_printf( + _("%s is not a valid room identifier"), id_s); + g_free(id_s); + purple_notify_error(gc, _("Invalid Room Identifier"), + _("Invalid Room Identifier"), buff); + g_free(buff); + } + else /* if (chat->left) */ + { + purple_notify_error(gc, _("Could not join chat room"), + _("Could not join chat room"), + _("You have to ask for invitation from another chat " + "participant")); + } + + components = ggp_chat_info_defaults(gc, ggp_chat_get_name_from_id(id)); + purple_serv_got_join_chat_failed(gc, components); + g_hash_table_destroy(components); +} + +void ggp_chat_leave(PurpleConnection *gc, int local_id) +{ + GGPInfo *info = purple_connection_get_protocol_data(gc); + ggp_chat_local_info *chat; + uin_t me; + + chat = ggp_chat_get_local(gc, local_id); + if (!chat) + { + purple_debug_error("gg", "ggp_chat_leave: " + "chat %u doesn't exists\n", local_id); + return; + } + + if (gg_chat_leave(info->session, chat->id) < 0) + { + purple_debug_error("gg", "ggp_chat_leave: " + "unable to leave chat %" G_GUINT64_FORMAT "\n", + chat->id); + } + chat->conv = NULL; + + me = ggp_str_to_uin(purple_account_get_username( + purple_connection_get_account(chat->gc))); + + ggp_chat_left(chat, me); + chat->left = TRUE; +} + +void ggp_chat_invite(PurpleConnection *gc, int local_id, const char *message, + const char *who) +{ + GGPInfo *info = purple_connection_get_protocol_data(gc); + ggp_chat_local_info *chat; + uin_t invited; + + chat = ggp_chat_get_local(gc, local_id); + if (!chat) + { + purple_debug_error("gg", "ggp_chat_invite: " + "chat %u doesn't exists\n", local_id); + return; + } + + invited = ggp_str_to_uin(who); + if (gg_chat_invite(info->session, chat->id, &invited, 1) < 0) + { + purple_debug_error("gg", "ggp_chat_invite: " + "unable to invite %s to chat %" G_GUINT64_FORMAT "\n", + who, chat->id); + } +} + +int ggp_chat_send(PurpleConnection *gc, int local_id, const char *message, + PurpleMessageFlags flags) +{ + GGPInfo *info = purple_connection_get_protocol_data(gc); + PurpleConversation *conv; + ggp_chat_local_info *chat; + gboolean succ = TRUE; + const gchar *me; + gchar *gg_msg; + + chat = ggp_chat_get_local(gc, local_id); + if (!chat) + { + purple_debug_error("gg", "ggp_chat_send: " + "chat %u doesn't exists\n", local_id); + return -1; + } + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, + ggp_chat_get_name_from_id(chat->id), + purple_connection_get_account(gc)); + + gg_msg = ggp_message_format_to_gg(conv, message); + + if (gg_chat_send_message(info->session, chat->id, gg_msg, TRUE) < 0) + succ = FALSE; + g_free(gg_msg); + + me = purple_account_get_username(purple_connection_get_account(gc)); + serv_got_chat_in(gc, chat->local_id, me, flags, message, time(NULL)); + + return succ ? 0 : -1; +} + +void ggp_chat_got_message(PurpleConnection *gc, uint64_t chat_id, + const char *message, time_t time, uin_t who) +{ + ggp_chat_local_info *chat; + uin_t me; + + me = ggp_str_to_uin(purple_account_get_username( + purple_connection_get_account(gc))); + + chat = ggp_chat_get(gc, chat_id); + if (!chat) + { + purple_debug_error("gg", "ggp_chat_got_message: " + "chat %" G_GUINT64_FORMAT " doesn't exists\n", chat_id); + return; + } + + ggp_chat_open_conv(chat); + if (who == me) + { + purple_conversation_write(chat->conv, ggp_uin_to_str(who), + message, PURPLE_MESSAGE_SEND, time); + } + else + { + serv_got_chat_in(gc, chat->local_id, ggp_uin_to_str(who), + PURPLE_MESSAGE_RECV, message, time); + } +} + +static gboolean ggp_chat_roomlist_get_list_finish(gpointer roomlist) +{ + purple_roomlist_set_in_progress((PurpleRoomlist*)roomlist, FALSE); + return FALSE; +} + +PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleConnection *gc) +{ + ggp_chat_session_data *sdata = ggp_chat_get_sdata(gc); + PurpleRoomlist *roomlist; + GList *fields = NULL; + int i; + + purple_debug_info("gg", "ggp_chat_roomlist_get_list\n"); + + roomlist = purple_roomlist_new(purple_connection_get_account(gc)); + + fields = g_list_append(fields, purple_roomlist_field_new( + PURPLE_ROOMLIST_FIELD_STRING, _("Conference identifier"), "id", + TRUE)); + + fields = g_list_append(fields, purple_roomlist_field_new( + PURPLE_ROOMLIST_FIELD_STRING, _("Start Date"), "date", + FALSE)); + + fields = g_list_append(fields, purple_roomlist_field_new( + PURPLE_ROOMLIST_FIELD_INT, _("User Count"), "users", + FALSE)); + + fields = g_list_append(fields, purple_roomlist_field_new( + PURPLE_ROOMLIST_FIELD_STRING, _("Status"), "status", + FALSE)); + + purple_roomlist_set_fields(roomlist, fields); + + for (i = sdata->chats_count - 1; i >= 0 ; i--) + { + PurpleRoomlistRoom *room; + ggp_chat_local_info *chat = &sdata->chats[i]; + const gchar *name; + time_t date; + const gchar *status; + int count = chat->participants_count; + + date = (uint32_t)(chat->id >> 32); + + if (chat->conv) + status = _("Joined"); + else if (chat->left) + status = _("Chat left"); + else + { + status = _("Can join chat"); + count--; + } + + name = ggp_chat_get_name_from_id(chat->id); + room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, + name, NULL); + purple_roomlist_room_add_field(roomlist, room, name); + purple_roomlist_room_add_field(roomlist, room, purple_date_format_full(localtime(&date))); + purple_roomlist_room_add_field(roomlist, room, GINT_TO_POINTER(count)); + purple_roomlist_room_add_field(roomlist, room, status); + purple_roomlist_room_add(roomlist, room); + } + + //TODO + //purple_roomlist_set_in_progress(roomlist, FALSE); + purple_timeout_add(1, ggp_chat_roomlist_get_list_finish, roomlist); + return roomlist; +} + +#else +void ggp_chat_setup(PurpleConnection *gc) +{ +} + +void ggp_chat_cleanup(PurpleConnection *gc) +{ +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/chat.h Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,34 @@ +#ifndef _GGP_CHAT_H +#define _GGP_CHAT_H + +#include <internal.h> +#include <libgadu.h> + +typedef struct _ggp_chat_session_data ggp_chat_session_data; + +#include "gg.h" + +void ggp_chat_setup(PurpleConnection *gc); +void ggp_chat_cleanup(PurpleConnection *gc); + +#if GGP_ENABLE_GG11 +void ggp_chat_got_event(PurpleConnection *gc, const struct gg_event *ev); + +GList * ggp_chat_info(PurpleConnection *gc); +GHashTable * ggp_chat_info_defaults(PurpleConnection *gc, + const char *chat_name); +char * ggp_chat_get_name(GHashTable *components); +void ggp_chat_join(PurpleConnection *gc, GHashTable *components); +void ggp_chat_leave(PurpleConnection *gc, int local_id); +void ggp_chat_invite(PurpleConnection *gc, int local_id, const char *message, + const char *who); +int ggp_chat_send(PurpleConnection *gc, int local_id, const char *message, + PurpleMessageFlags flags); + +void ggp_chat_got_message(PurpleConnection *gc, uint64_t chat_id, + const char *message, time_t time, uin_t who); + +PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleConnection *gc); +#endif + +#endif /* _GGP_CHAT_H */
--- a/libpurple/protocols/gg/confer.c Mon Sep 16 16:48:10 2013 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,169 +0,0 @@ -/** - * @file confer.c - * - * purple - * - * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - - -#include <libgadu.h> -#include "gg.h" -#include "utils.h" -#include "confer.h" - -/* PurpleConversation *ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name) {{{ */ -PurpleChatConversation *ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name) -{ - g_return_val_if_fail(gc != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - return purple_conversations_find_chat_with_account(name, - purple_connection_get_account(gc)); -} -/* }}} */ - -/* void ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name, const uin_t uin) {{{ */ -void ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name, - const uin_t uin) -{ - PurpleChatConversation *conv; - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPChat *chat; - GList *l; - gchar *str_uin; - - for (l = info->chats; l != NULL; l = l->next) { - chat = l->data; - - if (g_utf8_collate(chat->name, chat_name) != 0) - continue; - - if (g_list_find(chat->participants, GINT_TO_POINTER(uin)) == NULL) { - chat->participants = g_list_append( - chat->participants, GINT_TO_POINTER(uin)); - - str_uin = g_strdup_printf("%lu", (unsigned long int)uin); - conv = ggp_confer_find_by_name(gc, chat_name); - purple_chat_conversation_add_user(conv, str_uin, NULL, - PURPLE_CHAT_USER_NONE, TRUE); - - g_free(str_uin); - } - break; - } -} -/* }}} */ - -/* void ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name, const uin_t *recipients, int count) {{{ */ -void ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name, - const uin_t *recipients, int count) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - GList *l; - gchar *str_uin; - - for (l = info->chats; l != NULL; l = l->next) { - GGPChat *chat = l->data; - int i; - - if (g_utf8_collate(chat->name, chat_name) != 0) - continue; - - for (i = 0; i < count; i++) { - PurpleChatConversation *conv; - - if (g_list_find(chat->participants, - GINT_TO_POINTER(recipients[i])) != NULL) { - continue; - } - - chat->participants = g_list_append(chat->participants, - GINT_TO_POINTER(recipients[i])); - - str_uin = g_strdup_printf("%lu", (unsigned long int)recipients[i]); - conv = ggp_confer_find_by_name(gc, chat_name); - purple_chat_conversation_add_user(conv, str_uin, NULL, - PURPLE_CHAT_USER_NONE, TRUE); - g_free(str_uin); - } - break; - } -} -/* }}} */ - -/* const char *ggp_confer_find_by_participants(PurpleConnection *gc, const uin_t *recipients, int count) {{{ */ -const char *ggp_confer_find_by_participants(PurpleConnection *gc, - const uin_t *recipients, int count) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPChat *chat = NULL; - GList *l; - int matches; - - g_return_val_if_fail(info->chats != NULL, NULL); - - for (l = info->chats; l != NULL; l = l->next) { - GList *m; - - chat = l->data; - matches = 0; - - for (m = chat->participants; m != NULL; m = m->next) { - uin_t uin = GPOINTER_TO_INT(m->data); - int i; - - for (i = 0; i < count; i++) - if (uin == recipients[i]) - matches++; - } - - if (matches == count) - break; - - chat = NULL; - } - - if (chat == NULL) - return NULL; - else - return chat->name; -} -/* }}} */ - -/* const char *ggp_confer_add_new(PurpleConnection *gc, const char *name) {{{ */ -const char *ggp_confer_add_new(PurpleConnection *gc, const char *name) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPChat *chat; - - chat = g_new0(GGPChat, 1); - - if (name == NULL) - chat->name = g_strdup_printf("conf#%d", info->chats_count++); - else - chat->name = g_strdup(name); - - chat->participants = NULL; - - info->chats = g_list_append(info->chats, chat); - - return chat->name; -} -/* }}} */ - -/* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/confer.h Mon Sep 16 16:48:10 2013 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/** - * @file confer.h - * - * purple - * - * Copyright (C) 2005 Bartosz Oler <bartosz@bzimage.us> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - - -#ifndef _PURPLE_GG_CONFER_H -#define _PURPLE_GG_CONFER_H - -#include "gg.h" - -/** - * Finds a CHAT conversation for the current account with the specified name. - * - * @param gc PurpleConnection instance. - * @param name Name of the conversation. - * - * @return PurpleConversation or NULL if not found. - */ -PurpleChatConversation * -ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name); - -/** - * Adds the specified UIN to the specified conversation. - * - * @param gc PurpleConnection. - * @param chat_name Name of the conversation. - */ -void -ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name, - const uin_t uin); - -/** - * Add the specified UINs to the specified conversation. - * - * @param gc PurpleConnection. - * @param chat_name Name of the conversation. - * @param recipients List of the UINs. - * @param count Number of the UINs. - */ -void -ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name, - const uin_t *recipients, int count); - -/** - * Finds a conversation in which all the specified recipients participate. - * - * TODO: This function should be rewritten to better handle situations when - * somebody adds more people to the converation. - * - * @param gc PurpleConnection. - * @param recipients List of the people in the conversation. - * @param count Number of people. - * - * @return Name of the conversation. - */ -const char* -ggp_confer_find_by_participants(PurpleConnection *gc, const uin_t *recipients, - int count); - -/** - * Adds a new conversation to the internal list of conversations. - * If name is NULL then it will be automagically generated. - * - * @param gc PurpleConnection. - * @param name Name of the conversation. - * - * @return Name of the conversation. - */ -const char* -ggp_confer_add_new(PurpleConnection *gc, const char *name); - - -#endif /* _PURPLE_GG_CONFER_H */ - -/* vim: set ts=8 sts=0 sw=8 noet: */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/edisc.c Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,1175 @@ +#include "edisc.h" + +#include <debug.h> + +#include "gg.h" +#include "libgaduw.h" +#include "utils.h" +#include <http.h> + +#include <json-glib/json-glib.h> + +#define GGP_EDISC_OS "WINNT x86-msvc" +#define GGP_EDISC_TYPE "desktop" +#define GGP_EDISC_API "6" + +#define GGP_EDISC_RESPONSE_MAX 10240 +#define GGP_EDISC_FNAME_ALLOWED "1234567890" \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + " [](){}-+=_;'<>,.&$!" + +typedef struct _ggp_edisc_auth_data ggp_edisc_auth_data; + +typedef struct _ggp_edisc_xfer ggp_edisc_xfer; + +struct _ggp_edisc_session_data +{ + GHashTable *xfers_initialized; + GHashTable *xfers_history; + + PurpleHttpCookieJar *cookies; + gchar *security_token; + + PurpleHttpConnection *auth_request; + gboolean auth_done; + GList *auth_pending; +}; + +struct _ggp_edisc_xfer +{ + gchar *filename; + gchar *ticket_id; + + gboolean allowed, ready; + + PurpleConnection *gc; + PurpleHttpConnection *hc; + + gsize already_read; +}; + +typedef enum +{ + GGP_EDISC_XFER_ACK_STATUS_UNKNOWN, + GGP_EDISC_XFER_ACK_STATUS_ALLOWED, + GGP_EDISC_XFER_ACK_STATUS_REJECTED +} ggp_edisc_xfer_ack_status; + +typedef void (*ggp_ggdrive_auth_cb)(PurpleConnection *gc, gboolean success, + gpointer user_data); + +/* Setting up. */ +static inline ggp_edisc_session_data * +ggp_edisc_get_sdata(PurpleConnection *gc); + +/* Misc. */ +static void ggp_edisc_set_defaults(PurpleHttpRequest *req); +static int ggp_edisc_parse_error(const gchar *data); + +/* General xfer functions. */ +static void ggp_edisc_xfer_free(PurpleXfer *xfer); +static void ggp_edisc_xfer_error(PurpleXfer *xfer, const gchar *msg); +static void ggp_edisc_xfer_cancel(PurpleXfer *xfer); +static const gchar * ggp_edisc_xfer_ticket_url(const gchar *ticket_id); +static void ggp_edisc_xfer_progress_watcher(PurpleHttpConnection *hc, + gboolean reading_state, int processed, int total, gpointer _xfer); +static ggp_edisc_xfer_ack_status +ggp_edisc_xfer_parse_ack_status(const gchar *str); + + +/* Sending a file. */ +static void ggp_edisc_xfer_send_ticket_changed(PurpleConnection *gc, + PurpleXfer *xfer, gboolean is_allowed); + +/* Receiving a file. */ +static void ggp_edisc_xfer_recv_reject(PurpleXfer *xfer); +static void ggp_edisc_xfer_recv_ticket_got(PurpleConnection *gc, + const gchar *ticket_id); +static void ggp_edisc_xfer_recv_ticket_completed(PurpleXfer *xfer); + +/* Authentication. */ +static void ggp_ggdrive_auth(PurpleConnection *gc, ggp_ggdrive_auth_cb cb, + gpointer user_data); + +/******************************************************************************* + * Setting up. + ******************************************************************************/ + +static inline ggp_edisc_session_data * +ggp_edisc_get_sdata(PurpleConnection *gc) +{ + GGPInfo *accdata; + + g_return_val_if_fail(PURPLE_CONNECTION_IS_VALID(gc), NULL); + + accdata = purple_connection_get_protocol_data(gc); + g_return_val_if_fail(accdata != NULL, NULL); + + return accdata->edisc_data; +} + +void ggp_edisc_setup(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + ggp_edisc_session_data *sdata = g_new0(ggp_edisc_session_data, 1); + + accdata->edisc_data = sdata; + + sdata->cookies = purple_http_cookie_jar_new(); + sdata->xfers_initialized = g_hash_table_new(g_str_hash, g_str_equal); + sdata->xfers_history = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); +} + +void ggp_edisc_cleanup(PurpleConnection *gc) +{ + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc); + + purple_http_conn_cancel(sdata->auth_request); + g_list_free_full(sdata->auth_pending, g_free); + g_free(sdata->security_token); + + purple_http_cookie_jar_unref(sdata->cookies); + g_hash_table_destroy(sdata->xfers_initialized); + g_hash_table_destroy(sdata->xfers_history); + + g_free(sdata); +} + +/******************************************************************************* + * Misc. + ******************************************************************************/ + +static void ggp_edisc_set_defaults(PurpleHttpRequest *req) +{ + purple_http_request_set_max_len(req, GGP_EDISC_RESPONSE_MAX); + purple_http_request_header_set(req, "X-gged-api-version", + GGP_EDISC_API); + + /* optional fields */ + purple_http_request_header_set(req, "User-Agent", "Mozilla/5.0 (Windows" + " NT 6.1; rv:11.0) Gecko/20120613 GG/11.0.0.8169 (WINNT_x86-msv" + "c; pl; beta; standard)"); + purple_http_request_header_set(req, "Accept", "text/html,application/xh" + "tml+xml,application/xml;q=0.9,*/*;q=0.8"); + purple_http_request_header_set(req, "Accept-Language", + "pl,en-us;q=0.7,en;q=0.3"); + /* purple_http_request_header_set(req, "Accept-Encoding", + * "gzip, deflate"); */ + purple_http_request_header_set(req, "Accept-Charset", + "ISO-8859-2,utf-8;q=0.7,*;q=0.7"); + purple_http_request_header_set(req, "Connection", "keep-alive"); + purple_http_request_header_set(req, "Content-Type", + "application/x-www-form-urlencoded; charset=UTF-8"); +} + +static int ggp_edisc_parse_error(const gchar *data) +{ + JsonParser *parser; + JsonObject *result; + int error_id; + + parser = ggp_json_parse(data); + result = json_node_get_object(json_parser_get_root(parser)); + result = json_object_get_object_member(result, "result"); + error_id = json_object_get_int_member(result, "appStatus"); + purple_debug_info("gg", "edisc error: %s (%d)\n", + json_object_get_string_member(result, "errorMsg"), + error_id); + g_object_unref(parser); + + return error_id; +} + +/******************************************************************************* + * General xfer functions. + ******************************************************************************/ + +static void ggp_edisc_xfer_free(PurpleXfer *xfer) +{ + ggp_edisc_session_data *sdata; + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + + if (edisc_xfer == NULL) + return; + + g_free(edisc_xfer->filename); + purple_http_conn_cancel(edisc_xfer->hc); + + sdata = ggp_edisc_get_sdata(edisc_xfer->gc); + if (edisc_xfer->ticket_id != NULL) + g_hash_table_remove(sdata->xfers_initialized, + edisc_xfer->ticket_id); + + g_free(edisc_xfer); + purple_xfer_set_protocol_data(xfer, NULL); + +} + +static void ggp_edisc_xfer_error(PurpleXfer *xfer, const gchar *msg) +{ + if (purple_xfer_is_cancelled(xfer)) + g_return_if_reached(); + purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE); + purple_xfer_conversation_write(xfer, msg, TRUE); + purple_xfer_error( + purple_xfer_get_xfer_type(xfer), + purple_xfer_get_account(xfer), + purple_xfer_get_remote_user(xfer), + msg); + ggp_edisc_xfer_free(xfer); + purple_xfer_end(xfer); +} + +void ggp_edisc_xfer_ticket_changed(PurpleConnection *gc, const char *data) +{ + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc); + PurpleXfer *xfer; + JsonParser *parser; + JsonObject *ticket; + const gchar *ticket_id, *send_status; + ggp_edisc_xfer_ack_status ack_status; + gboolean is_completed; + + parser = ggp_json_parse(data); + ticket = json_node_get_object(json_parser_get_root(parser)); + ticket_id = json_object_get_string_member(ticket, "id"); + ack_status = ggp_edisc_xfer_parse_ack_status( + json_object_get_string_member(ticket, "ack_status")); + send_status = json_object_get_string_member(ticket, "send_status"); + + if (ticket_id == NULL) + ticket_id = ""; + xfer = g_hash_table_lookup(sdata->xfers_initialized, ticket_id); + if (xfer == NULL) { + purple_debug_misc("gg", "ggp_edisc_event_ticket_changed: " + "ticket %s not found, updating it...\n", + purple_debug_is_unsafe() ? ticket_id : ""); + ggp_edisc_xfer_recv_ticket_got(gc, ticket_id); + g_object_unref(parser); + return; + } + + is_completed = FALSE; + if (g_strcmp0("in_progress", send_status) == 0) { + /* do nothing */ + } else if (g_strcmp0("completed", send_status) == 0) { + is_completed = TRUE; + } else if (g_strcmp0("expired", send_status) == 0) + ggp_edisc_xfer_error(xfer, _("File transfer expired.")); + else { + purple_debug_warning("gg", "ggp_edisc_event_ticket_changed: " + "unknown send_status=%s\n", send_status); + g_object_unref(parser); + return; + } + + g_object_unref(parser); + + if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) { + if (is_completed) + ggp_edisc_xfer_recv_ticket_completed(xfer); + } else { + if (ack_status != GGP_EDISC_XFER_ACK_STATUS_UNKNOWN) + ggp_edisc_xfer_send_ticket_changed(gc, xfer, ack_status + == GGP_EDISC_XFER_ACK_STATUS_ALLOWED); + } + +} + +static void ggp_edisc_xfer_cancel(PurpleXfer *xfer) +{ + g_return_if_fail(xfer != NULL); + + ggp_edisc_xfer_free(xfer); +} + +static const gchar * ggp_edisc_xfer_ticket_url(const gchar *ticket_id) +{ + static gchar ticket_url[150]; + + g_snprintf(ticket_url, sizeof(ticket_url), + "https://drive.mpa.gg.pl/send_ticket/%s", ticket_id); + + return ticket_url; +} + +static void ggp_edisc_xfer_progress_watcher(PurpleHttpConnection *hc, + gboolean reading_state, int processed, int total, gpointer _xfer) +{ + PurpleXfer *xfer = _xfer; + gboolean eof; + int total_real; + + if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) { + if (!reading_state) + return; + } else { + if (reading_state) + return; + } + + eof = (processed >= total); + total_real = purple_xfer_get_size(xfer); + if (eof || processed > total_real) + processed = total_real; /* just to be sure */ + + purple_xfer_set_bytes_sent(xfer, processed); + purple_xfer_update_progress(xfer); +} + +static ggp_edisc_xfer_ack_status +ggp_edisc_xfer_parse_ack_status(const gchar *str) +{ + g_return_val_if_fail(str != NULL, GGP_EDISC_XFER_ACK_STATUS_UNKNOWN); + + if (g_strcmp0("unknown", str) == 0) + return GGP_EDISC_XFER_ACK_STATUS_UNKNOWN; + if (g_strcmp0("allowed", str) == 0) + return GGP_EDISC_XFER_ACK_STATUS_ALLOWED; + if (g_strcmp0("rejected", str) == 0) + return GGP_EDISC_XFER_ACK_STATUS_REJECTED; + + purple_debug_warning("gg", "ggp_edisc_xfer_parse_ack_status: " + "unknown status (%s)\n", str); + return GGP_EDISC_XFER_ACK_STATUS_UNKNOWN; +} + +/******************************************************************************* + * Sending a file. + ******************************************************************************/ + +static void ggp_edisc_xfer_send_init(PurpleXfer *xfer); +static void ggp_edisc_xfer_send_init_authenticated(PurpleConnection *gc, + gboolean success, gpointer _xfer); +static void ggp_edisc_xfer_send_init_ticket_created(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer _xfer); +static void ggp_edisc_xfer_send_reader(PurpleHttpConnection *hc, + gchar *buffer, size_t offset, size_t length, gpointer _xfer, + PurpleHttpContentReaderCb cb); +static void ggp_edisc_xfer_send_start(PurpleXfer *xfer); +static void ggp_edisc_xfer_send_done(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer _xfer); + +gboolean ggp_edisc_xfer_can_receive_file(PurpleConnection *gc, + const char *who) +{ + PurpleBuddy *buddy; + + g_return_val_if_fail(gc != NULL, FALSE); + g_return_val_if_fail(who != NULL, FALSE); + + buddy = purple_blist_find_buddy(purple_connection_get_account(gc), who); + if (buddy == NULL) + return FALSE; + + /* TODO: check, if this buddy have us on his list */ + + return PURPLE_BUDDY_IS_ONLINE(buddy); +} + +static void ggp_edisc_xfer_send_init(PurpleXfer *xfer) +{ + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + + purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_NOT_STARTED); + + edisc_xfer->filename = g_strdup(purple_xfer_get_filename(xfer)); + g_strcanon(edisc_xfer->filename, GGP_EDISC_FNAME_ALLOWED, '_'); + + ggp_ggdrive_auth(edisc_xfer->gc, ggp_edisc_xfer_send_init_authenticated, + xfer); +} + +static void ggp_edisc_xfer_send_init_authenticated(PurpleConnection *gc, + gboolean success, gpointer _xfer) +{ + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc); + PurpleHttpRequest *req; + PurpleXfer *xfer = _xfer; + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + gchar *data; + + if (purple_xfer_is_cancelled(xfer)) + return; + + if (!success) { + ggp_edisc_xfer_error(xfer, _("Authentication failed")); + return; + } + + req = purple_http_request_new("https://drive.mpa.gg.pl/send_ticket"); + purple_http_request_set_method(req, "PUT"); + + ggp_edisc_set_defaults(req); + purple_http_request_set_cookie_jar(req, sdata->cookies); + + purple_http_request_header_set(req, "X-gged-security-token", + sdata->security_token); + + data = g_strdup_printf("{\"send_ticket\":{" + "\"recipient\":\"%s\"," + "\"file_name\":\"%s\"," + "\"file_size\":\"%u\"" + "}}", + purple_xfer_get_remote_user(xfer), + edisc_xfer->filename, + (int)purple_xfer_get_size(xfer)); + purple_http_request_set_contents(req, data, -1); + g_free(data); + + edisc_xfer->hc = purple_http_request(gc, req, + ggp_edisc_xfer_send_init_ticket_created, xfer); + purple_http_request_unref(req); +} + +static void ggp_edisc_xfer_send_init_ticket_created(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer _xfer) +{ + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata( + purple_http_conn_get_purple_connection(hc)); + PurpleXfer *xfer = _xfer; + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + const gchar *data = purple_http_response_get_data(response, NULL); + ggp_edisc_xfer_ack_status ack_status; + JsonParser *parser; + JsonObject *ticket; + + if (purple_xfer_is_cancelled(xfer)) + return; + + edisc_xfer->hc = NULL; + + if (!purple_http_response_is_successful(response)) { + int error_id = ggp_edisc_parse_error(data); + if (error_id == 206) /* recipient not logged in */ + ggp_edisc_xfer_error(xfer, + _("Recipient not logged in")); + else if (error_id == 207) /* bad sender recipient relation */ + ggp_edisc_xfer_error(xfer, _("Recipient didn't added " + "you to his buddy list")); + else + ggp_edisc_xfer_error(xfer, + _("Cannot offer sending a file")); + return; + } + + parser = ggp_json_parse(data); + ticket = json_node_get_object(json_parser_get_root(parser)); + ticket = json_object_get_object_member(ticket, "result"); + ticket = json_object_get_object_member(ticket, "send_ticket"); + edisc_xfer->ticket_id = g_strdup(json_object_get_string_member( + ticket, "id")); + ack_status = ggp_edisc_xfer_parse_ack_status( + json_object_get_string_member(ticket, "ack_status")); + /* send_mode: "normal", "publink" (for legacy clients) */ + + g_object_unref(parser); + + if (edisc_xfer->ticket_id == NULL) { + purple_debug_error("gg", + "ggp_edisc_xfer_send_init_ticket_created: " + "couldn't get ticket id\n"); + return; + } + + purple_debug_info("gg", "ggp_edisc_xfer_send_init_ticket_created: " + "ticket \"%s\" created\n", edisc_xfer->ticket_id); + + g_hash_table_insert(sdata->xfers_initialized, + edisc_xfer->ticket_id, xfer); + g_hash_table_insert(sdata->xfers_history, + g_strdup(edisc_xfer->ticket_id), GINT_TO_POINTER(1)); + + if (ack_status != GGP_EDISC_XFER_ACK_STATUS_UNKNOWN) + ggp_edisc_xfer_send_ticket_changed(edisc_xfer->gc, xfer, + ack_status == GGP_EDISC_XFER_ACK_STATUS_ALLOWED); +} + +void ggp_edisc_xfer_send_ticket_changed(PurpleConnection *gc, PurpleXfer *xfer, + gboolean is_allowed) +{ + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + if (!edisc_xfer) { + purple_debug_fatal("gg", "ggp_edisc_event_ticket_changed: " + "transfer %p already free'd\n", xfer); + return; + } + + if (!is_allowed) { + purple_debug_info("gg", "ggp_edisc_event_ticket_changed: " + "transfer %p rejected\n", xfer); + purple_xfer_cancel_remote(xfer); + ggp_edisc_xfer_free(xfer); + return; + } + + if (edisc_xfer->allowed) { + purple_debug_misc("gg", "ggp_edisc_event_ticket_changed: " + "transfer %p already allowed\n", xfer); + return; + } + edisc_xfer->allowed = TRUE; + + purple_xfer_start(xfer, -1, NULL, 0); +} + +static void ggp_edisc_xfer_send_reader(PurpleHttpConnection *hc, + gchar *buffer, size_t offset, size_t length, gpointer _xfer, + PurpleHttpContentReaderCb cb) +{ + PurpleXfer *xfer = _xfer; + ggp_edisc_xfer *edisc_xfer; + int stored; + gboolean success, eof = FALSE; + + g_return_if_fail(xfer != NULL); + edisc_xfer = purple_xfer_get_protocol_data(xfer); + g_return_if_fail(edisc_xfer != NULL); + + if (edisc_xfer->already_read != offset) { + purple_debug_error("gg", "ggp_edisc_xfer_send_reader: " + "Invalid offset (%d != %" G_GSIZE_FORMAT ")\n", + edisc_xfer->already_read, offset); + ggp_edisc_xfer_error(xfer, _("Error while reading a file")); + return; + } + + stored = purple_xfer_read_file(xfer, (guchar *)buffer, length); + + if (stored < 0) + success = FALSE; + else { + success = TRUE; + edisc_xfer->already_read += stored; + eof = (edisc_xfer->already_read >= purple_xfer_get_size(xfer)); + } + + cb(hc, success, eof, stored); +} + +static void ggp_edisc_xfer_send_start(PurpleXfer *xfer) +{ + ggp_edisc_session_data *sdata; + ggp_edisc_xfer *edisc_xfer; + gchar *upload_url, *filename_e; + PurpleHttpRequest *req; + + g_return_if_fail(xfer != NULL); + edisc_xfer = purple_xfer_get_protocol_data(xfer); + g_return_if_fail(edisc_xfer != NULL); + sdata = ggp_edisc_get_sdata(edisc_xfer->gc); + + filename_e = purple_strreplace(edisc_xfer->filename, " ", "%20"); + upload_url = g_strdup_printf("https://drive.mpa.gg.pl/me/file/outbox/" + "%s%%2C%s", edisc_xfer->ticket_id, filename_e); + g_free(filename_e); + req = purple_http_request_new(upload_url); + g_free(upload_url); + + purple_http_request_set_method(req, "PUT"); + purple_http_request_set_timeout(req, -1); + + ggp_edisc_set_defaults(req); + purple_http_request_set_cookie_jar(req, sdata->cookies); + + purple_http_request_header_set(req, "X-gged-local-revision", "0"); + purple_http_request_header_set(req, "X-gged-security-token", + sdata->security_token); + purple_http_request_header_set(req, "X-gged-metadata", + "{\"node_type\": \"file\"}"); + + purple_http_request_set_contents_reader(req, ggp_edisc_xfer_send_reader, + purple_xfer_get_size(xfer), xfer); + + edisc_xfer->hc = purple_http_request(edisc_xfer->gc, req, + ggp_edisc_xfer_send_done, xfer); + purple_http_request_unref(req); + + purple_http_conn_set_progress_watcher(edisc_xfer->hc, + ggp_edisc_xfer_progress_watcher, xfer, 250000); +} + +static void ggp_edisc_xfer_send_done(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer _xfer) +{ + PurpleXfer *xfer = _xfer; + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + const gchar *data = purple_http_response_get_data(response, NULL); + JsonParser *parser; + JsonObject *result; + int result_status = -1; + + if (purple_xfer_is_cancelled(xfer)) + return; + + g_return_if_fail(edisc_xfer != NULL); + + edisc_xfer->hc = NULL; + + if (!purple_http_response_is_successful(response)) { + ggp_edisc_xfer_error(xfer, _("Error while sending a file")); + return; + } + + parser = ggp_json_parse(data); + result = json_node_get_object(json_parser_get_root(parser)); + result = json_object_get_object_member(result, "result"); + if (json_object_has_member(result, "status")) + result_status = json_object_get_int_member(result, "status"); + g_object_unref(parser); + + if (result_status == 0) { + purple_xfer_set_completed(xfer, TRUE); + purple_xfer_end(xfer); + ggp_edisc_xfer_free(xfer); + } else + ggp_edisc_xfer_error(xfer, _("Error while sending a file")); +} + +PurpleXfer * ggp_edisc_xfer_send_new(PurpleConnection *gc, const char *who) +{ + PurpleXfer *xfer; + ggp_edisc_xfer *edisc_xfer; + + g_return_val_if_fail(gc != NULL, NULL); + g_return_val_if_fail(who != NULL, NULL); + + xfer = purple_xfer_new(purple_connection_get_account(gc), + PURPLE_XFER_TYPE_SEND, who); + edisc_xfer = g_new0(ggp_edisc_xfer, 1); + purple_xfer_set_protocol_data(xfer, edisc_xfer); + + edisc_xfer->gc = gc; + + purple_xfer_set_init_fnc(xfer, ggp_edisc_xfer_send_init); + purple_xfer_set_start_fnc(xfer, ggp_edisc_xfer_send_start); + purple_xfer_set_cancel_send_fnc(xfer, ggp_edisc_xfer_cancel); + + return xfer; +} + +void ggp_edisc_xfer_send_file(PurpleConnection *gc, const char *who, + const char *filename) +{ + PurpleXfer *xfer; + + g_return_if_fail(gc != NULL); + g_return_if_fail(who != NULL); + + /* Nothing interesting here, this code is common among prpls. + * See ggp_edisc_xfer_send_new. */ + + xfer = ggp_edisc_xfer_send_new(gc, who); + if (filename) + purple_xfer_request_accepted(xfer, filename); + else + purple_xfer_request(xfer); +} + +/******************************************************************************* + * Receiving a file. + ******************************************************************************/ + +static void ggp_edisc_xfer_recv_ticket_update_authenticated( + PurpleConnection *gc, gboolean success, gpointer _ticket); +static void ggp_edisc_xfer_recv_ticket_update_got(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer user_data); +static PurpleXfer * ggp_edisc_xfer_recv_new(PurpleConnection *gc, + const char *who); +static void ggp_edisc_xfer_recv_accept(PurpleXfer *xfer); +static void ggp_edisc_xfer_recv_start(PurpleXfer *xfer); +static void ggp_edisc_xfer_recv_ack(PurpleXfer *xfer, gboolean accept); +static void ggp_edisc_xfer_recv_ack_done(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer _xfer); +static gboolean ggp_edisc_xfer_recv_writer(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, const gchar *buffer, size_t offset, + size_t length, gpointer user_data); +static void ggp_edisc_xfer_recv_done(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer _xfer); + +static void ggp_edisc_xfer_recv_ticket_got(PurpleConnection *gc, + const gchar *ticket_id) +{ + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc); + + if (g_hash_table_lookup(sdata->xfers_history, ticket_id)) + return; + + ggp_ggdrive_auth(gc, ggp_edisc_xfer_recv_ticket_update_authenticated, + g_strdup(ticket_id)); +} + +static void ggp_edisc_xfer_recv_ticket_update_authenticated( + PurpleConnection *gc, gboolean success, gpointer _ticket) +{ + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc); + PurpleHttpRequest *req; + gchar *ticket = _ticket; + + if (!success) { + purple_debug_warning("gg", + "ggp_edisc_xfer_recv_ticket_update_authenticated: " + "update of ticket %s aborted due to authentication " + "failure\n", ticket); + g_free(ticket); + return; + } + + req = purple_http_request_new(ggp_edisc_xfer_ticket_url(ticket)); + g_free(ticket); + + ggp_edisc_set_defaults(req); + purple_http_request_set_cookie_jar(req, sdata->cookies); + + purple_http_request_header_set(req, "X-gged-security-token", + sdata->security_token); + + purple_http_request(gc, req, ggp_edisc_xfer_recv_ticket_update_got, + NULL); + purple_http_request_unref(req); +} + +static void ggp_edisc_xfer_recv_ticket_update_got(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer user_data) +{ + PurpleConnection *gc = purple_http_conn_get_purple_connection(hc); + PurpleXfer *xfer; + ggp_edisc_xfer *edisc_xfer; + JsonParser *parser; + JsonObject *result; + int status = -1; + ggp_edisc_session_data *sdata; + + const gchar *ticket_id, *file_name, *send_mode_str; + uin_t sender, recipient; + int file_size; + + if (!purple_http_response_is_successful(response)) { + purple_debug_error("gg", + "ggp_edisc_xfer_recv_ticket_update_got: " + "cannot fetch update for ticket (code=%d)\n", + purple_http_response_get_code(response)); + return; + } + + sdata = ggp_edisc_get_sdata(gc); + + parser = ggp_json_parse(purple_http_response_get_data(response, NULL)); + result = json_node_get_object(json_parser_get_root(parser)); + result = json_object_get_object_member(result, "result"); + if (json_object_has_member(result, "status")) + status = json_object_get_int_member(result, "status"); + result = json_object_get_object_member(result, "send_ticket"); + + if (status != 0) { + purple_debug_warning("gg", + "ggp_edisc_xfer_recv_ticket_update_got: failed to get " + "update (status=%d)\n", status); + g_object_unref(parser); + return; + } + + ticket_id = json_object_get_string_member(result, "id"); + sender = ggp_str_to_uin(json_object_get_string_member(result, + "sender")); + recipient = ggp_str_to_uin(json_object_get_string_member(result, + "recipient")); + file_size = g_ascii_strtoll(json_object_get_string_member(result, + "file_size"), NULL, 10); + file_name = json_object_get_string_member(result, "file_name"); + + /* GG11: normal + * AQQ 2.4.2.10: direct_inbox + */ + send_mode_str = json_object_get_string_member(result, "send_mode"); + + /* more fields: + * send_progress (float), ack_status, send_status + */ + + if (purple_debug_is_verbose() && purple_debug_is_unsafe()) + purple_debug_info("gg", "Got ticket update: id=%s, sender=%u, " + "recipient=%u, file name=\"%s\", file size=%d, " + "send mode=%s)\n", + ticket_id, + sender, recipient, + file_name, file_size, + send_mode_str); + + xfer = g_hash_table_lookup(sdata->xfers_initialized, ticket_id); + if (xfer != NULL) { + purple_debug_misc("gg", "ggp_edisc_xfer_recv_ticket_update_got:" + " ticket %s already updated\n", + purple_debug_is_unsafe() ? ticket_id : ""); + g_object_unref(parser); + return; + } + + if (recipient != ggp_get_my_uin(gc)) { + purple_debug_misc("gg", "ggp_edisc_xfer_recv_ticket_update_got:" + " ticket %s is not for incoming transfer " + "(its from %u to %u)\n", + purple_debug_is_unsafe() ? ticket_id : "", + sender, recipient); + g_object_unref(parser); + return; + } + + xfer = ggp_edisc_xfer_recv_new(gc, ggp_uin_to_str(sender)); + purple_xfer_set_filename(xfer, file_name); + purple_xfer_set_size(xfer, file_size); + purple_xfer_request(xfer); + edisc_xfer = purple_xfer_get_protocol_data(xfer); + edisc_xfer->ticket_id = g_strdup(ticket_id); + g_hash_table_insert(sdata->xfers_initialized, + edisc_xfer->ticket_id, xfer); + g_hash_table_insert(sdata->xfers_history, + g_strdup(ticket_id), GINT_TO_POINTER(1)); + + g_object_unref(parser); +} + +static PurpleXfer * ggp_edisc_xfer_recv_new(PurpleConnection *gc, + const char *who) +{ + PurpleXfer *xfer; + ggp_edisc_xfer *edisc_xfer; + + g_return_val_if_fail(gc != NULL, NULL); + g_return_val_if_fail(who != NULL, NULL); + + xfer = purple_xfer_new(purple_connection_get_account(gc), + PURPLE_XFER_TYPE_RECEIVE, who); + edisc_xfer = g_new0(ggp_edisc_xfer, 1); + purple_xfer_set_protocol_data(xfer, edisc_xfer); + + edisc_xfer->gc = gc; + + purple_xfer_set_init_fnc(xfer, ggp_edisc_xfer_recv_accept); + purple_xfer_set_request_denied_fnc(xfer, ggp_edisc_xfer_recv_reject); + purple_xfer_set_start_fnc(xfer, ggp_edisc_xfer_recv_start); + purple_xfer_set_cancel_recv_fnc(xfer, ggp_edisc_xfer_cancel); + + return xfer; +} + +static void ggp_edisc_xfer_recv_reject(PurpleXfer *xfer) +{ + ggp_edisc_xfer_recv_ack(xfer, FALSE); +} + +static void ggp_edisc_xfer_recv_accept(PurpleXfer *xfer) +{ + ggp_edisc_xfer_recv_ack(xfer, TRUE); +} + +static void ggp_edisc_xfer_recv_ack(PurpleXfer *xfer, gboolean accept) +{ + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(edisc_xfer->gc); + PurpleHttpRequest *req; + + edisc_xfer->allowed = accept; + + req = purple_http_request_new(ggp_edisc_xfer_ticket_url( + edisc_xfer->ticket_id)); + purple_http_request_set_method(req, "PUT"); + + ggp_edisc_set_defaults(req); + purple_http_request_set_cookie_jar(req, sdata->cookies); + purple_http_request_header_set(req, "X-gged-security-token", + sdata->security_token); + + purple_http_request_header_set(req, "X-gged-ack-status", + accept ? "allow" : "reject"); + + edisc_xfer->hc = purple_http_request(edisc_xfer->gc, req, + accept ? ggp_edisc_xfer_recv_ack_done : NULL, xfer); + purple_http_request_unref(req); + + if (!accept) { + edisc_xfer->hc = NULL; + ggp_edisc_xfer_free(xfer); + } +} + +static void ggp_edisc_xfer_recv_ack_done(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer _xfer) +{ + PurpleXfer *xfer = _xfer; + ggp_edisc_xfer *edisc_xfer; + + if (purple_xfer_is_cancelled(xfer)) + g_return_if_reached(); + + edisc_xfer = purple_xfer_get_protocol_data(xfer); + edisc_xfer->hc = NULL; + + if (!purple_http_response_is_successful(response)) { + ggp_edisc_xfer_error(xfer, _("Cannot confirm file transfer.")); + return; + } + + purple_debug_info("gg", "ggp_edisc_xfer_recv_ack_done: [%s]\n", + purple_http_response_get_data(response, NULL)); +} + +static void ggp_edisc_xfer_recv_ticket_completed(PurpleXfer *xfer) +{ + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + + if (edisc_xfer->ready) + return; + edisc_xfer->ready = TRUE; + + purple_xfer_start(xfer, -1, NULL, 0); +} + +static void ggp_edisc_xfer_recv_start(PurpleXfer *xfer) +{ + ggp_edisc_session_data *sdata; + ggp_edisc_xfer *edisc_xfer; + gchar *upload_url; + PurpleHttpRequest *req; + + g_return_if_fail(xfer != NULL); + edisc_xfer = purple_xfer_get_protocol_data(xfer); + g_return_if_fail(edisc_xfer != NULL); + sdata = ggp_edisc_get_sdata(edisc_xfer->gc); + + upload_url = g_strdup_printf("https://drive.mpa.gg.pl/me/file/inbox/" + "%s,%s?api_version=%s&security_token=%s", + edisc_xfer->ticket_id, purple_url_encode(purple_xfer_get_filename(xfer)), + GGP_EDISC_API, sdata->security_token); + req = purple_http_request_new(upload_url); + g_free(upload_url); + + purple_http_request_set_timeout(req, -1); + + ggp_edisc_set_defaults(req); + purple_http_request_set_max_len(req, purple_xfer_get_size(xfer) + 1); + purple_http_request_set_cookie_jar(req, sdata->cookies); + + purple_http_request_set_response_writer(req, ggp_edisc_xfer_recv_writer, + xfer); + + edisc_xfer->hc = purple_http_request(edisc_xfer->gc, req, + ggp_edisc_xfer_recv_done, xfer); + purple_http_request_unref(req); + + purple_http_conn_set_progress_watcher(edisc_xfer->hc, + ggp_edisc_xfer_progress_watcher, xfer, 250000); +} + +static gboolean ggp_edisc_xfer_recv_writer(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, const gchar *buffer, size_t offset, + size_t length, gpointer _xfer) +{ + PurpleXfer *xfer = _xfer; + ggp_edisc_xfer *edisc_xfer; + gssize stored; + + g_return_val_if_fail(xfer != NULL, FALSE); + edisc_xfer = purple_xfer_get_protocol_data(xfer); + g_return_val_if_fail(edisc_xfer != NULL, FALSE); + + stored = purple_xfer_write_file(xfer, (guchar *)buffer, length) ? + length : -1; + + if (stored < 0 || (gsize)stored != length) { + purple_debug_error("gg", "ggp_edisc_xfer_recv_writer: " + "saved too less\n"); + return FALSE; + } + + if (stored > purple_xfer_get_bytes_remaining(xfer)) { + purple_debug_error("gg", "ggp_edisc_xfer_recv_writer: " + "saved too much (%d > %d)\n", + stored, (int)purple_xfer_get_bytes_remaining(xfer)); + return FALSE; + } + + /* May look redundant with ggp_edisc_xfer_progress_watcher, + * but it isn't! + */ + purple_xfer_set_bytes_sent(xfer, + purple_xfer_get_bytes_sent(xfer) + stored); + + return TRUE; +} + +static void ggp_edisc_xfer_recv_done(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer _xfer) +{ + PurpleXfer *xfer = _xfer; + ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer); + + if (purple_xfer_is_cancelled(xfer)) + return; + + g_return_if_fail(edisc_xfer != NULL); + + edisc_xfer->hc = NULL; + + if (!purple_http_response_is_successful(response)) { + ggp_edisc_xfer_error(xfer, _("Error while receiving a file")); + return; + } + + if (purple_xfer_get_bytes_remaining(xfer) == 0) { + purple_xfer_set_completed(xfer, TRUE); + purple_xfer_end(xfer); + ggp_edisc_xfer_free(xfer); + } else { + purple_debug_warning("gg", "ggp_edisc_xfer_recv_done: didn't " + "received everything\n"); + ggp_edisc_xfer_error(xfer, _("Error while receiving a file")); + } +} + +/******************************************************************************* + * Authentication. + ******************************************************************************/ + +struct _ggp_edisc_auth_data +{ + ggp_ggdrive_auth_cb cb; + gpointer user_data; +}; + +static void ggp_ggdrive_auth_done(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer user_data); + +static void ggp_ggdrive_auth(PurpleConnection *gc, ggp_ggdrive_auth_cb cb, + gpointer user_data) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc); + ggp_edisc_auth_data *auth; + const gchar *imtoken; + gchar *metadata; + PurpleHttpRequest *req; + + imtoken = ggp_get_imtoken(gc); + if (!imtoken) + { + cb(gc, FALSE, user_data); + return; + } + + if (sdata->auth_done) + { + cb(gc, sdata->security_token != NULL, user_data); + return; + } + + auth = g_new0(ggp_edisc_auth_data, 1); + auth->cb = cb; + auth->user_data = user_data; + sdata->auth_pending = g_list_prepend(sdata->auth_pending, auth); + + if (sdata->auth_request) + return; + + purple_debug_info("gg", "ggp_ggdrive_auth(gc=%p)\n", gc); + + req = purple_http_request_new("https://drive.mpa.gg.pl/signin"); + purple_http_request_set_method(req, "PUT"); + + ggp_edisc_set_defaults(req); + purple_http_request_set_cookie_jar(req, sdata->cookies); + + metadata = g_strdup_printf("{" + "\"id\": \"%032x\", " + "\"name\": \"%s\", " + "\"os_version\": \"" GGP_EDISC_OS "\", " + "\"client_version\": \"%s\", " + "\"type\": \"" GGP_EDISC_TYPE "\"}", + g_random_int_range(1, 1 << 16), + purple_get_host_name(), + ggp_libgaduw_version(gc)); + + purple_http_request_header_set_printf(req, "Authorization", + "IMToken %s", imtoken); + purple_http_request_header_set_printf(req, "X-gged-user", + "gg/pl:%u", accdata->session->uin); + purple_http_request_header_set(req, "X-gged-client-metadata", metadata); + g_free(metadata); + + sdata->auth_request = purple_http_request(gc, req, + ggp_ggdrive_auth_done, NULL); + purple_http_request_unref(req); +} + +static void ggp_ggdrive_auth_results(PurpleConnection *gc, gboolean success) +{ + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc); + GList *it; + + purple_debug_info("gg", "ggp_ggdrive_auth_results(gc=%p): %d\n", + gc, success); + + it = g_list_first(sdata->auth_pending); + while (it) { + ggp_edisc_auth_data *auth = it->data; + it = g_list_next(it); + + auth->cb(gc, success, auth->user_data); + g_free(auth); + } + g_list_free(sdata->auth_pending); + sdata->auth_pending = NULL; + sdata->auth_done = TRUE; +} + +static void ggp_ggdrive_auth_done(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer user_data) +{ + PurpleConnection *gc = purple_http_conn_get_purple_connection(hc); + ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc); + JsonParser *parser; + JsonObject *result; + int status = -1; + + sdata->auth_request = NULL; + + if (!purple_http_response_is_successful(response)) { + purple_debug_misc("gg", "ggp_ggdrive_auth_done: authentication " + "failed due to unsuccessful request (code = %d)\n", + purple_http_response_get_code(response)); + ggp_ggdrive_auth_results(gc, FALSE); + return; + } + + parser = ggp_json_parse(purple_http_response_get_data(response, NULL)); + result = json_node_get_object(json_parser_get_root(parser)); + result = json_object_get_object_member(result, "result"); + if (json_object_has_member(result, "status")) + status = json_object_get_int_member(result, "status"); + g_object_unref(parser); + + if (status != 0 ) { + purple_debug_misc("gg", "ggp_ggdrive_auth_done: authentication " + "failed due to bad result (status=%d)\n", status); + if (purple_debug_is_verbose()) + purple_debug_misc("gg", "ggp_ggdrive_auth_done: " + "result = %s\n", + purple_http_response_get_data(response, NULL)); + ggp_ggdrive_auth_results(gc, FALSE); + return; + } + + sdata->security_token = g_strdup(purple_http_response_get_header( + response, "X-gged-security-token")); + if (!sdata->security_token) { + purple_debug_misc("gg", "ggp_ggdrive_auth_done: authentication " + "failed due to missing security token header\n"); + ggp_ggdrive_auth_results(gc, FALSE); + return; + } + + if (purple_debug_is_unsafe()) + purple_debug_misc("gg", "ggp_ggdrive_auth_done: " + "security_token=%s\n", sdata->security_token); + ggp_ggdrive_auth_results(gc, TRUE); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/edisc.h Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,22 @@ +#ifndef _GGP_EDISC_H +#define _GGP_EDISC_H + +#include <internal.h> + +typedef struct _ggp_edisc_session_data ggp_edisc_session_data; + +/* Setting up. */ +void ggp_edisc_setup(PurpleConnection *gc); +void ggp_edisc_cleanup(PurpleConnection *gc); + +/* General xfer functions. */ +void ggp_edisc_xfer_ticket_changed(PurpleConnection *gc, + const char *data); + +/* Sending a file. */ +gboolean ggp_edisc_xfer_can_receive_file(PurpleConnection *gc, const char *who); +void ggp_edisc_xfer_send_file(PurpleConnection *gc, const char *who, + const char *filename); +PurpleXfer * ggp_edisc_xfer_send_new(PurpleConnection *gc, const char *who); + +#endif /* _GGP_EDISC_H */
--- a/libpurple/protocols/gg/gg.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/gg.c Mon Sep 16 18:08:19 2013 +0530 @@ -39,7 +39,7 @@ #include "xmlnode.h" #include "gg.h" -#include "confer.h" +#include "chat.h" #include "search.h" #include "blist.h" #include "utils.h" @@ -51,7 +51,11 @@ #include "multilogon.h" #include "status.h" #include "servconn.h" +#include "tcpsocket.h" #include "pubdir-prpl.h" +#include "message-prpl.h" +#include "html.h" +#include "libgaduw.h" /* ---------------------------------------------------------------------- */ static PurpleProtocol *my_protocol = NULL; @@ -65,7 +69,7 @@ if (buddy_data) return buddy_data; - buddy_data = g_new0(ggp_buddy_data, 1); + buddy_data = g_new0(ggp_buddy_data, 1); //TODO: leak purple_buddy_set_protocol_data(buddy, buddy_data); return buddy_data; } @@ -81,6 +85,30 @@ purple_buddy_set_protocol_data(buddy, NULL); } +const gchar * ggp_get_imtoken(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + + if (accdata->imtoken) + return accdata->imtoken; + + if (accdata->imtoken_warned) + return NULL; + accdata->imtoken_warned = TRUE; + + purple_notify_error(gc, _("Authentication failed"), + _("IMToken value has not been received."), + _("Some features will be disabled. " + "You may try again after a while.")); + return NULL; +} + +uin_t ggp_own_uin(PurpleConnection *gc) +{ + return ggp_str_to_uin(purple_account_get_username( + purple_connection_get_account(gc))); +} + /* ---------------------------------------------------------------------- */ // buddy list import/export from/to file @@ -169,72 +197,6 @@ purple_request_cpar_from_connection(gc), gc); } -/* ----- CONFERENCES ---------------------------------------------------- */ - -static void ggp_callback_add_to_chat_ok(PurpleBuddy *buddy, PurpleRequestFields *fields) -{ - PurpleConnection *conn; - PurpleRequestField *field; - GList *sel; - - conn = purple_account_get_connection(purple_buddy_get_account(buddy)); - - g_return_if_fail(conn != NULL); - - field = purple_request_fields_get_field(fields, "name"); - sel = purple_request_field_list_get_selected(field); - - if (sel == NULL) { - purple_debug_error("gg", "No chat selected\n"); - return; - } - - ggp_confer_participants_add_uin(conn, sel->data, - ggp_str_to_uin(purple_buddy_get_name(buddy))); -} - -static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - GGPInfo *info; - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - - GList *l; - gchar *msg; - - buddy = (PurpleBuddy *)node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - info = purple_connection_get_protocol_data(gc); - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_list_new("name", "Chat name"); - for (l = info->chats; l != NULL; l = l->next) { - GGPChat *chat = l->data; - purple_request_field_list_add_icon(field, chat->name, NULL, chat->name); - } - purple_request_field_group_add_field(group, field); - - msg = g_strdup_printf(_("Select a chat for buddy: %s"), - purple_buddy_get_alias(buddy)); - purple_request_fields(gc, - _("Add to chat..."), - _("Add to chat..."), - msg, - fields, - _("Add"), G_CALLBACK(ggp_callback_add_to_chat_ok), - _("Cancel"), NULL, - purple_request_cpar_from_connection(gc), - buddy); - g_free(msg); -} - /* ----- BLOCK BUDDIES -------------------------------------------------- */ static void ggp_add_deny(PurpleConnection *gc, const char *who) @@ -263,197 +225,6 @@ /* ----- INTERNAL CALLBACKS --------------------------------------------- */ /* ---------------------------------------------------------------------- */ -/** - * Dispatch a message received from a buddy. - * - * @param gc PurpleConnection. - * @param ev Gadu-Gadu event structure. - * - * Image receiving, some code borrowed from Kadu http://www.kadu.net - */ -void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event_msg *ev, gboolean multilogon) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - PurpleChatConversation *chat; - gchar *from; - gchar *msg; - gchar *tmp; - time_t mtime; - uin_t sender = ev->sender; - - if (ev->message == NULL) - { - purple_debug_warning("gg", "ggp_recv_message_handler: NULL as message pointer\n"); - return; - } - - from = g_strdup_printf("%lu", (unsigned long int)ev->sender); - - tmp = g_strdup_printf("%s", ev->message); - purple_str_strip_char(tmp, '\r'); - msg = g_markup_escape_text(tmp, -1); - g_free(tmp); - - if (ev->msgclass & GG_CLASS_QUEUED) - mtime = ev->time; - else - mtime = time(NULL); - - /* We got richtext message */ - if (ev->formats_length) - { - gboolean got_image = FALSE, bold = FALSE, italic = FALSE, under = FALSE; - char *cformats = (char *)ev->formats; - char *cformats_end = cformats + ev->formats_length; - gint increased_len = 0; - struct gg_msg_richtext_format *actformat; - struct gg_msg_richtext_image *actimage; - GString *message = g_string_new(msg); - - purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->formats_length); - - while (cformats < cformats_end) - { - gint byteoffset; - actformat = (struct gg_msg_richtext_format *)cformats; - cformats += sizeof(struct gg_msg_richtext_format); - byteoffset = g_utf8_offset_to_pointer(message->str, actformat->position + increased_len) - message->str; - - if(actformat->position == 0 && actformat->font == 0) { - purple_debug_warning("gg", "ggp_recv_message_handler: bogus formatting (inc: %i)\n", increased_len); - continue; - } - purple_debug_info("gg", "ggp_recv_message_handler: format at pos: %i, image:%i, bold:%i, italic: %i, under:%i (inc: %i)\n", - actformat->position, - (actformat->font & GG_FONT_IMAGE) != 0, - (actformat->font & GG_FONT_BOLD) != 0, - (actformat->font & GG_FONT_ITALIC) != 0, - (actformat->font & GG_FONT_UNDERLINE) != 0, - increased_len); - - if (actformat->font & GG_FONT_IMAGE) - { - const char *placeholder; - - got_image = TRUE; - actimage = (struct gg_msg_richtext_image*)(cformats); - cformats += sizeof(struct gg_msg_richtext_image); - purple_debug_info("gg", "ggp_recv_message_handler: image received, size: %d, crc32: %i\n", actimage->size, actimage->crc32); - - /* Checking for errors, image size shouldn't be - * larger than 255.000 bytes */ - if (actimage->size > 255000) { - purple_debug_warning("gg", "ggp_recv_message_handler: received image large than 255 kb\n"); - continue; - } - - gg_image_request(info->session, ev->sender, - actimage->size, actimage->crc32); - - placeholder = ggp_image_pending_placeholder(actimage->crc32); - g_string_insert(message, byteoffset, placeholder); - increased_len += strlen(placeholder); - continue; - } - - if (actformat->font & GG_FONT_BOLD) { - if (bold == FALSE) { - g_string_insert(message, byteoffset, "<b>"); - increased_len += 3; - bold = TRUE; - } - } else if (bold) { - g_string_insert(message, byteoffset, "</b>"); - increased_len += 4; - bold = FALSE; - } - - if (actformat->font & GG_FONT_ITALIC) { - if (italic == FALSE) { - g_string_insert(message, byteoffset, "<i>"); - increased_len += 3; - italic = TRUE; - } - } else if (italic) { - g_string_insert(message, byteoffset, "</i>"); - increased_len += 4; - italic = FALSE; - } - - if (actformat->font & GG_FONT_UNDERLINE) { - if (under == FALSE) { - g_string_insert(message, byteoffset, "<u>"); - increased_len += 3; - under = TRUE; - } - } else if (under) { - g_string_insert(message, byteoffset, "</u>"); - increased_len += 4; - under = FALSE; - } - - if (actformat->font & GG_FONT_COLOR) { - cformats += sizeof(struct gg_msg_richtext_color); - } - } - - msg = message->str; - g_string_free(message, FALSE); - - if (got_image) - { - ggp_image_got_im(gc, sender, msg, mtime); - return; - } - } - - purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d; multilogon = %d)\n", - from, msg, ev->msgclass, - ev->recipients_count, - multilogon); - - if (multilogon && ev->recipients_count != 0) { - purple_debug_warning("gg", "ggp_recv_message_handler: conference multilogon messages are not yet handled\n"); - } else if (multilogon) { - PurpleAccount *account = purple_connection_get_account(gc); - PurpleIMConversation *im; - const gchar *who = ggp_uin_to_str(ev->sender); // not really sender - im = purple_conversations_find_im_with_account(who, account); - if (im == NULL) - im = purple_im_conversation_new(account, who); - purple_conversation_write(PURPLE_CONVERSATION(im), purple_account_get_username(account), msg, PURPLE_MESSAGE_SEND, mtime); - } else if (ev->recipients_count == 0) { - serv_got_im(gc, from, msg, 0, mtime); - } else { - const char *chat_name; - int chat_id; - - chat_name = ggp_confer_find_by_participants(gc, - ev->recipients, - ev->recipients_count); - - if (chat_name == NULL) { - chat_name = ggp_confer_add_new(gc, NULL); - serv_got_joined_chat(gc, info->chats_count, chat_name); - - ggp_confer_participants_add_uin(gc, chat_name, - ev->sender); - - ggp_confer_participants_add(gc, chat_name, - ev->recipients, - ev->recipients_count); - } - chat = ggp_confer_find_by_name(gc, chat_name); - chat_id = purple_chat_conversation_get_id(chat); - - serv_got_chat_in(gc, chat_id, - ggp_buddylist_get_buddy_name(gc, ev->sender), - PURPLE_MESSAGE_RECV, msg, mtime); - } - g_free(msg); - g_free(from); -} - static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int length) { gchar *from; @@ -477,39 +248,43 @@ static void ggp_xml_event_handler(PurpleConnection *gc, char *data) { PurpleXmlNode *xml = NULL; - PurpleXmlNode *purple_xmlnode_next_event; + PurpleXmlNode *xmlnode_next_event; xml = purple_xmlnode_from_str(data, -1); if (xml == NULL) + { + purple_debug_error("gg", "ggp_xml_event_handler: " + "invalid xml: [%s]\n", data); goto out; + } - purple_xmlnode_next_event = purple_xmlnode_get_child(xml, "event"); - while (purple_xmlnode_next_event != NULL) + xmlnode_next_event = purple_xmlnode_get_child(xml, "event"); + while (xmlnode_next_event != NULL) { - PurpleXmlNode *purple_xmlnode_current_event = purple_xmlnode_next_event; + PurpleXmlNode *xmlnode_current_event = xmlnode_next_event; - PurpleXmlNode *purple_xmlnode_type; + PurpleXmlNode *xmlnode_type; char *event_type_raw; int event_type = 0; - PurpleXmlNode *purple_xmlnode_sender; + PurpleXmlNode *xmlnode_sender; char *event_sender_raw; uin_t event_sender = 0; - purple_xmlnode_next_event = purple_xmlnode_get_next_twin(purple_xmlnode_next_event); + xmlnode_next_event = purple_xmlnode_get_next_twin(xmlnode_next_event); - purple_xmlnode_type = purple_xmlnode_get_child(purple_xmlnode_current_event, "type"); - if (purple_xmlnode_type == NULL) + xmlnode_type = purple_xmlnode_get_child(xmlnode_current_event, "type"); + if (xmlnode_type == NULL) continue; - event_type_raw = purple_xmlnode_get_data(purple_xmlnode_type); + event_type_raw = purple_xmlnode_get_data(xmlnode_type); if (event_type_raw != NULL) event_type = atoi(event_type_raw); g_free(event_type_raw); - purple_xmlnode_sender = purple_xmlnode_get_child(purple_xmlnode_current_event, "sender"); - if (purple_xmlnode_sender != NULL) + xmlnode_sender = purple_xmlnode_get_child(xmlnode_current_event, "sender"); + if (xmlnode_sender != NULL) { - event_sender_raw = purple_xmlnode_get_data(purple_xmlnode_sender); + event_sender_raw = purple_xmlnode_get_data(xmlnode_sender); if (event_sender_raw != NULL) event_sender = ggp_str_to_uin(event_sender_raw); g_free(event_sender_raw); @@ -553,16 +328,18 @@ case GG_EVENT_NONE: /* Nothing happened. */ break; - case GG_EVENT_MSG: - ggp_recv_message_handler(gc, &ev->event.msg, FALSE); + case GG_EVENT_CONN_FAILED: + purple_connection_error (gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Server disconnected")); break; - case GG_EVENT_ACK: - /* Changing %u to %i fixes compiler warning */ - purple_debug_info("gg", - "ggp_callback_recv: message sent to: %i, delivery status=%d, seq=%d\n", - ev->event.ack.recipient, ev->event.ack.status, - ev->event.ack.seq); + case GG_EVENT_MSG: + ggp_message_got(gc, &ev->event.msg); break; +#if GGP_ENABLE_GG11 + case GG_EVENT_ACK110: + break; +#endif case GG_EVENT_IMAGE_REPLY: ggp_image_recv(gc, &ev->event.image_reply); break; @@ -584,6 +361,11 @@ case GG_EVENT_USER_DATA: ggp_events_user_data(gc, &ev->event.user_data); break; +#if GGP_ENABLE_GG11 + case GG_EVENT_JSON_EVENT: + ggp_events_json(gc, &ev->event.json_event); + break; +#endif case GG_EVENT_USERLIST100_VERSION: ggp_roster_version(gc, &ev->event.userlist100_version); break; @@ -591,13 +373,30 @@ ggp_roster_reply(gc, &ev->event.userlist100_reply); break; case GG_EVENT_MULTILOGON_MSG: - ggp_multilogon_msg(gc, &ev->event.multilogon_msg); + ggp_message_got_multilogon(gc, &ev->event.multilogon_msg); break; case GG_EVENT_MULTILOGON_INFO: ggp_multilogon_info(gc, &ev->event.multilogon_info); break; +#if GGP_ENABLE_GG11 + case GG_EVENT_IMTOKEN: + purple_debug_info("gg", "gg11: got IMTOKEN\n"); + g_free(info->imtoken); + info->imtoken = g_strdup(ev->event.imtoken.imtoken); + break; + case GG_EVENT_PONG110: + purple_debug_info("gg", "gg11: got PONG110 %lu\n", ev->event.pong110.time); + break; + case GG_EVENT_CHAT_INFO: + case GG_EVENT_CHAT_INFO_GOT_ALL: + case GG_EVENT_CHAT_INFO_UPDATE: + case GG_EVENT_CHAT_CREATED: + case GG_EVENT_CHAT_INVITE_ACK: + ggp_chat_got_event(gc, ev); + break; +#endif default: - purple_debug_error("gg", + purple_debug_warning("gg", "unsupported event type=%d\n", ev->type); break; } @@ -605,7 +404,7 @@ gg_free_event(ev); } -static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond) +void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond) { PurpleConnection *gc = _gc; GGPInfo *info; @@ -643,6 +442,14 @@ case GG_STATE_TLS_NEGOTIATION: purple_debug_info("gg", "GG_STATE_TLS_NEGOTIATION\n"); break; +#if GGP_ENABLE_GG11 + case GG_STATE_RESOLVING_HUB: + purple_debug_info("gg", "GG_STATE_RESOLVING_HUB\n"); + break; + case GG_STATE_READING_HUB: + purple_debug_info("gg", "GG_STATE_READING_HUB\n"); + break; +#endif default: purple_debug_error("gg", "unknown state = %d\n", info->session->state); @@ -677,12 +484,13 @@ break; case GG_EVENT_CONN_SUCCESS: { - const gchar * server_ip = ggp_ipv4_to_str( - info->session->server_addr); +#if GGP_ENABLE_GG11 purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS:" " successfully connected to %s\n", - server_ip); - ggp_servconn_add_server(server_ip); + info->session->connect_host); + ggp_servconn_add_server(info->session-> + connect_host); +#endif purple_input_remove(info->inpa); info->inpa = purple_input_add(info->session->fd, PURPLE_INPUT_READ, @@ -798,6 +606,10 @@ return normalized; } +/* TODO: + * - move to status.c ? + * - add information about not adding to his buddy list (not_a_friend) + */ static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) { PurpleStatus *status; @@ -829,72 +641,40 @@ } } -static GList *ggp_blist_node_menu(PurpleBlistNode *node) -{ - PurpleMenuAction *act; - GList *m = NULL; - PurpleAccount *account; - PurpleConnection *gc; - GGPInfo *info; - - if (!PURPLE_IS_BUDDY(node)) - return NULL; - - account = purple_buddy_get_account((PurpleBuddy *) node); - gc = purple_account_get_connection(account); - info = purple_connection_get_protocol_data(gc); - if (info->chats) { - act = purple_menu_action_new(_("Add to chat"), - PURPLE_CALLBACK(ggp_bmenu_add_to_chat), - NULL, NULL); - m = g_list_append(m, act); - } - - return m; -} - -static GList *ggp_chat_info(PurpleConnection *gc) -{ - GList *m = NULL; - PurpleProtocolChatEntry *pce; - - pce = g_new0(PurpleProtocolChatEntry, 1); - pce->label = _("Chat _name:"); - pce->identifier = "name"; - pce->required = TRUE; - m = g_list_append(m, pce); - - return m; -} - static void ggp_login(PurpleAccount *account) { PurpleConnection *gc = purple_account_get_connection(account); struct gg_login_params *glp; GGPInfo *info; +#if GGP_ENABLE_GG11 const char *address; - const gchar *encryption_type; +#endif + const gchar *encryption_type, *protocol_version; if (!ggp_deprecated_setup_proxy(gc)) return; + purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_HTML | + PURPLE_CONNECTION_FLAG_NO_URLDESC); + glp = g_new0(struct gg_login_params, 1); +#if GGP_ENABLE_GG11 + glp->struct_size = sizeof(struct gg_login_params); +#endif info = g_new0(GGPInfo, 1); - /* Probably this should be moved to *_new() function. */ - info->session = NULL; - info->chats = NULL; - info->chats_count = 0; - purple_connection_set_protocol_data(gc, info); - + ggp_tcpsocket_setup(gc, glp); ggp_image_setup(gc); ggp_avatar_setup(gc); ggp_roster_setup(gc); ggp_multilogon_setup(gc); ggp_status_setup(gc); - + ggp_chat_setup(gc); + ggp_message_setup(gc); + ggp_edisc_setup(gc); + glp->uin = ggp_str_to_uin(purple_account_get_username(account)); glp->password = ggp_convert_to_cp1250(purple_connection_get_password(gc)); @@ -903,6 +683,7 @@ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("The username specified is invalid.")); + purple_str_wipe(glp->password); g_free(glp); return; } @@ -933,44 +714,55 @@ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable")); + purple_str_wipe(glp->password); g_free(glp); return; } } else /* encryption_type == "none" */ glp->tls = GG_SSL_DISABLED; - purple_debug_info("gg", "TLS mode: %d\n", glp->tls); + purple_debug_misc("gg", "TLS mode: %d\n", glp->tls); + + protocol_version = purple_account_get_string(account, + "protocol_version", "default"); + purple_debug_info("gg", "Requested protocol version: %s\n", + protocol_version); +#if GGP_ENABLE_GG11 + if (g_strcmp0(protocol_version, "gg10") == 0) + glp->protocol_version = GG_PROTOCOL_VERSION_100; + else if (g_strcmp0(protocol_version, "gg11") == 0) + glp->protocol_version = GG_PROTOCOL_VERSION_110; +#else + glp->protocol_version = 0x2e; +#endif ggp_status_set_initial(gc, glp); - + +#if GGP_ENABLE_GG11 address = purple_account_get_string(account, "gg_server", ""); if (address && *address) - { - glp->server_addr = inet_addr(address); - glp->server_port = 8074; - - if (glp->server_addr == INADDR_NONE) - { - purple_connection_error(gc, - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("Provided server IP address is not valid")); - g_free(glp); - return; - } - } else - purple_debug_info("gg", "Trying to retrieve address from gg appmsg service\n"); + glp->connect_host = g_strdup(address); +#endif info->session = gg_login(glp); +#if GGP_ENABLE_GG11 + g_free(glp->connect_host); +#endif + purple_str_wipe(glp->password); + g_free(glp); + purple_connection_update_progress(gc, _("Connecting"), 0, 2); if (info->session == NULL) { purple_connection_error (gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection failed")); - g_free(glp); return; } - info->inpa = purple_input_add(info->session->fd, PURPLE_INPUT_READ, - ggp_async_login_handler, gc); + + if (info->session->fd > 0) { + info->inpa = purple_input_add(info->session->fd, + PURPLE_INPUT_READ, ggp_async_login_handler, gc); + } } static void ggp_close(PurpleConnection *gc) @@ -978,14 +770,13 @@ PurpleAccount *account; GGPInfo *info;; - if (gc == NULL) { - purple_debug_info("gg", "gc == NULL\n"); - return; - } + g_return_if_fail(gc != NULL); account = purple_connection_get_account(gc); info = purple_connection_get_protocol_data(gc); + purple_notify_close_with_handle(gc); + if (info) { if (info->session != NULL) { @@ -994,19 +785,18 @@ gg_free_session(info->session); } - /* Immediately close any notifications on this handle since that process depends - * upon the contents of info->searches, which we are about to destroy. - */ - purple_notify_close_with_handle(gc); - ggp_image_cleanup(gc); ggp_avatar_cleanup(gc); ggp_roster_cleanup(gc); ggp_multilogon_cleanup(gc); ggp_status_cleanup(gc); + ggp_chat_cleanup(gc); + ggp_message_cleanup(gc); + ggp_edisc_cleanup(gc); if (info->inpa > 0) purple_input_remove(info->inpa); + g_free(info->imtoken); purple_connection_set_protocol_data(gc, NULL); g_free(info); @@ -1015,131 +805,6 @@ purple_debug_info("gg", "Connection closed.\n"); } -static int ggp_send_im(PurpleConnection *gc, const char *who, const char *msg, - PurpleMessageFlags flags) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - char *tmp, *plain; - int ret = 1; - unsigned char format[1024]; - unsigned int format_length = sizeof(struct gg_msg_richtext); - gint pos = 0; - GData *attribs; - const char *start, *end = NULL, *last; - ggp_buddy_data *buddy_data = ggp_buddy_get_data( - purple_blist_find_buddy(purple_connection_get_account(gc), who)); - - if (msg == NULL || *msg == '\0') { - return 0; - } - - if (buddy_data->blocked) - return -1; - - last = msg; - - /* Check if the message is richtext */ - /* TODO: Check formatting, too */ - if(purple_markup_find_tag("img", last, &start, &end, &attribs)) { - - GString *string_buffer = g_string_new(NULL); - struct gg_msg_richtext fmt; - - do - { - const char *id = g_datalist_get_data(&attribs, "id"); - struct gg_msg_richtext_format actformat; - struct gg_msg_richtext_image actimage; - ggp_image_prepare_result prepare_result; - - /* Add text before the image */ - if(start - last) - { - pos = pos + g_utf8_strlen(last, start - last); - g_string_append_len(string_buffer, last, - start - last); - } - last = end + 1; - - if (id == NULL) - { - g_datalist_clear(&attribs); - continue; - } - - /* add the image itself */ - prepare_result = ggp_image_prepare( - gc, atoi(id), who, &actimage); - if (prepare_result == GGP_IMAGE_PREPARE_OK) - { - actformat.font = GG_FONT_IMAGE; - actformat.position = pos; - - memcpy(format + format_length, &actformat, - sizeof(actformat)); - format_length += sizeof(actformat); - memcpy(format + format_length, &actimage, - sizeof(actimage)); - format_length += sizeof(actimage); - } - else if (prepare_result == GGP_IMAGE_PREPARE_TOO_BIG) - { - PurpleIMConversation *im = - purple_conversations_find_im_with_account(who, - purple_connection_get_account(gc)); - purple_conversation_write(PURPLE_CONVERSATION(im), "", - _("Image is too large, please try " - "smaller one."), PURPLE_MESSAGE_ERROR, - time(NULL)); - } - - g_datalist_clear(&attribs); - } while (purple_markup_find_tag("img", last, &start, &end, - &attribs)); - - /* Add text after the images */ - if(last && *last) { - pos = pos + g_utf8_strlen(last, -1); - g_string_append(string_buffer, last); - } - - fmt.flag = 2; - fmt.length = format_length - sizeof(fmt); - memcpy(format, &fmt, sizeof(fmt)); - - purple_debug_info("gg", "ggp_send_im: richtext msg = %s\n", string_buffer->str); - plain = purple_unescape_html(string_buffer->str); - g_string_free(string_buffer, TRUE); - } else { - purple_debug_info("gg", "ggp_send_im: msg = %s\n", msg); - plain = purple_unescape_html(msg); - } - - tmp = g_strdup(plain); - - if (tmp && (format_length - sizeof(struct gg_msg_richtext))) { - if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) { - ret = -1; - } else { - ret = 1; - } - } else if (NULL == tmp || *tmp == 0) { - ret = 0; - } else if (strlen(tmp) > GG_MSG_MAXSIZE) { - ret = -E2BIG; - } else if (gg_send_message(info->session, GG_CLASS_CHAT, - ggp_str_to_uin(who), (unsigned char *)tmp) < 0) { - ret = -1; - } else { - ret = 1; - } - - g_free(plain); - g_free(tmp); - - return ret; -} - static unsigned int ggp_send_typing(PurpleConnection *gc, const char *name, PurpleIMTypingState state) { GGPInfo *info = purple_connection_get_protocol_data(gc); @@ -1186,94 +851,6 @@ ggp_roster_remove_buddy(gc, buddy, group); } -static void ggp_join_chat(PurpleConnection *gc, GHashTable *data) -{ - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPChat *chat; - char *chat_name; - GList *l; - PurpleChatConversation *conv; - PurpleAccount *account = purple_connection_get_account(gc); - - chat_name = g_hash_table_lookup(data, "name"); - - if (chat_name == NULL) - return; - - purple_debug_info("gg", "joined %s chat\n", chat_name); - - for (l = info->chats; l != NULL; l = l->next) { - chat = l->data; - - if (chat != NULL && g_utf8_collate(chat->name, chat_name) == 0) { - purple_notify_error(gc, _("Chat error"), - _("This chat name is already in use"), NULL); - return; - } - } - - ggp_confer_add_new(gc, chat_name); - conv = serv_got_joined_chat(gc, info->chats_count, chat_name); - purple_chat_conversation_add_user(conv, purple_account_get_username(account), - NULL, PURPLE_CHAT_USER_NONE, TRUE); -} - -static char *ggp_get_chat_name(GHashTable *data) { - return g_strdup(g_hash_table_lookup(data, "name")); -} - -static int ggp_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) -{ - PurpleChatConversation *conv; - GGPInfo *info = purple_connection_get_protocol_data(gc); - GGPChat *chat = NULL; - GList *l; - /* char *msg, *plain; */ - gchar *msg; - uin_t *uins; - int count = 0; - - if ((conv = purple_conversations_find_chat(gc, id)) == NULL) - return -EINVAL; - - for (l = info->chats; l != NULL; l = l->next) { - chat = l->data; - - if (g_utf8_collate(chat->name, purple_conversation_get_name( - PURPLE_CONVERSATION(conv))) == 0) { - break; - } - - chat = NULL; - } - - if (chat == NULL) { - purple_debug_error("gg", - "ggp_chat_send: Hm... that's strange. No such chat?\n"); - return -EINVAL; - } - - uins = g_new0(uin_t, g_list_length(chat->participants)); - - for (l = chat->participants; l != NULL; l = l->next) { - uin_t uin = GPOINTER_TO_INT(l->data); - - uins[count++] = uin; - } - - msg = purple_unescape_html(message); - gg_send_message_confer(info->session, GG_CLASS_CHAT, count, uins, - (unsigned char *)msg); - g_free(msg); - g_free(uins); - - serv_got_chat_in(gc, id, - purple_account_get_username(purple_connection_get_account(gc)), - flags, message, time(NULL)); - - return 0; -} - static void ggp_keepalive(PurpleConnection *gc) { GGPInfo *info = purple_connection_get_protocol_data(gc); @@ -1351,6 +928,8 @@ if (buddy_data->blocked) return "not-authorized"; + if (buddy_data->not_a_friend) + return "unavailable"; return NULL; } @@ -1375,34 +954,12 @@ return 1232; } -static void purple_gg_debug_handler(int level, const char * format, va_list args) { - PurpleDebugLevel purple_level; - char *msg = g_strdup_vprintf(format, args); - - /* This is pretty pointless since the GG_DEBUG levels don't correspond to - * the purple ones */ - switch (level) { - case GG_DEBUG_FUNCTION: - purple_level = PURPLE_DEBUG_INFO; - break; - case GG_DEBUG_MISC: - case GG_DEBUG_NET: - case GG_DEBUG_DUMP: - case GG_DEBUG_TRAFFIC: - default: - purple_level = PURPLE_DEBUG_MISC; - break; - } - - purple_debug(purple_level, "gg", "%s", msg); - g_free(msg); -} - static void ggp_protocol_init(PurpleProtocol *protocol) { PurpleAccountOption *option; GList *encryption_options = NULL; + GList *protocol_version = NULL; protocol->id = "gg"; protocol->name = "Gadu-Gadu"; @@ -1436,6 +993,15 @@ protocol->protocol_options = g_list_append(protocol->protocol_options, option); + ADD_VALUE(protocol_version, _("Default"), "default"); + ADD_VALUE(protocol_version, "GG 10", "gg10"); + ADD_VALUE(protocol_version, "GG 11", "gg11"); + + option = purple_account_option_list_new(_("Protocol version"), + "protocol_version", protocol_version); + protocol->protocol_options = g_list_append(protocol->protocol_options, + option); + option = purple_account_option_bool_new(_("Show links from strangers"), "show_links_from_strangers", 1); protocol->protocol_options = g_list_append(protocol->protocol_options, @@ -1458,7 +1024,6 @@ client_iface->list_emblem = ggp_list_emblem; client_iface->status_text = ggp_status_buddy_text; client_iface->tooltip_text = ggp_tooltip_text; - client_iface->blist_node_menu = ggp_blist_node_menu; client_iface->buddy_free = ggp_buddy_free; client_iface->normalize = ggp_normalize; client_iface->offline_message = ggp_offline_message; @@ -1484,18 +1049,31 @@ static void ggp_protocol_im_iface_init(PurpleProtocolIMIface *im_iface) { - im_iface->send = ggp_send_im; + im_iface->send = ggp_message_send_im; im_iface->send_typing = ggp_send_typing; } +#if GGP_ENABLE_GG11 +static void +ggp_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +{ + chat_iface->info = ggp_chat_info; + chat_iface->info_defaults = ggp_chat_info_defaults; + chat_iface->join = ggp_chat_join; + chat_iface->get_name = ggp_chat_get_name; + chat_iface->invite = ggp_chat_invite; + chat_iface->leave = ggp_chat_leave; + chat_iface->send = ggp_chat_send; + + chat_iface->reject = NULL; /* TODO */ +} + static void -ggp_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface) +ggp_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface) { - chat_iface->info = ggp_chat_info; - chat_iface->join = ggp_join_chat; - chat_iface->get_name = ggp_get_chat_name; - chat_iface->send = ggp_chat_send; + roomlist_iface->get_list = ggp_chat_roomlist_get_list; } +#endif static void ggp_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface) @@ -1504,6 +1082,14 @@ privacy_iface->rem_deny = ggp_rem_deny; } +static void +ggp_protocol_xfer_iface_init(PurpleProtocolXferIface *xfer_iface) +{ + xfer_iface->can_receive = ggp_edisc_xfer_can_receive_file; + xfer_iface->send = ggp_edisc_xfer_send_file; + xfer_iface->new_xfer = ggp_edisc_xfer_send_new; +} + PURPLE_DEFINE_TYPE_EXTENDED( GGPProtocol, ggp_protocol, PURPLE_TYPE_PROTOCOL, 0, @@ -1515,12 +1101,18 @@ PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, ggp_protocol_im_iface_init) - +#if GGP_ENABLE_GG11 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, ggp_protocol_chat_iface_init) + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, + ggp_protocol_roomlist_iface_init) +#endif PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, ggp_protocol_privacy_iface_init) + + PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE, + ggp_protocol_xfer_iface_init) ); static PurplePluginInfo * @@ -1558,13 +1150,14 @@ purple_prefs_add_none("/protocols/gg"); - gg_debug_handler = purple_gg_debug_handler; - purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with " "libgadu %s...\n", gg_libgadu_version()); + ggp_libgaduw_setup(); ggp_resolver_purple_setup(); ggp_servconn_setup(ggp_server_option); + ggp_html_setup(); + ggp_message_setup_global(); return TRUE; } @@ -1573,6 +1166,9 @@ plugin_unload(PurplePlugin *plugin, GError **error) { ggp_servconn_cleanup(); + ggp_html_cleanup(); + ggp_message_cleanup_global(); + ggp_libgaduw_cleanup(); if (!purple_protocols_remove(my_protocol, error)) return FALSE;
--- a/libpurple/protocols/gg/gg.h Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/gg.h Mon Sep 16 18:08:19 2013 +0530 @@ -24,6 +24,9 @@ #ifndef _PURPLE_GG_H #define _PURPLE_GG_H +#define GGP_UIN_LEN_MAX 10 +#define GGP_ENABLE_GG11 0 + #include <libgadu.h> #include "internal.h" #include "search.h" @@ -35,10 +38,9 @@ #include "roster.h" #include "multilogon.h" #include "status.h" - -#define PUBDIR_RESULTS_MAX 20 - -#define GGP_UIN_LEN_MAX 10 +#include "chat.h" +#include "message-prpl.h" +#include "edisc.h" #define GGP_TYPE_PROTOCOL (ggp_protocol_get_type()) #define GGP_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GGP_TYPE_PROTOCOL, GGPProtocol)) @@ -57,39 +59,37 @@ PurpleProtocolClass parent_class; } GGPProtocolClass; -typedef struct -{ - char *name; - GList *participants; - -} GGPChat; - typedef struct { - struct gg_session *session; guint inpa; - GList *chats; - int chats_count; - ggp_image_connection_data image_data; + gchar *imtoken; + gboolean imtoken_warned; + + ggp_image_session_data *image_data; ggp_avatar_session_data avatar_data; ggp_roster_session_data roster_data; ggp_multilogon_session_data *multilogon_data; ggp_status_session_data *status_data; + ggp_chat_session_data *chat_data; + ggp_message_session_data *message_data; + ggp_edisc_session_data *edisc_data; } GGPInfo; typedef struct { gboolean blocked; + gboolean not_a_friend; } ggp_buddy_data; -/** - * Returns the GType for the GGPProtocol object. - */ GType ggp_protocol_get_type(void); -void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event_msg *ev, gboolean multilogon); - ggp_buddy_data * ggp_buddy_get_data(PurpleBuddy *buddy); +const gchar * ggp_get_imtoken(PurpleConnection *gc); + +uin_t ggp_own_uin(PurpleConnection *gc); + +void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond); + #endif /* _PURPLE_GG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/html.c Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,158 @@ +#include "html.h" + +#include <debug.h> + +typedef struct +{ + GRegex *re_html_attr; + GRegex *re_css_attr; + GRegex *re_color_hex; + GRegex *re_color_rgb; +} ggp_html_global_data; + +static ggp_html_global_data global_data; + +void ggp_html_setup(void) +{ + global_data.re_html_attr = g_regex_new( + "([a-z-]+)=\"([^\"]+)\"", + G_REGEX_OPTIMIZE, 0, NULL); + global_data.re_css_attr = g_regex_new( + "([a-z-]+): *([^;]+)", + G_REGEX_OPTIMIZE, 0, NULL); + global_data.re_color_hex = g_regex_new( + "^#([0-9a-fA-F]+){6}$", + G_REGEX_OPTIMIZE, 0, NULL); + global_data.re_color_rgb = g_regex_new( + "^rgb\\(([0-9]+), *([0-9]+), *([0-9]+)\\)$", + G_REGEX_OPTIMIZE, 0, NULL); +} + +void ggp_html_cleanup(void) +{ + g_regex_unref(global_data.re_html_attr); + g_regex_unref(global_data.re_css_attr); + g_regex_unref(global_data.re_color_hex); + g_regex_unref(global_data.re_color_rgb); +} + +GHashTable * ggp_html_tag_attribs(const gchar *attribs_str) +{ + GMatchInfo *match; + GHashTable *attribs = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + + if (attribs_str == NULL) + return attribs; + + g_regex_match(global_data.re_html_attr, attribs_str, 0, &match); + while (g_match_info_matches(match)) + { + g_hash_table_insert(attribs, + g_match_info_fetch(match, 1), + g_match_info_fetch(match, 2)); + + g_match_info_next(match, NULL); + } + g_match_info_free(match); + + return attribs; +} + +GHashTable * ggp_html_css_attribs(const gchar *attribs_str) +{ + GMatchInfo *match; + GHashTable *attribs = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + + if (attribs_str == NULL) + return attribs; + + g_regex_match(global_data.re_css_attr, attribs_str, 0, &match); + while (g_match_info_matches(match)) + { + g_hash_table_insert(attribs, + g_match_info_fetch(match, 1), + g_match_info_fetch(match, 2)); + + g_match_info_next(match, NULL); + } + g_match_info_free(match); + + return attribs; +} + +int ggp_html_decode_color(const gchar *str) +{ + GMatchInfo *match; + int color = -1; + + g_regex_match(global_data.re_color_hex, str, 0, &match); + if (g_match_info_matches(match)) + { + if (sscanf(str + 1, "%x", &color) != 1) + color = -1; + } + g_match_info_free(match); + if (color >= 0) + return color; + + g_regex_match(global_data.re_color_rgb, str, 0, &match); + if (g_match_info_matches(match)) + { + int r = -1, g = -1, b = -1; + gchar *c_str; + + c_str = g_match_info_fetch(match, 1); + if (c_str) + r = atoi(c_str); + g_free(c_str); + + c_str = g_match_info_fetch(match, 2); + if (c_str) + g = atoi(c_str); + g_free(c_str); + + c_str = g_match_info_fetch(match, 3); + if (c_str) + b = atoi(c_str); + g_free(c_str); + + if (r >= 0 && r < 256 && g >= 0 && g < 256 && b >= 0 && b < 256) + color = (r << 16) | (g << 8) | b; + } + g_match_info_free(match); + if (color >= 0) + return color; + + return -1; +} + +ggp_html_tag ggp_html_parse_tag(const gchar *tag_str) +{ + if (0 == g_ascii_strcasecmp(tag_str, "eom")) + return GGP_HTML_TAG_EOM; + if (0 == g_ascii_strcasecmp(tag_str, "span")) + return GGP_HTML_TAG_SPAN; + if (0 == g_ascii_strcasecmp(tag_str, "div")) + return GGP_HTML_TAG_DIV; + if (0 == g_ascii_strcasecmp(tag_str, "br")) + return GGP_HTML_TAG_BR; + if (0 == g_ascii_strcasecmp(tag_str, "a")) + return GGP_HTML_TAG_A; + if (0 == g_ascii_strcasecmp(tag_str, "b")) + return GGP_HTML_TAG_B; + if (0 == g_ascii_strcasecmp(tag_str, "i")) + return GGP_HTML_TAG_I; + if (0 == g_ascii_strcasecmp(tag_str, "u")) + return GGP_HTML_TAG_U; + if (0 == g_ascii_strcasecmp(tag_str, "s")) + return GGP_HTML_TAG_S; + if (0 == g_ascii_strcasecmp(tag_str, "img")) + return GGP_HTML_TAG_IMG; + if (0 == g_ascii_strcasecmp(tag_str, "font")) + return GGP_HTML_TAG_FONT; + if (0 == g_ascii_strcasecmp(tag_str, "hr")) + return GGP_HTML_TAG_HR; + return GGP_HTML_TAG_UNKNOWN; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/html.h Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,32 @@ +#ifndef _GGP_HTML_H +#define _GGP_HTML_H + +#include <internal.h> + +typedef enum +{ + GGP_HTML_TAG_UNKNOWN, + GGP_HTML_TAG_EOM, + GGP_HTML_TAG_A, + GGP_HTML_TAG_B, + GGP_HTML_TAG_I, + GGP_HTML_TAG_U, + GGP_HTML_TAG_S, + GGP_HTML_TAG_IMG, + GGP_HTML_TAG_FONT, + GGP_HTML_TAG_SPAN, + GGP_HTML_TAG_DIV, + GGP_HTML_TAG_BR, + GGP_HTML_TAG_HR, +} ggp_html_tag; + +void ggp_html_setup(void); +void ggp_html_cleanup(void); + +GHashTable * ggp_html_tag_attribs(const gchar *attribs_str); +GHashTable * ggp_html_css_attribs(const gchar *attribs_str); +int ggp_html_decode_color(const gchar *str); +ggp_html_tag ggp_html_parse_tag(const gchar *tag_str); + + +#endif /* _GGP_HTML_H */
--- a/libpurple/protocols/gg/image.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/image.c Mon Sep 16 18:08:19 2013 +0530 @@ -35,105 +35,106 @@ #include "gg.h" #include "utils.h" -#define GGP_PENDING_IMAGE_ID_PREFIX "gg-pending-image-" - -typedef struct +struct _ggp_image_session_data { - uin_t from; - gchar *text; - time_t mtime; -} ggp_image_pending_message; + GHashTable *got_images; + GHashTable *incoming_images; + GHashTable *sent_images; +}; typedef struct { int id; - gchar *conv_name; -} ggp_image_pending_image; + gchar *conv_name; /* TODO: callback */ +} ggp_image_sent; -static void ggp_image_pending_message_free(gpointer data) +typedef struct { - ggp_image_pending_message *pending_message = - (ggp_image_pending_message*)data; - g_free(pending_message->text); - g_free(pending_message); + GList *listeners; +} ggp_image_requested; + +typedef struct +{ + ggp_image_request_cb cb; + gpointer user_data; +} ggp_image_requested_listener; + +static void ggp_image_got_free(gpointer data) +{ + int id = GPOINTER_TO_INT(data); + purple_imgstore_unref_by_id(id); } -static void ggp_image_pending_image_free(gpointer data) +static void ggp_image_sent_free(gpointer data) { - ggp_image_pending_image *pending_image = - (ggp_image_pending_image*)data; - g_free(pending_image->conv_name); - g_free(pending_image); + ggp_image_sent *sent_image = (ggp_image_sent*)data; + purple_imgstore_unref_by_id(sent_image->id); + g_free(sent_image->conv_name); + g_free(sent_image); } -static inline ggp_image_connection_data * -ggp_image_get_imgdata(PurpleConnection *gc) +static void ggp_image_requested_free(gpointer data) +{ + ggp_image_requested *req = data; + g_list_free_full(req->listeners, g_free); + g_free(req); +} + +static uint64_t ggp_image_params_to_id(uint32_t crc32, uint32_t size) +{ + return ((uint64_t)crc32 << 32) | size; +} + +static inline ggp_image_session_data * +ggp_image_get_sdata(PurpleConnection *gc) { GGPInfo *accdata = purple_connection_get_protocol_data(gc); - return &accdata->image_data; + return accdata->image_data; } void ggp_image_setup(PurpleConnection *gc) { - ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + ggp_image_session_data *sdata = g_new0(ggp_image_session_data, 1); + + accdata->image_data = sdata; - imgdata->pending_messages = NULL; - imgdata->pending_images = g_hash_table_new_full(NULL, NULL, NULL, - ggp_image_pending_image_free); + sdata->got_images = g_hash_table_new_full( + g_int64_hash, g_int64_equal, g_free, + ggp_image_got_free); + sdata->incoming_images = g_hash_table_new_full( + g_int64_hash, g_int64_equal, g_free, + ggp_image_requested_free); + sdata->sent_images = g_hash_table_new_full( + g_int64_hash, g_int64_equal, g_free, + ggp_image_sent_free); } void ggp_image_cleanup(PurpleConnection *gc) { - ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + ggp_image_session_data *sdata = ggp_image_get_sdata(gc); - g_list_free_full(imgdata->pending_messages, - &ggp_image_pending_message_free); - g_hash_table_destroy(imgdata->pending_images); -} - -const char * ggp_image_pending_placeholder(uint32_t id) -{ - static char buff[50]; - - g_snprintf(buff, 50, "<img id=\"" GGP_PENDING_IMAGE_ID_PREFIX - "%u\">", id); - - return buff; + g_hash_table_destroy(sdata->got_images); + g_hash_table_destroy(sdata->incoming_images); + g_hash_table_destroy(sdata->sent_images); + g_free(sdata); } -void ggp_image_got_im(PurpleConnection *gc, uin_t from, gchar *text, - time_t mtime) +ggp_image_prepare_result ggp_image_prepare(PurpleConversation *conv, + const int stored_id, uint64_t *id) { - ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); - ggp_image_pending_message *pending_message = - g_new(ggp_image_pending_message, 1); - - purple_debug_info("gg", "ggp_image_got_im: received message with " - "images from %u: %s\n", from, text); - - pending_message->from = from; - pending_message->text = text; - pending_message->mtime = mtime; - - imgdata->pending_messages = g_list_append(imgdata->pending_messages, - pending_message); -} - -ggp_image_prepare_result ggp_image_prepare(PurpleConnection *gc, const int id, - const char *conv_name, struct gg_msg_richtext_image *image_info) -{ - ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); - PurpleStoredImage *image = purple_imgstore_find_by_id(id); + PurpleConnection *gc = purple_conversation_get_connection(conv); + ggp_image_session_data *sdata = ggp_image_get_sdata(gc); + PurpleStoredImage *image = purple_imgstore_find_by_id(stored_id); size_t image_size; gconstpointer image_data; - const char *image_filename; uint32_t image_crc; - ggp_image_pending_image *pending_image; + ggp_image_sent *sent_image; if (!image) { - purple_debug_error("gg", "ggp_image_prepare_to_send: image %d " - "not found in image store\n", id); + purple_debug_error("gg", "ggp_image_prepare: image %d " + "not found in image store\n", stored_id); return GGP_IMAGE_PREPARE_FAILURE; } @@ -141,29 +142,26 @@ if (image_size > GGP_IMAGE_SIZE_MAX) { - purple_debug_warning("gg", "ggp_image_prepare_to_send: image " + purple_debug_warning("gg", "ggp_image_prepare: image " "is too big (max bytes: %d)\n", GGP_IMAGE_SIZE_MAX); return GGP_IMAGE_PREPARE_TOO_BIG; } purple_imgstore_ref(image); image_data = purple_imgstore_get_data(image); - image_filename = purple_imgstore_get_filename(image); image_crc = gg_crc32(0, image_data, image_size); - purple_debug_info("gg", "ggp_image_prepare_to_send: image prepared " - "[id=%d, crc=%u, size=%" G_GSIZE_FORMAT ", filename=%s]\n", - id, image_crc, image_size, image_filename); + purple_debug_info("gg", "ggp_image_prepare: image prepared " + "[id=%d, crc=%u, size=%" G_GSIZE_FORMAT "]\n", + stored_id, image_crc, image_size); - pending_image = g_new(ggp_image_pending_image, 1); - pending_image->id = id; - pending_image->conv_name = g_strdup(conv_name); - g_hash_table_insert(imgdata->pending_images, GINT_TO_POINTER(image_crc), - pending_image); - - image_info->unknown1 = 0x0109; - image_info->size = gg_fix32(image_size); - image_info->crc32 = gg_fix32(image_crc); + *id = ggp_image_params_to_id(image_crc, image_size); + + sent_image = g_new(ggp_image_sent, 1); + sent_image->id = stored_id; + sent_image->conv_name = g_strdup(purple_conversation_get_name(conv)); + g_hash_table_insert(sdata->sent_images, ggp_uint64dup(*id), + sent_image); return GGP_IMAGE_PREPARE_OK; } @@ -171,11 +169,11 @@ void ggp_image_recv(PurpleConnection *gc, const struct gg_event_image_reply *image_reply) { - ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); + ggp_image_session_data *sdata = ggp_image_get_sdata(gc); int stored_id; - const char *imgtag_search; - gchar *imgtag_replace; - GList *pending_messages_it; + ggp_image_requested *req; + GList *it; + uint64_t id; /* TODO: This PurpleStoredImage will be rendered within the IM window and right-clicking the image will allow the user to save the image @@ -190,66 +188,50 @@ image_reply->size, image_reply->filename); + id = ggp_image_params_to_id(image_reply->crc32, image_reply->size); + purple_debug_info("gg", "ggp_image_recv: got image " - "[id=%d, crc=%u, size=%u, filename=\"%s\"]\n", + "[stored_id=%d, crc=%u, size=%u, filename=%s, id=" + GGP_IMAGE_ID_FORMAT "]\n", stored_id, image_reply->crc32, image_reply->size, - image_reply->filename); + image_reply->filename, + id); - imgtag_search = ggp_image_pending_placeholder(image_reply->crc32); - imgtag_replace = g_strdup_printf("<img src=\"" - PURPLE_STORED_IMAGE_PROTOCOL "%u\">", stored_id); + g_hash_table_insert(sdata->got_images, ggp_uint64dup(id), + GINT_TO_POINTER(stored_id)); - pending_messages_it = g_list_first(imgdata->pending_messages); - while (pending_messages_it) + req = g_hash_table_lookup(sdata->incoming_images, &id); + if (!req) { - ggp_image_pending_message *pending_message = - (ggp_image_pending_message*)pending_messages_it->data; - gchar *newText; - - if (strstr(pending_message->text, imgtag_search) == NULL) - { - pending_messages_it = g_list_next(pending_messages_it); - continue; - } - - purple_debug_misc("gg", "ggp_image_recv: found message " - "containing image: %s\n", pending_message->text); + purple_debug_warning("gg", "ggp_image_recv: " + "image " GGP_IMAGE_ID_FORMAT " wasn't requested\n", + id); + return; + } + + it = g_list_first(req->listeners); + while (it) + { + ggp_image_requested_listener *listener = it->data; + it = g_list_next(it); - newText = purple_strreplace(pending_message->text, - imgtag_search, imgtag_replace); - g_free(pending_message->text); - pending_message->text = newText; - - if (strstr(pending_message->text, - "<img id=\"" GGP_PENDING_IMAGE_ID_PREFIX) == NULL) - { - purple_debug_info("gg", "ggp_image_recv: " - "message is ready to display\n"); - serv_got_im(gc, ggp_uin_to_str(pending_message->from), - pending_message->text, - PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_IMAGES, - pending_message->mtime); - - ggp_image_pending_message_free(pending_message); - imgdata->pending_messages = g_list_remove( - imgdata->pending_messages, pending_message); - } - - pending_messages_it = g_list_next(pending_messages_it); + listener->cb(gc, id, stored_id, listener->user_data); } - g_free(imgtag_replace); + g_hash_table_remove(sdata->incoming_images, &id); } void ggp_image_send(PurpleConnection *gc, const struct gg_event_image_request *image_request) { GGPInfo *accdata = purple_connection_get_protocol_data(gc); - ggp_image_connection_data *imgdata = ggp_image_get_imgdata(gc); - ggp_image_pending_image *pending_image; + ggp_image_session_data *sdata = ggp_image_get_sdata(gc); + ggp_image_sent *sent_image; PurpleStoredImage *image; PurpleConversation *conv; + uint64_t id; + gchar *gg_filename; purple_debug_info("gg", "ggp_image_send: got image request " "[uin=%u, crc=%u, size=%u]\n", @@ -257,10 +239,18 @@ image_request->crc32, image_request->size); - pending_image = g_hash_table_lookup(imgdata->pending_images, - GINT_TO_POINTER(image_request->crc32)); + id = ggp_image_params_to_id(image_request->crc32, image_request->size); + + sent_image = g_hash_table_lookup(sdata->sent_images, &id); - if (pending_image == NULL) + if (sent_image == NULL && image_request->sender == ggp_str_to_uin( + purple_account_get_username(purple_connection_get_account(gc)))) + { + purple_debug_misc("gg", "ggp_image_send: requested image " + "not found, but this may be another session request\n"); + return; + } + if (sent_image == NULL) { purple_debug_warning("gg", "ggp_image_send: requested image " "not found\n"); @@ -268,35 +258,91 @@ } purple_debug_misc("gg", "ggp_image_send: requested image found " - "[id=%d, conv=%s]\n", - pending_image->id, - pending_image->conv_name); + "[id=" GGP_IMAGE_ID_FORMAT ", stored id=%d, conv=%s]\n", + id, + sent_image->id, + sent_image->conv_name); - image = purple_imgstore_find_by_id(pending_image->id); + image = purple_imgstore_find_by_id(sent_image->id); if (!image) { purple_debug_error("gg", "ggp_image_send: requested image " "found, but doesn't exists in image store\n"); - g_hash_table_remove(imgdata->pending_images, + g_hash_table_remove(sdata->sent_images, GINT_TO_POINTER(image_request->crc32)); return; } //TODO: check allowed recipients + gg_filename = g_strdup_printf(GGP_IMAGE_ID_FORMAT, id); gg_image_reply(accdata->session, image_request->sender, - purple_imgstore_get_filename(image), + gg_filename, purple_imgstore_get_data(image), purple_imgstore_get_size(image)); - purple_imgstore_unref(image); + g_free(gg_filename); - conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account( - pending_image->conv_name, purple_connection_get_account(gc))); + conv = purple_conversations_find_with_account( + sent_image->conv_name, + purple_connection_get_account(gc)); if (conv != NULL) - purple_conversation_write(conv, "", _("Image delivered."), + { + gchar *msg = g_strdup_printf(_("Image delivered to %u."), + image_request->sender); + purple_conversation_write(conv, "", msg, PURPLE_MESSAGE_NO_LOG | PURPLE_MESSAGE_NOTIFY, time(NULL)); + g_free(msg); + } +} + +void ggp_image_request(PurpleConnection *gc, uin_t uin, uint64_t id, + ggp_image_request_cb cb, gpointer user_data) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + ggp_image_session_data *sdata = ggp_image_get_sdata(gc); + ggp_image_requested *req; + ggp_image_requested_listener *listener; + uint32_t crc = id >> 32; + uint32_t size = id; - g_hash_table_remove(imgdata->pending_images, - GINT_TO_POINTER(image_request->crc32)); + if (size > GGP_IMAGE_SIZE_MAX && crc <= GGP_IMAGE_SIZE_MAX) + { + uint32_t tmp; + purple_debug_warning("gg", "ggp_image_request: " + "crc and size are swapped!\n"); + tmp = crc; + crc = size; + size = tmp; + } + + req = g_hash_table_lookup(sdata->incoming_images, &id); + if (!req) + { + req = g_new0(ggp_image_requested, 1); + g_hash_table_insert(sdata->incoming_images, + ggp_uint64dup(id), req); + purple_debug_info("gg", "ggp_image_request: " + "requesting image " GGP_IMAGE_ID_FORMAT "\n", id); + if (gg_image_request(accdata->session, uin, size, crc) != 0) + purple_debug_error("gg", "ggp_image_request: failed\n"); + } + else + { + purple_debug_info("gg", "ggp_image_request: " + "image " GGP_IMAGE_ID_FORMAT " already requested\n", + id); + } + + listener = g_new0(ggp_image_requested_listener, 1); + listener->cb = cb; + listener->user_data = user_data; + req->listeners = g_list_append(req->listeners, listener); } + +int ggp_image_get_cached(PurpleConnection *gc, uint64_t id) +{ + ggp_image_session_data *sdata = ggp_image_get_sdata(gc); + + return GPOINTER_TO_INT(g_hash_table_lookup(sdata->got_images, &id)); +}
--- a/libpurple/protocols/gg/image.h Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/image.h Mon Sep 16 18:08:19 2013 +0530 @@ -34,12 +34,9 @@ #include <libgadu.h> #define GGP_IMAGE_SIZE_MAX 255000 +#define GGP_IMAGE_ID_FORMAT "%016" G_GINT64_MODIFIER "x" -typedef struct -{ - GList *pending_messages; - GHashTable *pending_images; -} ggp_image_connection_data; +typedef struct _ggp_image_session_data ggp_image_session_data; typedef enum { @@ -48,19 +45,21 @@ GGP_IMAGE_PREPARE_TOO_BIG } ggp_image_prepare_result; +typedef void (*ggp_image_request_cb)(PurpleConnection *gc, uint64_t id, + int stored_id, gpointer user_data); + void ggp_image_setup(PurpleConnection *gc); void ggp_image_cleanup(PurpleConnection *gc); -const char * ggp_image_pending_placeholder(uint32_t id); - -void ggp_image_got_im(PurpleConnection *gc, uin_t from, gchar *msg, - time_t mtime); -ggp_image_prepare_result ggp_image_prepare(PurpleConnection *gc, const int id, - const char *conv_name, struct gg_msg_richtext_image *image_info); +ggp_image_prepare_result ggp_image_prepare(PurpleConversation *conv, + const int stored_id, uint64_t *id); void ggp_image_recv(PurpleConnection *gc, const struct gg_event_image_reply *image_reply); void ggp_image_send(PurpleConnection *gc, const struct gg_event_image_request *image_request); +void ggp_image_request(PurpleConnection *gc, uin_t uin, uint64_t id, + ggp_image_request_cb cb, gpointer user_data); +int ggp_image_get_cached(PurpleConnection *gc, uint64_t id); #endif /* _GGP_IMAGE_H */
--- a/libpurple/protocols/gg/libgadu-events.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/libgadu-events.c Mon Sep 16 18:08:19 2013 +0530 @@ -32,6 +32,7 @@ #include <debug.h> #include "avatar.h" +#include "edisc.h" void ggp_events_user_data(PurpleConnection *gc, struct gg_event_user_data *data) { @@ -81,3 +82,47 @@ ggp_avatar_buddy_remove(gc, uin); } } + +#if GGP_ENABLE_GG11 +static void ggp_events_new_version(const gchar *data) +{ + /* data = {"severity":"download"} */ + purple_debug_info("gg", "Gadu-Gadu server reports new client version." + " %s", data); +} + +void ggp_events_json(PurpleConnection *gc, struct gg_event_json_event *ev) +{ + static const gchar *ignored_events[] = { + "edisc/scope_files_changed", + "notifications/state", + "invitations/list", + "notifications/list", /* gifts */ + NULL + }; + const gchar **it; + + if (g_strcmp0("edisc/send_ticket_changed", ev->type) == 0) { + ggp_edisc_xfer_ticket_changed(gc, ev->data); + return; + } + + if (g_strcmp0("updates/new-version", ev->type) == 0) { + ggp_events_new_version(ev->data); + return; + } + + for (it = ignored_events; *it != NULL; it++) { + if (g_strcmp0(*it, ev->type) == 0) + return; + } + + if (purple_debug_is_unsafe() && purple_debug_is_verbose()) + purple_debug_warning("gg", "ggp_events_json: " + "unhandled event \"%s\": %s\n", + ev->type, ev->data); + else + purple_debug_warning("gg", "ggp_events_json: " + "unhandled event \"%s\"\n", ev->type); +} +#endif
--- a/libpurple/protocols/gg/libgadu-events.h Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/libgadu-events.h Mon Sep 16 18:08:19 2013 +0530 @@ -33,7 +33,13 @@ #include <internal.h> #include <libgadu.h> +#include "gg.h" + void ggp_events_user_data(PurpleConnection *gc, struct gg_event_user_data *data); +#if GGP_ENABLE_GG11 +void ggp_events_json(PurpleConnection *gc, struct gg_event_json_event *ev); +#endif + #endif /* _GGP_LIBGADU_EVENTS_H */
--- a/libpurple/protocols/gg/libgaduw.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/libgaduw.c Mon Sep 16 18:08:19 2013 +0530 @@ -32,6 +32,75 @@ #include <debug.h> #include "purplew.h" +#include "gg.h" + +static void ggp_libgaduw_debug_handler(int level, const char * format, + va_list args); + +/******************************************************************************* + * Setup/cleanup. + ******************************************************************************/ + +void ggp_libgaduw_setup(void) +{ + gg_debug_handler = ggp_libgaduw_debug_handler; +} + +void ggp_libgaduw_cleanup(void) +{ + gg_debug_handler = NULL; +} + +/******************************************************************************* + * General. + ******************************************************************************/ + +const gchar * ggp_libgaduw_version(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + const gchar *ver = accdata->session->client_version; + + if (ver != NULL && isdigit(ver[0])) + return ver; + return GG_DEFAULT_CLIENT_VERSION; +} + +static void ggp_libgaduw_debug_handler(int level, const char * format, + va_list args) +{ + PurpleDebugLevel purple_level; + char *msg; + + if ((level & GG_DEBUG_NET) || + (level & GG_DEBUG_FUNCTION) +#if GGP_ENABLE_GG11 + || (level & GG_DEBUG_VERBOSE) +#endif + ) { + if (!purple_debug_is_verbose()) + return; + } + + if ((level & GG_DEBUG_DUMP) || /* GG session protocol packets */ + (level & GG_DEBUG_TRAFFIC)) { /* HTTP traffic */ + if (!purple_debug_is_verbose() || !purple_debug_is_unsafe()) + return; + } + + msg = g_strdup_vprintf(format, args); + +#if GGP_ENABLE_GG11 + if (level & GG_DEBUG_ERROR) + purple_level = PURPLE_DEBUG_ERROR; + else if (level & GG_DEBUG_WARNING) + purple_level = PURPLE_DEBUG_WARNING; + else +#endif + purple_level = PURPLE_DEBUG_MISC; + + purple_debug(purple_level, "gg", "%s", msg); + g_free(msg); +} /******************************************************************************* * HTTP requests.
--- a/libpurple/protocols/gg/libgaduw.h Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/libgaduw.h Mon Sep 16 18:08:19 2013 +0530 @@ -49,10 +49,14 @@ guint inpa; } ggp_libgaduw_http_req; +void ggp_libgaduw_setup(void); +void ggp_libgaduw_cleanup(void); + +const gchar * ggp_libgaduw_version(PurpleConnection *gc); + ggp_libgaduw_http_req * ggp_libgaduw_http_watch(PurpleConnection *gc, struct gg_http *h, ggp_libgaduw_http_cb cb, gpointer user_data, gboolean show_processing); void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req); - #endif /* _GGP_LIBGADUW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/message-prpl.c Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,805 @@ +#include "message-prpl.h" + +#include <debug.h> + +#include "gg.h" +#include "chat.h" +#include "utils.h" +#include "html.h" + +#define GGP_GG10_DEFAULT_FORMAT "<span style=\"color:#000000; " \ + "font-family:'MS Shell Dlg 2'; font-size:9pt; \">" +#define GGP_GG10_DEFAULT_FORMAT_REPLACEMENT "<span>" +#define GGP_GG11_FORCE_COMPAT FALSE + +#define GGP_IMAGE_REPLACEMENT "<img id=\"gg-pending-image-" GGP_IMAGE_ID_FORMAT "\">" +#define GGP_IMAGE_DESTINATION "<img src=\"" PURPLE_STORED_IMAGE_PROTOCOL "%u\">" + +typedef struct +{ + enum + { + GGP_MESSAGE_GOT_TYPE_IM, + GGP_MESSAGE_GOT_TYPE_CHAT, + GGP_MESSAGE_GOT_TYPE_MULTILOGON + } type; + + uin_t user; + gchar *text; + time_t time; + uint64_t chat_id; + GList *pending_images; + + PurpleConnection *gc; +} ggp_message_got_data; + +typedef struct +{ + GRegex *re_html_tag; + GRegex *re_gg_img; +} ggp_message_global_data; + +static ggp_message_global_data global_data; + +struct _ggp_message_session_data +{ + GList *pending_messages; +}; + +typedef struct +{ + int size; + gchar *face; + int color, bgcolor; + gboolean b, i, u, s; +} ggp_font; + +static ggp_font * ggp_font_new(void); +static ggp_font * ggp_font_clone(ggp_font *font); +static void ggp_font_free(gpointer font); + +static PurpleIMConversation * ggp_message_get_conv(PurpleConnection *gc, + uin_t uin); +static void ggp_message_got_data_free(ggp_message_got_data *msg); +static gboolean ggp_message_request_images(PurpleConnection *gc, + ggp_message_got_data *msg); +static void ggp_message_got_display(PurpleConnection *gc, + ggp_message_got_data *msg); +static void ggp_message_format_from_gg(ggp_message_got_data *msg, + const gchar *text); + +/**************/ + +void ggp_message_setup_global(void) +{ + global_data.re_html_tag = g_regex_new( + "<(/)?([a-zA-Z]+)( [^>]+)?>", + G_REGEX_OPTIMIZE, 0, NULL); + global_data.re_gg_img = g_regex_new( + "<img name=\"([0-9a-fA-F]+)\"/?>", + G_REGEX_OPTIMIZE, 0, NULL); +} + +void ggp_message_cleanup_global(void) +{ + g_regex_unref(global_data.re_html_tag); + g_regex_unref(global_data.re_gg_img); +} + +static inline ggp_message_session_data * +ggp_message_get_sdata(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + return accdata->message_data; +} + +void ggp_message_setup(PurpleConnection *gc) +{ + GGPInfo *accdata = purple_connection_get_protocol_data(gc); + ggp_message_session_data *sdata = g_new0(ggp_message_session_data, 1); + + accdata->message_data = sdata; +} + +void ggp_message_cleanup(PurpleConnection *gc) +{ + ggp_message_session_data *sdata = ggp_message_get_sdata(gc); + + g_list_free_full(sdata->pending_messages, + (GDestroyNotify)ggp_message_got_data_free); + g_free(sdata); +} + +static ggp_font * ggp_font_new(void) +{ + ggp_font *font; + + font = g_new0(ggp_font, 1); + font->color = -1; + font->bgcolor = -1; + + return font; +} + +static ggp_font * ggp_font_clone(ggp_font * font) +{ + ggp_font *clone = g_new0(ggp_font, 1); + + *clone = *font; + clone->face = g_strdup(font->face); + + return clone; +} + +static void ggp_font_free(gpointer _font) +{ + ggp_font *font = _font; + + g_free(font->face); + g_free(font); +} + +/**/ + +static PurpleIMConversation * ggp_message_get_conv(PurpleConnection *gc, + uin_t uin) +{ + PurpleAccount *account = purple_connection_get_account(gc); + PurpleIMConversation *im; + const gchar *who = ggp_uin_to_str(uin); + + im = purple_conversations_find_im_with_account(who, account); + if (im) + return im; + im = purple_im_conversation_new(account, who); + return im; +} + +static void ggp_message_got_data_free(ggp_message_got_data *msg) +{ + g_list_free_full(msg->pending_images, g_free); + g_free(msg->text); + g_free(msg); +} + +static void ggp_message_request_images_got(PurpleConnection *gc, uint64_t id, + int stored_id, gpointer _msg) +{ + ggp_message_session_data *sdata = ggp_message_get_sdata(gc); + ggp_message_got_data *msg = _msg; + GList *m_it, *i_it; + gchar *tmp, *tag_search, *tag_replace; + + m_it = g_list_find(sdata->pending_messages, msg); + if (!m_it) + { + purple_debug_error("gg", "ggp_message_request_images_got: " + "message %p is not in queue\n", msg); + return; + } + + i_it = g_list_find_custom(msg->pending_images, &id, ggp_int64_compare); + if (!i_it) + { + purple_debug_error("gg", "ggp_message_request_images_got: " + "image " GGP_IMAGE_ID_FORMAT " is not present in this " + "message\n", id); + return; + } + + tag_search = g_strdup_printf(GGP_IMAGE_REPLACEMENT, id); + tag_replace = g_strdup_printf(GGP_IMAGE_DESTINATION, stored_id); + + tmp = msg->text; + msg->text = purple_strreplace(msg->text, tag_search, tag_replace); + g_free(tmp); + + g_free(tag_search); + g_free(tag_replace); + + g_free(i_it->data); + msg->pending_images = g_list_delete_link(msg->pending_images, i_it); + if (msg->pending_images != NULL) + { + purple_debug_info("gg", "ggp_message_request_images_got: " + "got image " GGP_IMAGE_ID_FORMAT ", but the message " + "contains more of them\n", id); + return; + } + + ggp_message_got_display(gc, msg); + ggp_message_got_data_free(msg); + sdata->pending_messages = g_list_delete_link(sdata->pending_messages, + m_it); +} + +static gboolean ggp_message_request_images(PurpleConnection *gc, + ggp_message_got_data *msg) +{ + ggp_message_session_data *sdata = ggp_message_get_sdata(gc); + GList *it; + if (msg->pending_images == NULL) + return FALSE; + + it = msg->pending_images; + while (it) + { + ggp_image_request(gc, msg->user, *(uint64_t*)it->data, + ggp_message_request_images_got, msg); + it = g_list_next(it); + } + + sdata->pending_messages = g_list_append(sdata->pending_messages, msg); + + return TRUE; +} + +void ggp_message_got(PurpleConnection *gc, const struct gg_event_msg *ev) +{ + ggp_message_got_data *msg = g_new0(ggp_message_got_data, 1); + + msg->gc = gc; + msg->time = ev->time; + msg->user = ev->sender; + +#if GGP_ENABLE_GG11 + if (ev->chat_id != 0) + { + msg->type = GGP_MESSAGE_GOT_TYPE_CHAT; + msg->chat_id = ev->chat_id; + } + else +#endif + { + msg->type = GGP_MESSAGE_GOT_TYPE_IM; + } + + ggp_message_format_from_gg(msg, ev->xhtml_message); + if (ggp_message_request_images(gc, msg)) + return; + + ggp_message_got_display(gc, msg); + ggp_message_got_data_free(msg); +} + +void ggp_message_got_multilogon(PurpleConnection *gc, + const struct gg_event_msg *ev) +{ + ggp_message_got_data *msg = g_new0(ggp_message_got_data, 1); + + msg->gc = gc; + msg->time = ev->time; + msg->user = ev->sender; /* not really a sender*/ + +#if GGP_ENABLE_GG11 + if (ev->chat_id != 0) + { + msg->type = GGP_MESSAGE_GOT_TYPE_CHAT; + msg->chat_id = ev->chat_id; + } + else +#endif + { + msg->type = GGP_MESSAGE_GOT_TYPE_MULTILOGON; + } + + ggp_message_format_from_gg(msg, ev->xhtml_message); + if (ggp_message_request_images(gc, msg)) + return; + + ggp_message_got_display(gc, msg); + ggp_message_got_data_free(msg); +} + +static void ggp_message_got_display(PurpleConnection *gc, + ggp_message_got_data *msg) +{ + if (msg->type == GGP_MESSAGE_GOT_TYPE_IM) + { + serv_got_im(gc, ggp_uin_to_str(msg->user), msg->text, + PURPLE_MESSAGE_RECV, msg->time); + } +#if GGP_ENABLE_GG11 + else if (msg->type == GGP_MESSAGE_GOT_TYPE_CHAT) + { + ggp_chat_got_message(gc, msg->chat_id, msg->text, msg->time, + msg->user); + } +#endif + else if (msg->type == GGP_MESSAGE_GOT_TYPE_MULTILOGON) + { + PurpleIMConversation *im = ggp_message_get_conv(gc, msg->user); + const gchar *me = purple_account_get_username( + purple_connection_get_account(gc)); + + purple_conversation_write(PURPLE_CONVERSATION(im), me, msg->text, + PURPLE_MESSAGE_SEND, msg->time); + } + else + purple_debug_error("gg", "ggp_message_got_display: " + "unexpected message type: %d\n", msg->type); +} + +static gboolean ggp_message_format_from_gg_found_img(const GMatchInfo *info, + GString *res, gpointer data) +{ + ggp_message_got_data *msg = data; + gchar *name, *replacement; + int64_t id; + int stored_id; + + name = g_match_info_fetch(info, 1); + if (sscanf(name, "%" G_GINT64_MODIFIER "x", &id) != 1) + id = 0; + g_free(name); + if (!id) + { + g_string_append(res, "["); + g_string_append(res, _("broken image")); + g_string_append(res, "]"); + return FALSE; + } + + stored_id = ggp_image_get_cached(msg->gc, id); + + if (stored_id > 0) + { + purple_debug_info("gg", "ggp_message_format_from_gg_found_img: " + "getting image " GGP_IMAGE_ID_FORMAT " from cache\n", + id); + replacement = g_strdup_printf(GGP_IMAGE_DESTINATION, stored_id); + g_string_append(res, replacement); + g_free(replacement); + return FALSE; + } + + if (NULL == g_list_find_custom(msg->pending_images, &id, + ggp_int64_compare)) + { + msg->pending_images = g_list_append(msg->pending_images, + ggp_uint64dup(id)); + } + + replacement = g_strdup_printf(GGP_IMAGE_REPLACEMENT, id); + g_string_append(res, replacement); + g_free(replacement); + + return FALSE; +} + +static void ggp_message_format_from_gg(ggp_message_got_data *msg, + const gchar *text) +{ + gchar *text_new, *tmp; + + if (text == NULL) + { + msg->text = g_strdup(""); + return; + } + + text_new = g_strdup(text); + purple_str_strip_char(text_new, '\r'); + + tmp = text_new; + text_new = purple_strreplace(text_new, GGP_GG10_DEFAULT_FORMAT, + GGP_GG10_DEFAULT_FORMAT_REPLACEMENT); + g_free(tmp); + + tmp = text_new; + text_new = g_regex_replace_eval(global_data.re_gg_img, text_new, -1, 0, + 0, ggp_message_format_from_gg_found_img, msg, NULL); + g_free(tmp); + + msg->text = text_new; +} + +gchar * ggp_message_format_to_gg(PurpleConversation *conv, const gchar *text) +{ + gchar *text_new, *tmp; + GList *rt = NULL; /* reformatted text */ + GMatchInfo *match; + guint pos = 0; + GList *pending_objects = NULL; + GList *font_stack = NULL; + static int html_sizes_pt[7] = { 7, 8, 9, 10, 12, 14, 16 }; + + ggp_font *font_new, *font_current, *font_base; + gboolean font_changed = FALSE; + gboolean in_any_tag = FALSE; + + /* TODO: verbose + * purple_debug_info("gg", "ggp formatting text: [%s]\n", text); + */ + + /* default font */ + font_base = ggp_font_new(); + font_current = ggp_font_new(); + font_new = ggp_font_new(); + + /* GG11 doesn't use nbsp, it just print spaces */ + text_new = purple_strreplace(text, " ", " "); + + /* add end-of-message tag */ + if (strstr(text_new, "<eom>") != NULL) + { + tmp = text_new; + text_new = purple_strreplace(text_new, "<eom>", ""); + g_free(tmp); + purple_debug_warning("gg", "ggp_message_format_to_gg: " + "unexpected <eom> tag\n"); + } + tmp = text_new; + text_new = g_strdup_printf("%s<eom></eom>", text_new); + g_free(tmp); + + g_regex_match(global_data.re_html_tag, text_new, 0, &match); + while (g_match_info_matches(match)) + { + int m_start, m_end, m_pos; + gboolean tag_close; + gchar *tag_str, *attribs_str; + ggp_html_tag tag; + gboolean text_before; + + /* reading tag and its contents */ + g_match_info_fetch_pos(match, 0, &m_start, &m_end); + g_assert(m_start >= 0 && m_end >= 0); + text_before = ((guint)m_start > pos); + g_match_info_fetch_pos(match, 1, &m_pos, NULL); + tag_close = (m_pos >= 0); + tag_str = g_match_info_fetch(match, 2); + tag = ggp_html_parse_tag(tag_str); + attribs_str = g_match_info_fetch(match, 3); + g_match_info_next(match, NULL); + + if (tag == GGP_HTML_TAG_UNKNOWN) + purple_debug_warning("gg", "ggp_message_format_to_gg: " + "uknown tag %s\n", tag_str); + + /* closing *all* formatting-related tags (GG11 weirness) + * and adding pending objects */ + if ((text_before && (font_changed || pending_objects)) || + (tag == GGP_HTML_TAG_EOM && tag_close)) + { + font_changed = FALSE; + if (in_any_tag) + { + in_any_tag = FALSE; + if (font_current->s && !GGP_GG11_FORCE_COMPAT) + rt = g_list_prepend(rt, + g_strdup("</s>")); + if (font_current->u) + rt = g_list_prepend(rt, + g_strdup("</u>")); + if (font_current->i) + rt = g_list_prepend(rt, + g_strdup("</i>")); + if (font_current->b) + rt = g_list_prepend(rt, + g_strdup("</b>")); + rt = g_list_prepend(rt, g_strdup("</span>")); + } + if (pending_objects) + { + rt = g_list_concat(pending_objects, rt); + pending_objects = NULL; + } + } + + /* opening formatting-related tags again */ + if (text_before && !in_any_tag) + { + gchar *style; + GList *styles = NULL; + gboolean has_size = (font_new->size > 0 && + font_new->size <= 7 && font_new->size != 3); + + if (has_size) + styles = g_list_append(styles, g_strdup_printf( + "font-size:%dpt;", + html_sizes_pt[font_new->size - 1])); + if (font_new->face) + styles = g_list_append(styles, g_strdup_printf( + "font-family:%s;", font_new->face)); + if (font_new->bgcolor >= 0 && !GGP_GG11_FORCE_COMPAT) + styles = g_list_append(styles, g_strdup_printf( + "background-color:#%06x;", + font_new->bgcolor)); + if (font_new->color >= 0) + styles = g_list_append(styles, g_strdup_printf( + "color:#%06x;", font_new->color)); + + if (styles) + { + gchar *combined = ggp_strjoin_list(" ", styles); + g_list_free_full(styles, g_free); + style = g_strdup_printf(" style=\"%s\"", + combined); + g_free(combined); + } + else + style = g_strdup(""); + rt = g_list_prepend(rt, g_strdup_printf("<span%s>", + style)); + g_free(style); + + if (font_new->b) + rt = g_list_prepend(rt, g_strdup("<b>")); + if (font_new->i) + rt = g_list_prepend(rt, g_strdup("<i>")); + if (font_new->u) + rt = g_list_prepend(rt, g_strdup("<u>")); + if (font_new->s && !GGP_GG11_FORCE_COMPAT) + rt = g_list_prepend(rt, g_strdup("<s>")); + + ggp_font_free(font_current); + font_current = font_new; + font_new = ggp_font_clone(font_current); + + in_any_tag = TRUE; + } + if (text_before) + { + rt = g_list_prepend(rt, + g_strndup(text_new + pos, m_start - pos)); + } + + /* set formatting of a following text */ + if (tag == GGP_HTML_TAG_B) + { + font_changed |= (font_new->b != !tag_close); + font_new->b = !tag_close; + } + else if (tag == GGP_HTML_TAG_I) + { + font_changed |= (font_new->i != !tag_close); + font_new->i = !tag_close; + } + else if (tag == GGP_HTML_TAG_U) + { + font_changed |= (font_new->u != !tag_close); + font_new->u = !tag_close; + } + else if (tag == GGP_HTML_TAG_S) + { + font_changed |= (font_new->s != !tag_close); + font_new->s = !tag_close; + } + else if (tag == GGP_HTML_TAG_IMG && !tag_close) + { + GHashTable *attribs = ggp_html_tag_attribs(attribs_str); + gchar *val = NULL; + uint64_t id; + int stored_id = -1; + ggp_image_prepare_result res = -1; + + if ((val = g_hash_table_lookup(attribs, "src")) != NULL + && g_str_has_prefix(val, + PURPLE_STORED_IMAGE_PROTOCOL)) + { + val += strlen(PURPLE_STORED_IMAGE_PROTOCOL); + if (sscanf(val, "%u", &stored_id) != 1) + stored_id = -1; + } + + if (stored_id >= 0) + res = ggp_image_prepare(conv, stored_id, &id); + + if (res == GGP_IMAGE_PREPARE_OK) + { + pending_objects = g_list_prepend( + pending_objects, g_strdup_printf( + "<img name=\"" GGP_IMAGE_ID_FORMAT + "\">", id)); + } + else if (res == GGP_IMAGE_PREPARE_TOO_BIG) + { + purple_conversation_write(conv, "", + _("Image is too large, please try " + "smaller one."), PURPLE_MESSAGE_ERROR, + time(NULL)); + } + else + { + purple_conversation_write(conv, "", + _("Image cannot be sent."), + PURPLE_MESSAGE_ERROR, time(NULL)); + } + + g_hash_table_destroy(attribs); + } + else if (tag == GGP_HTML_TAG_FONT && !tag_close) + { + GHashTable *attribs = ggp_html_tag_attribs(attribs_str); + gchar *val = NULL; + + font_stack = g_list_prepend(font_stack, + ggp_font_clone(font_new)); + + if ((val = g_hash_table_lookup(attribs, "size")) != NULL + && val[0] >= '1' && val[0] <= '7' && + val[1] == '\0') + { + int size = val[0] - '0'; + font_changed |= (font_new->size != size); + font_new->size = size; + } + + if ((val = g_hash_table_lookup(attribs, "face")) + != NULL) + { + font_changed |= + (g_strcmp0(font_new->face, val) != 0); + g_free(font_new->face); + font_new->face = g_strdup(val); + } + + if ((val = g_hash_table_lookup(attribs, "color")) + != NULL && val[0] == '#' && strlen(val) == 7) + { + int color = ggp_html_decode_color(val); + font_changed |= (font_new->color != color); + font_new->color = color; + } + + g_hash_table_destroy(attribs); + } + else if ((tag == GGP_HTML_TAG_SPAN || tag == GGP_HTML_TAG_DIV) + && !tag_close) + { + GHashTable *attribs, *styles = NULL; + gchar *style = NULL; + gchar *val = NULL; + + attribs = ggp_html_tag_attribs(attribs_str); + + font_stack = g_list_prepend(font_stack, + ggp_font_clone(font_new)); + if (tag == GGP_HTML_TAG_DIV) + pending_objects = g_list_prepend( + pending_objects, g_strdup("<br>")); + + style = g_hash_table_lookup(attribs, "style"); + if (style) + styles = ggp_html_css_attribs(style); + + if ((val = g_hash_table_lookup(styles, + "background-color")) != NULL) + { + int color = ggp_html_decode_color(val); + font_changed |= (font_new->bgcolor != color); + font_new->bgcolor = color; + } + + if ((val = g_hash_table_lookup(styles, + "color")) != NULL) + { + int color = ggp_html_decode_color(val); + font_changed |= (font_new->color != color); + font_new->color = color; + } + + if (styles) + g_hash_table_destroy(styles); + g_hash_table_destroy(attribs); + } + else if ((tag == GGP_HTML_TAG_FONT || tag == GGP_HTML_TAG_SPAN + || tag == GGP_HTML_TAG_DIV) && tag_close) + { + font_changed = TRUE; + + ggp_font_free(font_new); + if (font_stack) + { + font_new = (ggp_font*)font_stack->data; + font_stack = g_list_delete_link( + font_stack, font_stack); + } + else + font_new = ggp_font_clone(font_base); + } + else if (tag == GGP_HTML_TAG_BR) + { + pending_objects = g_list_prepend(pending_objects, + g_strdup("<br>")); + } + else if (tag == GGP_HTML_TAG_HR) + { + pending_objects = g_list_prepend(pending_objects, + g_strdup("<br><span>---</span><br>")); + } + else if (tag == GGP_HTML_TAG_A || tag == GGP_HTML_TAG_EOM) + { + /* do nothing */ + } + else if (tag == GGP_HTML_TAG_UNKNOWN) + { + purple_debug_warning("gg", "ggp_message_format_to_gg: " + "uknown tag %s\n", tag_str); + } + else + { + purple_debug_error("gg", "ggp_message_format_to_gg: " + "not handled tag %s\n", tag_str); + } + + pos = m_end; + g_free(tag_str); + g_free(attribs_str); + } + g_match_info_free(match); + + if (pos < strlen(text_new) || in_any_tag) + { + purple_debug_fatal("gg", "ggp_message_format_to_gg: " + "end of message not reached\n"); + } + + /* releasing fonts recources */ + ggp_font_free(font_new); + ggp_font_free(font_current); + ggp_font_free(font_base); + g_list_free_full(font_stack, ggp_font_free); + + /* combining reformatted text info one string */ + rt = g_list_reverse(rt); + g_free(text_new); + text_new = ggp_strjoin_list("", rt); + g_list_free_full(rt, g_free); + + /* TODO: verbose + * purple_debug_info("gg", "reformatted text: [%s]\n", text_new); + */ + + return text_new; +} + +int ggp_message_send_im(PurpleConnection *gc, const char *who, + const char *message, PurpleMessageFlags flags) +{ + GGPInfo *info = purple_connection_get_protocol_data(gc); + PurpleIMConversation *im; + ggp_buddy_data *buddy_data; + gchar *gg_msg; + gboolean succ; + + /* TODO: return -ENOTCONN, if not connected */ + + if (message == NULL || message[0] == '\0') + return 0; + + buddy_data = ggp_buddy_get_data(purple_blist_find_buddy( + purple_connection_get_account(gc), who)); + + if (buddy_data->blocked) + return -1; + + im = purple_conversations_find_im_with_account( + who, purple_connection_get_account(gc)); + + gg_msg = ggp_message_format_to_gg(PURPLE_CONVERSATION(im), message); + + /* TODO: splitting messages */ + if (strlen(gg_msg) > GG_MSG_MAXSIZE) + { + g_free(gg_msg); + return -E2BIG; + } + +#if GGP_ENABLE_GG11 + succ = (gg_send_message_html(info->session, GG_CLASS_CHAT, + ggp_str_to_uin(who), (unsigned char *)gg_msg) >= 0); +#else + { + gchar *plain = purple_markup_strip_html(gg_msg); + succ = (gg_send_message(info->session, GG_CLASS_CHAT, + ggp_str_to_uin(who), (unsigned char *)plain) >= 0); + g_free(plain); + } +#endif + + g_free(gg_msg); + + return succ ? 1 : -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/message-prpl.h Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,22 @@ +#ifndef _GGP_MESSAGE_PRPL_H +#define _GGP_MESSAGE_PRPL_H + +#include <internal.h> +#include <libgadu.h> + +typedef struct _ggp_message_session_data ggp_message_session_data; + +void ggp_message_setup_global(void); +void ggp_message_cleanup_global(void); +void ggp_message_setup(PurpleConnection *gc); +void ggp_message_cleanup(PurpleConnection *gc); + +void ggp_message_got(PurpleConnection *gc, const struct gg_event_msg *ev); +void ggp_message_got_multilogon(PurpleConnection *gc, + const struct gg_event_msg *ev); + +int ggp_message_send_im(PurpleConnection *gc, const char *who, + const char *message, PurpleMessageFlags flags); +gchar * ggp_message_format_to_gg(PurpleConversation *conv, const gchar *text); + +#endif /* _GGP_MESSAGE_PRPL_H */
--- a/libpurple/protocols/gg/multilogon.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/multilogon.c Mon Sep 16 18:08:19 2013 +0530 @@ -24,7 +24,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "multilogon.h" @@ -32,6 +32,8 @@ #include <debug.h> #include "gg.h" +#include "utils.h" +#include "message-prpl.h" struct _ggp_multilogon_session_data { @@ -54,7 +56,8 @@ { GGPInfo *accdata = purple_connection_get_protocol_data(gc); - ggp_multilogon_session_data *mldata = g_new0(ggp_multilogon_session_data, 1); + ggp_multilogon_session_data *mldata = + g_new0(ggp_multilogon_session_data, 1); accdata->multilogon_data = mldata; } @@ -64,11 +67,6 @@ g_free(mldata); } -void ggp_multilogon_msg(PurpleConnection *gc, struct gg_event_msg *msg) -{ - ggp_recv_message_handler(gc, msg, TRUE); -} - void ggp_multilogon_info(PurpleConnection *gc, struct gg_event_multilogon_info *info) {
--- a/libpurple/protocols/gg/multilogon.h Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/multilogon.h Mon Sep 16 18:08:19 2013 +0530 @@ -38,7 +38,6 @@ void ggp_multilogon_setup(PurpleConnection *gc); void ggp_multilogon_cleanup(PurpleConnection *gc); -void ggp_multilogon_msg(PurpleConnection *gc, struct gg_event_msg *msg); void ggp_multilogon_info(PurpleConnection *gc, struct gg_event_multilogon_info *msg);
--- a/libpurple/protocols/gg/purplew.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/purplew.c Mon Sep 16 18:08:19 2013 +0530 @@ -32,22 +32,18 @@ #include <request.h> #include <debug.h> +#include "tcpsocket.h" + guint ggp_purplew_http_input_add(struct gg_http *http_req, PurpleInputFunction func, gpointer user_data) { - PurpleInputCondition cond = 0; - int check = http_req->check; - - if (check & GG_CHECK_READ) - cond |= PURPLE_INPUT_READ; - if (check & GG_CHECK_WRITE) - cond |= PURPLE_INPUT_WRITE; - //TODO: verbose mode //purple_debug_misc("gg", "ggp_purplew_http_input_add: " // "[req=%x, fd=%d, cond=%d]\n", // (unsigned int)http_req, http_req->fd, cond); - return purple_input_add(http_req->fd, cond, func, user_data); + return purple_input_add(http_req->fd, + ggp_tcpsocket_inputcond_gg_to_purple(http_req->check), + func, user_data); } static void ggp_purplew_request_processing_cancel(
--- a/libpurple/protocols/gg/roster.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/roster.c Mon Sep 16 18:08:19 2013 +0530 @@ -478,7 +478,7 @@ static gboolean ggp_roster_reply_list_read_buddy(PurpleConnection *gc, PurpleXmlNode *node, ggp_roster_content *content, GHashTable *remove_buddies) { - gchar *alias, *group_name; + gchar *alias, *group_name = NULL; uin_t uin; gboolean succ = TRUE; PurpleXmlNode *group_list, *group_elem;
--- a/libpurple/protocols/gg/servconn.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/servconn.c Mon Sep 16 18:08:19 2013 +0530 @@ -68,6 +68,7 @@ void ggp_servconn_add_server(const gchar *server) { GList *old_entry; + gchar *joined; old_entry = g_list_find_custom(global_data.server_history, server, (GCompareFunc)g_strcmp0); @@ -84,8 +85,9 @@ global_data.server_history, GGP_SERVCONN_HISTORY_MAXLEN, g_free); - purple_prefs_set_string(GGP_SERVCONN_HISTORY_PREF, ggp_strjoin_list(";", - global_data.server_history)); + joined = ggp_strjoin_list(";", global_data.server_history); + purple_prefs_set_string(GGP_SERVCONN_HISTORY_PREF, joined); + g_free(joined); purple_account_option_string_set_hints(global_data.server_option, ggp_servconn_get_servers()); }
--- a/libpurple/protocols/gg/status.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/status.c Mon Sep 16 18:08:19 2013 +0530 @@ -164,6 +164,9 @@ case GG_STATUS_NOT_AVAIL: case GG_STATUS_NOT_AVAIL_DESCR: case GG_STATUS_BLOCKED: +#if GGP_ENABLE_GG11 + case GG_STATUS_UNKNOWN: +#endif return purple_primitive_get_id_from_type( PURPLE_STATUS_OFFLINE); case GG_STATUS_FFC: @@ -186,7 +189,7 @@ return purple_primitive_get_id_from_type( PURPLE_STATUS_UNAVAILABLE); default: - purple_debug_warning("gg", "ggp_status_to_purplestatus: unknown status %d\n", status); + purple_debug_warning("gg", "ggp_status_to_purplestatus: unknown status %#02x\n", status); return purple_primitive_get_id_from_type( PURPLE_STATUS_AVAILABLE); } @@ -419,7 +422,12 @@ return; } ggp_buddy_get_data(buddy)->blocked = (status == GG_STATUS_BLOCKED); - +#if GGP_ENABLE_GG11 + ggp_buddy_get_data(buddy)->not_a_friend = (status == GG_STATUS_UNKNOWN); +#else + ggp_buddy_get_data(buddy)->not_a_friend = FALSE; +#endif + if (descr != NULL) { status_message = g_strdup(descr); @@ -437,7 +445,7 @@ "own status changed to %s [%s]\n", purple_status, status_message ? status_message : ""); } - else + else if (purple_debug_is_verbose()) { purple_debug_misc("gg", "ggp_status_got_others_buddy: " "status of %u changed to %s [%s]\n", uin, @@ -462,15 +470,17 @@ { ggp_buddy_data *buddy_data = ggp_buddy_get_data(buddy); const gchar *purple_message; - + if (buddy_data->blocked) return g_strdup(_("Blocked")); - + if (buddy_data->not_a_friend) + return g_strdup(_("Not a buddy")); + purple_message = purple_status_get_attr_string( purple_presence_get_active_status( purple_buddy_get_presence(buddy)), "message"); if (!purple_message) return NULL; - + return g_markup_escape_text(purple_message, -1); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/tcpsocket.c Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,113 @@ +#include "tcpsocket.h" + +#include "gg.h" + +#include "debug.h" +#include "purple-socket.h" + +#if GGP_ENABLE_GG11 + +static void +ggp_tcpsocket_connected(PurpleSocket *ps, const gchar *error, gpointer priv_gg) +{ + PurpleConnection *gc = purple_socket_get_connection(ps); + GGPInfo *info = purple_connection_get_protocol_data(gc); + int fd = -1; + + if (error == NULL) + fd = purple_socket_get_fd(ps); + + if (!gg_socket_manager_connected(ps, priv_gg, fd)) { + purple_debug_error("gg", "socket not handled"); + purple_socket_destroy(ps); + return; + } + + if (info->inpa > 0) + purple_input_remove(info->inpa); + info->inpa = purple_input_add(fd, ggp_tcpsocket_inputcond_gg_to_purple( + info->session->check), ggp_async_login_handler, gc); +} + +static void* +ggp_tcpsocket_connect(void *_gc, const char *host, int port, int is_tls, + int is_async, void *priv) +{ + PurpleConnection *gc = _gc; + PurpleSocket *ps; + + g_return_val_if_fail(host != NULL, NULL); + g_return_val_if_fail(is_async, NULL); + + purple_debug_misc("gg", "ggp_tcpsocket_connect(%p, %s:%d, %s, %p)", + gc, host, port, is_tls ? "tls" : "tcp", priv); + + ps = purple_socket_new(gc); + purple_socket_set_tls(ps, is_tls); + purple_socket_set_host(ps, host); + purple_socket_set_port(ps, port); + if (!purple_socket_connect(ps, ggp_tcpsocket_connected, priv)) { + purple_socket_destroy(ps); + return NULL; + } + + return ps; +} + +static void +ggp_tcpsocket_close(void *_gc, void *_ps) +{ + PurpleSocket *ps = _ps; + + purple_socket_destroy(ps); +} + +static ssize_t +ggp_tcpsocket_read(void *_gc, void *_ps, unsigned char *buffer, size_t bufsize) +{ + PurpleSocket *ps = _ps; + + return purple_socket_read(ps, buffer, bufsize); +} + +static ssize_t +ggp_tcpsocket_write(void *_gc, void *_ps, const unsigned char *data, size_t len) +{ + PurpleSocket *ps = _ps; + + return purple_socket_write(ps, data, len); +} + +void +ggp_tcpsocket_setup(PurpleConnection *gc, struct gg_login_params *glp) +{ + glp->socket_manager_type = purple_ssl_is_supported() ? + GG_SOCKET_MANAGER_TYPE_TLS : GG_SOCKET_MANAGER_TYPE_TCP; + glp->socket_manager.cb_data = gc; + glp->socket_manager.connect = ggp_tcpsocket_connect; + glp->socket_manager.close = ggp_tcpsocket_close; + glp->socket_manager.read = ggp_tcpsocket_read; + glp->socket_manager.write = ggp_tcpsocket_write; +} + +#else + +void +ggp_tcpsocket_setup(PurpleConnection *gc, struct gg_login_params *glp) +{ +} + +#endif + +PurpleInputCondition +ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check) +{ + PurpleInputCondition cond = 0; + + if (check & GG_CHECK_READ) + cond |= PURPLE_INPUT_READ; + if (check & GG_CHECK_WRITE) + cond |= PURPLE_INPUT_WRITE; + + return cond; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/tcpsocket.h Mon Sep 16 18:08:19 2013 +0530 @@ -0,0 +1,13 @@ +#ifndef _GGP_TCPSOCKET_H +#define _GGP_TCPSOCKET_H + +#include <internal.h> +#include <libgadu.h> + +void +ggp_tcpsocket_setup(PurpleConnection *gc, struct gg_login_params *glp); + +PurpleInputCondition +ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check); + +#endif /* _GGP_TCPSOCKET_H */
--- a/libpurple/protocols/gg/utils.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/utils.c Mon Sep 16 18:08:19 2013 +0530 @@ -59,6 +59,14 @@ return buff; } +uin_t ggp_get_my_uin(PurpleConnection *gc) +{ + g_return_val_if_fail(gc != NULL, 0); + + return ggp_str_to_uin(purple_account_get_username( + purple_connection_get_account(gc))); +} + static gchar * ggp_convert(const gchar *src, const char *srcenc, const char *dstenc) { @@ -102,16 +110,6 @@ "<>\\\\|-]+$", password, 0, 0); } -guint64 ggp_microtime(void) -{ - // replace with g_get_monotonic_time, when gtk 2.28 will be available - GTimeVal time_s; - - g_get_current_time(&time_s); - - return ((guint64)time_s.tv_sec << 32) | time_s.tv_usec; -} - gchar * ggp_utf8_strndup(const gchar *str, gsize n) { size_t raw_len = strlen(str); @@ -192,20 +190,6 @@ return joined; } -const gchar * ggp_ipv4_to_str(uint32_t raw_ip) -{ - static gchar buff[INET_ADDRSTRLEN]; - buff[0] = '\0'; - - g_snprintf(buff, sizeof(buff), "%d.%d.%d.%d", - ((raw_ip >> 0) & 0xFF), - ((raw_ip >> 8) & 0xFF), - ((raw_ip >> 16) & 0xFF), - ((raw_ip >> 24) & 0xFF)); - - return buff; -} - GList * ggp_list_truncate(GList *list, guint length, GDestroyNotify free_func) { while (g_list_length(list) > length) @@ -248,3 +232,35 @@ return 0; return g_timeval.tv_sec; } + +uint64_t * ggp_uint64dup(uint64_t val) +{ + uint64_t *ptr = g_new(uint64_t, 1); + *ptr = val; + return ptr; +} + +gint ggp_int64_compare(gconstpointer _a, gconstpointer _b) +{ + const int64_t *ap = _a, *bp = _b; + const int64_t a = *ap, b = *bp; + if (a == b) + return 0; + if (a < b) + return -1; + else + return 1; +} + +JsonParser * ggp_json_parse(const gchar *data) +{ + JsonParser *parser; + + parser = json_parser_new(); + if (json_parser_load_from_data(parser, data, -1, NULL)) + return parser; + + if (purple_debug_is_unsafe()) + purple_debug_warning("gg", "Invalid JSON: %s\n", data); + return NULL; +}
--- a/libpurple/protocols/gg/utils.h Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/gg/utils.h Mon Sep 16 18:08:19 2013 +0530 @@ -32,6 +32,7 @@ #include <internal.h> #include <libgadu.h> +#include <json-glib/json-glib.h> /** * Converts stringified UIN to uin_t. @@ -52,6 +53,14 @@ const char * ggp_uin_to_str(uin_t uin); /** + * Gets UIN for the account. + * + * @param gc The connection, in which account is connected. + * @return UIN for this account. + */ +uin_t ggp_get_my_uin(PurpleConnection *gc); + +/** * Converts encoding of a given string from UTF-8 to CP1250. * * @param src Input string. @@ -73,8 +82,6 @@ gboolean ggp_password_validate(const gchar *password); -guint64 ggp_microtime(void); - gchar * ggp_utf8_strndup(const gchar *str, gsize n); GSList * ggp_list_copy_to_slist_deep(GList *list, GCopyFunc func, @@ -85,8 +92,6 @@ gchar * ggp_strjoin_list(const gchar *separator, GList *list); -const gchar * ggp_ipv4_to_str(uint32_t raw_ip); - GList * ggp_list_truncate(GList *list, guint length, GDestroyNotify free_func); gchar * ggp_free_if_equal(gchar *str, const gchar *pattern); @@ -95,4 +100,10 @@ time_t ggp_date_from_iso8601(const gchar *str); +uint64_t * ggp_uint64dup(uint64_t val); + +gint ggp_int64_compare(gconstpointer a, gconstpointer b); + +JsonParser * ggp_json_parse(const gchar *data); + #endif /* _GGP_UTILS_H */
--- a/libpurple/protocols/jabber/auth_cyrus.c Mon Sep 16 16:48:10 2013 +0530 +++ b/libpurple/protocols/jabber/auth_cyrus.c Mon Sep 16 18:08:19 2013 +0530 @@ -262,7 +262,7 @@ purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), msg, - 1, account, NULL, NULL, account, + 1, purple_request_cpar_from_account(account), account, allow_cyrus_plaintext_auth, disallow_plaintext_auth); g_free(msg);
--- a/valgrind-suppressions Mon Sep 16 16:48:10 2013 +0530 +++ b/valgrind-suppressions Mon Sep 16 18:08:19 2013 +0530 @@ -161,6 +161,75 @@ { webkitgtk uninitialized values Memcheck:Cond + ... + obj:/usr/lib/libwebkitgtk-* + ... +} +{ + webkitgtk uninitialized values 2 + Memcheck:Value4 + ... obj:/usr/lib/libwebkitgtk-* ... } +{ + webkitgtk uninitialized values 3 + Memcheck:Cond + ... + fun:_ZN3JSC17ConservativeRoots14genericAddSpanINS_13DummyMarkHookEEEvPvS3_RT_ + ... +} +{ + webkitgtk uninitialized values 4 + Memcheck:Value4 + ... + fun:_ZN3JSC17ConservativeRoots14genericAddSpanINS_13DummyMarkHookEEEvPvS3_RT_ + ... +} +{ + webkitgtk uninitialized values 5 + Memcheck:Value4 + ... + obj:/usr/lib/libjavascriptcoregtk- +} +{ + wcslen_sse2 optimization + Memcheck:Addr8 + fun:__wcslen_sse2 + ... +} +{ + wcslen_sse2 optimization 2 + Memcheck:Cond + fun:__wcslen_sse2 + ... +} +{ + idna bug + Memcheck:Addr4 + fun:idna_to_ascii_4z + fun:idna_to_ascii_8z + ... +} +{ + libcairo uninitialized values + Memcheck:Cond + obj:*libpixman-1.so.* + obj:*libpixman-1.so.* + obj:*libpixman-1.so.* + obj:*libpixman-1.so.* + fun:pixman_image_composite32 + obj:*libcairo.so.2.* + obj:*libcairo.so.2.* + obj:*libcairo.so.2.* + obj:*libcairo.so.2.* + obj:* +} +{ + librsvg uninitialized values + Memcheck:Cond + obj:*librsvg-2.so.2.* + fun:rsvg_handle_get_pixbuf_sub + fun:rsvg_handle_get_pixbuf + obj:* +}