Sun, 30 Sep 2012 21:57:44 +0200
Merge from trunk
--- a/libpurple/protocols/gg/Makefile.am Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/Makefile.am Sun Sep 30 21:57:44 2012 +0200 @@ -1,5 +1,6 @@ #V=0 -#GADU_EXTRA_WARNINGS = -Wall -Wextra -Werror +#CFLAGS = -g -O0 +GADU_EXTRA = -Wall -Wextra -Werror -fno-inline pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) @@ -51,44 +52,50 @@ GGSOURCES = \ $(INTGGSOURCES) \ - utils.h \ - utils.c \ - confer.h \ - confer.c \ - buddylist.h \ + account.c \ + account.h \ + avatar.c \ + avatar.h \ buddylist.c \ - gg.h \ + buddylist.h \ + chat.c \ + chat.h \ + deprecated.c \ + deprecated.h \ gg.c \ - resolver-purple.h \ - resolver-purple.c \ - image.h \ + gg.h \ + ggdrive.c \ + ggdrive.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 \ + 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,9 +125,9 @@ endif AM_CPPFLAGS = \ - $(GADU_EXTRA_WARNINGS) \ -I$(top_srcdir)/libpurple \ -I$(top_builddir)/libpurple \ $(INTGG_CFLAGS) \ $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) + $(DEBUG_CFLAGS) \ + $(GADU_EXTRA)
--- a/libpurple/protocols/gg/avatar.c Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/avatar.c Sun Sep 30 21:57:44 2012 +0200 @@ -152,7 +152,7 @@ { 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); @@ -317,10 +317,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 Sun Sep 30 21:57:44 2012 +0200 @@ -0,0 +1,602 @@ +#include "chat.h" + +#include <debug.h> + +#include "gg.h" +#include "utils.h" +#include "message-prpl.h" + +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; + int 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 %llu 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 || + ev->type == GG_EVENT_CHAT_SEND_MSG_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 %llu\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 %llu\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), "%llu", 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 *buff = g_strdup_printf( + _("%llu is not a valid room identifier"), id); + 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 %llu\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 %llu\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 %llu 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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/chat.h Sun Sep 30 21:57:44 2012 +0200 @@ -0,0 +1,30 @@ +#ifndef _GGP_CHAT_H +#define _GGP_CHAT_H + +#include <internal.h> +#include <libgadu.h> + +typedef struct _ggp_chat_session_data ggp_chat_session_data; + +void ggp_chat_setup(PurpleConnection *gc); +void ggp_chat_cleanup(PurpleConnection *gc); + +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 /* _GGP_CHAT_H */
--- a/libpurple/protocols/gg/confer.c Sun Sep 30 21:38:30 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +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) {{{ */ -PurpleConversation *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_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, 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) -{ - PurpleConversation *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_conv_chat_add_user(PURPLE_CONV_CHAT(conv), str_uin, NULL, - PURPLE_CBFLAGS_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++) { - PurpleConversation *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_conv_chat_add_user(PURPLE_CONV_CHAT(conv), - str_uin, NULL, - PURPLE_CBFLAGS_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 Sun Sep 30 21:38:30 2012 +0200 +++ /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. - */ -PurpleConversation * -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: */
--- a/libpurple/protocols/gg/gg.c Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/gg.c Sun Sep 30 21:57:44 2012 +0200 @@ -39,7 +39,7 @@ #include "xmlnode.h" #include "gg.h" -#include "confer.h" +#include "chat.h" #include "search.h" #include "buddylist.h" #include "utils.h" @@ -52,6 +52,9 @@ #include "status.h" #include "servconn.h" #include "pubdir-prpl.h" +#include "message-prpl.h" +#include "html.h" +#include "ggdrive.h" /* ---------------------------------------------------------------------- */ @@ -61,7 +64,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; } @@ -77,6 +80,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 @@ -168,72 +195,6 @@ 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_connection_get_account(gc), NULL, NULL, - buddy); - g_free(msg); -} - /* ----- BLOCK BUDDIES -------------------------------------------------- */ static void ggp_add_deny(PurpleConnection *gc, const char *who) @@ -262,198 +223,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); - PurpleConversation *conv; - 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); - PurpleConversation *conv; - const gchar *who = ggp_uin_to_str(ev->sender); // not really sender - conv = purple_find_conversation_with_account( - PURPLE_CONV_TYPE_IM, who, account); - if (conv == NULL) - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who); - purple_conversation_write(conv, 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); - } - conv = ggp_confer_find_by_name(gc, chat_name); - chat_id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)); - - 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; @@ -481,7 +250,11 @@ xml = xmlnode_from_str(data, -1); if (xml == NULL) + { + purple_debug_error("gg", "ggp_xml_event_handler: " + "invalid xml: [%s]\n", data); goto out; + } xmlnode_next_event = xmlnode_get_child(xml, "event"); while (xmlnode_next_event != NULL) @@ -553,8 +326,13 @@ case GG_EVENT_NONE: /* Nothing happened. */ break; + case GG_EVENT_CONN_FAILED: + purple_connection_error (gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Server disconnected")); + break; case GG_EVENT_MSG: - ggp_recv_message_handler(gc, &ev->event.msg, FALSE); + ggp_message_got(gc, &ev->event.msg); break; case GG_EVENT_ACK: /* Changing %u to %i fixes compiler warning */ @@ -591,13 +369,33 @@ 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; + case GG_EVENT_IMTOKEN: + purple_debug_info("gg", "gg11: got IMTOKEN\n"); + g_free(info->imtoken); + info->imtoken = g_strdup(ev->event.imtoken.imtoken); + ggp_ggdrive_test(gc); + break; + case GG_EVENT_PONG110: + purple_debug_info("gg", "gg11: got PONG110 %lu\n", ev->event.pong110.time); + break; + case GG_EVENT_JSON_EVENT: + purple_debug_info("gg", "gg11: got JSON event\n"); + 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: + case GG_EVENT_CHAT_SEND_MSG_ACK: + ggp_chat_got_event(gc, ev); + break; default: - purple_debug_error("gg", + purple_debug_warning("gg", "unsupported event type=%d\n", ev->type); break; } @@ -643,6 +441,12 @@ case GG_STATE_TLS_NEGOTIATION: purple_debug_info("gg", "GG_STATE_TLS_NEGOTIATION\n"); break; + 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; default: purple_debug_error("gg", "unknown state = %d\n", info->session->state); @@ -829,44 +633,6 @@ } } -static GList *ggp_blist_node_menu(PurpleBlistNode *node) -{ - PurpleMenuAction *act; - GList *m = NULL; - PurpleAccount *account; - PurpleConnection *gc; - GGPInfo *info; - - if (!PURPLE_BLIST_NODE_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; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 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); @@ -878,23 +644,21 @@ if (!ggp_deprecated_setup_proxy(gc)) return; + purple_connection_set_flags(gc, PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_URLDESC); + glp = g_new0(struct gg_login_params, 1); 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_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); + glp->uin = ggp_str_to_uin(purple_account_get_username(account)); glp->password = ggp_convert_to_cp1250(purple_account_get_password(account)); @@ -977,14 +741,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) { @@ -993,19 +756,17 @@ 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); if (info->inpa > 0) purple_input_remove(info->inpa); + g_free(info->imtoken); purple_connection_set_protocol_data(gc, NULL); g_free(info); @@ -1014,132 +775,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_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) - { - PurpleConversation *conv = - purple_find_conversation_with_account( - PURPLE_CONV_TYPE_IM, who, - purple_connection_get_account(gc)); - purple_conversation_write(conv, "", - _("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, PurpleTypingState state) { GGPInfo *info = purple_connection_get_protocol_data(gc); @@ -1186,94 +821,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; - PurpleConversation *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_conv_chat_add_user(PURPLE_CONV_CHAT(conv), - purple_account_get_username(account), NULL, - PURPLE_CBFLAGS_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) -{ - PurpleConversation *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_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(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); @@ -1380,12 +927,12 @@ ggp_status_buddy_text, /* status_text */ ggp_tooltip_text, /* tooltip_text */ ggp_status_types, /* status_types */ - ggp_blist_node_menu, /* blist_node_menu */ + NULL, /* blist_node_menu */ ggp_chat_info, /* chat_info */ - NULL, /* chat_info_defaults */ + ggp_chat_info_defaults, /* chat_info_defaults */ ggp_login, /* login */ ggp_close, /* close */ - ggp_send_im, /* send_im */ + ggp_message_send_im, /* send_im */ NULL, /* set_info */ ggp_send_typing, /* send_typing */ ggp_pubdir_get_info_prpl, /* get_info */ @@ -1401,11 +948,11 @@ NULL, /* rem_permit */ ggp_rem_deny, /* rem_deny */ NULL, /* set_permit_deny */ - ggp_join_chat, /* join_chat */ - NULL, /* reject_chat */ - ggp_get_chat_name, /* get_chat_name */ - NULL, /* chat_invite */ - NULL, /* chat_leave */ + ggp_chat_join, /* join_chat */ + NULL, /* TODO */ /* reject_chat */ + ggp_chat_get_name, /* get_chat_name */ + ggp_chat_invite, /* chat_invite */ + ggp_chat_leave, /* chat_leave */ NULL, /* chat_whisper */ ggp_chat_send, /* chat_send */ ggp_keepalive, /* keepalive */ @@ -1422,7 +969,7 @@ NULL, /* get_cb_real_name */ NULL, /* set_chat_topic */ NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ + ggp_chat_roomlist_get_list, /* roomlist_get_list */ NULL, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ @@ -1551,13 +1098,18 @@ ggp_resolver_purple_setup(); ggp_servconn_setup(ggp_server_option); - + ggp_html_setup(); + ggp_message_setup_global(); + return TRUE; } static gboolean ggp_unload(PurplePlugin *plugin) { ggp_servconn_cleanup(); + ggp_html_cleanup(); + ggp_message_cleanup_global(); + return TRUE; }
--- a/libpurple/protocols/gg/gg.h Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/gg.h Sun Sep 30 21:57:44 2012 +0200 @@ -35,31 +35,25 @@ #include "roster.h" #include "multilogon.h" #include "status.h" - -#define PUBDIR_RESULTS_MAX 20 +#include "chat.h" +#include "message-prpl.h" #define GGP_UIN_LEN_MAX 10 - -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; } GGPInfo; typedef struct @@ -67,8 +61,10 @@ gboolean blocked; } ggp_buddy_data; -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); + #endif /* _PURPLE_GG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/ggdrive.c Sun Sep 30 21:57:44 2012 +0200 @@ -0,0 +1,127 @@ +#include "ggdrive.h" + +#include <debug.h> + +#include "gg.h" +#include "libgaduw.h" + +#define GGP_GGDRIVE_HOSTNAME "MyComputer" +#define GGP_GGDRIVE_OS "WINNT x86-msvc" +#define GGP_GGDRIVE_TYPE "desktop" + +#define GGP_GGDRIVE_RESPONSE_MAX 1024 + +/******************************************************************************* + * Authentication + ******************************************************************************/ + +typedef void (*ggp_ggdrive_authentication_cb)(const gchar *security_token, + gpointer user_data); + +static void ggp_ggdrive_authenticate(PurpleConnection *gc, + ggp_ggdrive_authentication_cb cb, gpointer user_data); + +static void ggp_ggdrive_authenticate_done(PurpleUtilFetchUrlData *url_data, + gpointer _user_data, const gchar *url_text, gsize len, + const gchar *error_message); + +typedef struct +{ + PurpleConnection *gc; + ggp_ggdrive_authentication_cb cb; + gpointer user_data; +} ggp_ggdrive_authenticate_data; + +/******************************************************************************/ + +static void ggp_ggdrive_authenticate(PurpleConnection *gc, + ggp_ggdrive_authentication_cb cb, gpointer user_data) +{ + PurpleAccount *account = purple_connection_get_account(gc); + const gchar *imtoken; + gchar *request, *metadata; + ggp_ggdrive_authenticate_data *req_data; + + imtoken = ggp_get_imtoken(gc); + if (!imtoken) + { + cb(NULL, user_data); + return; + } + + metadata = g_strdup_printf("{" + "\"id\": \"%032x\", " + "\"name\": \"" GGP_GGDRIVE_HOSTNAME "\", " + "\"os_version\": \"" GGP_GGDRIVE_OS "\", " + "\"client_version\": \"%s\", " + "\"type\": \"" GGP_GGDRIVE_TYPE "\"}", + g_random_int_range(1, 1 << 16), + ggp_libgaduw_version(gc)); + + request = g_strdup_printf( + "PUT /signin HTTP/1.1\r\n" + "Host: drive.mpa.gg.pl\r\n" + "Authorization: IMToken %s\r\n" + "X-gged-user: gg/pl:%u\r\n" + "X-gged-client-metadata: %s\r\n" + "X-gged-api-version: 6\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 0\r\n" + "\r\n", + imtoken, ggp_own_uin(gc), metadata); + + req_data = g_new0(ggp_ggdrive_authenticate_data, 1); + req_data->gc = gc; + req_data->cb = cb; + req_data->user_data = user_data; + + purple_util_fetch_url_request(account, "https://drive.mpa.gg.pl/signin", + FALSE, NULL, TRUE, request, TRUE, GGP_GGDRIVE_RESPONSE_MAX, + ggp_ggdrive_authenticate_done, req_data); + + g_free(metadata); + g_free(request); +} + +static void ggp_ggdrive_authenticate_done(PurpleUtilFetchUrlData *url_data, + gpointer _user_data, const gchar *url_text, gsize len, + const gchar *error_message) +{ + ggp_ggdrive_authenticate_data *req_data = _user_data; + PurpleConnection *gc = req_data->gc; + ggp_ggdrive_authentication_cb cb = req_data->cb; + gpointer user_data = req_data->user_data; + + g_free(req_data); + + if (!PURPLE_CONNECTION_IS_VALID(gc)) + { + cb(NULL, user_data); + return; + } + + if (len == 0) + { + purple_debug_error("gg", "ggp_ggdrive_authenticate_done: failed\n"); + cb(NULL, user_data); + return; + } + + purple_debug_info("gg", "ggp_ggdrive_authenticate_done: [%s]\n", url_text); +} + +/***/ + +static void ggp_ggdrive_test_cb(const gchar *security_token, + gpointer user_data) +{ + if (!security_token) + purple_debug_error("gg", "ggp_ggdrive_test_cb: didn't got security token\n"); + else + purple_debug_info("gg", "ggp_ggdrive_test_cb: got security token [%s]\n", security_token); +} + +void ggp_ggdrive_test(PurpleConnection *gc) +{ + ggp_ggdrive_authenticate(gc, ggp_ggdrive_test_cb, NULL); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/ggdrive.h Sun Sep 30 21:57:44 2012 +0200 @@ -0,0 +1,8 @@ +#ifndef _GGP_GGDRIVE_H +#define _GGP_GGDRIVE_H + +#include <internal.h> + +void ggp_ggdrive_test(PurpleConnection *gc); + +#endif /* _GGP_GGDRIVE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/html.c Sun Sep 30 21:57:44 2012 +0200 @@ -0,0 +1,160 @@ +#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 == strcmp(tag_str, "eom")) + return GGP_HTML_TAG_EOM; + if (0 == strcmp(tag_str, "span")) + return GGP_HTML_TAG_SPAN; + if (0 == strcmp(tag_str, "div")) + return GGP_HTML_TAG_DIV; + if (0 == strcmp(tag_str, "br")) + return GGP_HTML_TAG_BR; + if (0 == strcmp(tag_str, "a")) + return GGP_HTML_TAG_A; + if (0 == strcmp(tag_str, "b")) + return GGP_HTML_TAG_B; + if (0 == strcmp(tag_str, "i")) + return GGP_HTML_TAG_I; + if (0 == strcmp(tag_str, "u")) + return GGP_HTML_TAG_U; + if (0 == strcmp(tag_str, "s")) + return GGP_HTML_TAG_S; + if (0 == strcmp(tag_str, "img")) + return GGP_HTML_TAG_IMG; + if (0 == strcmp(tag_str, "font")) + return GGP_HTML_TAG_FONT; + if (0 == strcmp(tag_str, "hr")) + return GGP_HTML_TAG_HR; + if (0 == strcmp(tag_str, "a")) + return GGP_HTML_TAG_IGNORED; + return GGP_HTML_TAG_UNKNOWN; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/html.h Sun Sep 30 21:57:44 2012 +0200 @@ -0,0 +1,33 @@ +#ifndef _GGP_HTML_H +#define _GGP_HTML_H + +#include <internal.h> + +typedef enum +{ + GGP_HTML_TAG_UNKNOWN, + GGP_HTML_TAG_IGNORED, + 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 Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/image.c Sun Sep 30 21:57:44 2012 +0200 @@ -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=%zu, filename=%s]\n", - id, image_crc, image_size, image_filename); + purple_debug_info("gg", "ggp_image_prepare: image prepared " + "[id=%d, crc=%u, size=%zu]\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,77 +169,59 @@ 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; stored_id = purple_imgstore_add_with_id( g_memdup(image_reply->image, image_reply->size), 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=%016llx]\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 %016llx 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", @@ -249,10 +229,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"); @@ -260,36 +248,90 @@ } purple_debug_misc("gg", "ggp_image_send: requested image found " - "[id=%d, conv=%s]\n", - pending_image->id, - pending_image->conv_name); + "[id=%016llx, 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("%016llx", 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_find_conversation_with_account( - PURPLE_CONV_TYPE_IM, pending_image->conv_name, + PURPLE_CONV_TYPE_ANY, 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 %016llx\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 %016llx 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 Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/image.h Sun Sep 30 21:57:44 2012 +0200 @@ -35,11 +35,7 @@ #define GGP_IMAGE_SIZE_MAX 255000 -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 +44,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/libgaduw.c Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/libgaduw.c Sun Sep 30 21:57:44 2012 +0200 @@ -32,6 +32,7 @@ #include <debug.h> #include "purplew.h" +#include "gg.h" /******************************************************************************* * HTTP requests. @@ -138,3 +139,13 @@ req->h->destroy(req->h); g_free(req); } + +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; +}
--- a/libpurple/protocols/gg/libgaduw.h Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/libgaduw.h Sun Sep 30 21:57:44 2012 +0200 @@ -54,5 +54,7 @@ gboolean show_processing); void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req); +const gchar * ggp_libgaduw_version(PurpleConnection *gc); + #endif /* _GGP_LIBGADUW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/message-prpl.c Sun Sep 30 21:57:44 2012 +0200 @@ -0,0 +1,788 @@ +#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-%016llx\">" +#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 PurpleConversation * 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-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 PurpleConversation * ggp_message_get_conv(PurpleConnection *gc, + uin_t uin) +{ + PurpleAccount *account = purple_connection_get_account(gc); + PurpleConversation *conv; + const gchar *who = ggp_uin_to_str(uin); + + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, who, account); + if (conv) + return conv; + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who); + return conv; +} + +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 %016llx 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 %016llx, 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 (ev->chat_id != 0) + { + msg->type = GGP_MESSAGE_GOT_TYPE_CHAT; + msg->chat_id = ev->chat_id; + } + else + { + 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 (ev->chat_id != 0) + { + msg->type = GGP_MESSAGE_GOT_TYPE_CHAT; + msg->chat_id = ev->chat_id; + } + else + { + 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); + } + else if (msg->type == GGP_MESSAGE_GOT_TYPE_CHAT) + { + ggp_chat_got_message(gc, msg->chat_id, msg->text, msg->time, + msg->user); + } + else if (msg->type == GGP_MESSAGE_GOT_TYPE_MULTILOGON) + { + PurpleConversation *conv = ggp_message_get_conv(gc, msg->user); + const gchar *me = purple_account_get_username( + purple_connection_get_account(gc)); + + purple_conversation_write(conv, 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, "%llx", &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 %016llx 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; + int 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 = (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=\"%016llx\">", 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); + PurpleConversation *conv; + 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_find_buddy( + purple_connection_get_account(gc), who)); + + if (buddy_data->blocked) + return -1; + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + who, purple_connection_get_account(gc)); + + gg_msg = ggp_message_format_to_gg(conv, message); + + /* TODO: splitting messages */ + if (strlen(gg_msg) > GG_MSG_MAXSIZE) + { + g_free(gg_msg); + return -E2BIG; + } + + succ = (gg_send_message_html(info->session, GG_CLASS_CHAT, + ggp_str_to_uin(who), (unsigned char *)gg_msg) >= 0); + + 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 Sun Sep 30 21:57:44 2012 +0200 @@ -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 Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/multilogon.c Sun Sep 30 21:57:44 2012 +0200 @@ -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 Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/multilogon.h Sun Sep 30 21:57:44 2012 +0200 @@ -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/resolver-purple.c Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/resolver-purple.c Sun Sep 30 21:57:44 2012 +0200 @@ -68,7 +68,7 @@ const char *error_message) { ggp_resolver_purple_data *data = (ggp_resolver_purple_data*)cbdata; - const int fd = data->pipes[1]; + const int fd = data->pipes[1]; /* TODO: invalid read after logoff */ int ipv4_count, all_count, write_size; struct in_addr *addresses;
--- a/libpurple/protocols/gg/servconn.c Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/servconn.c Sun Sep 30 21:57:44 2012 +0200 @@ -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/utils.c Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/utils.c Sun Sep 30 21:57:44 2012 +0200 @@ -232,7 +232,7 @@ GDate g_date; static gchar buff[30]; - g_date_set_time(&g_date, date); + g_date_set_time_t(&g_date, date); if (0 == g_date_strftime(buff, sizeof(buff), format, &g_date)) return NULL; return buff; @@ -248,3 +248,22 @@ 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; +}
--- a/libpurple/protocols/gg/utils.h Sun Sep 30 21:38:30 2012 +0200 +++ b/libpurple/protocols/gg/utils.h Sun Sep 30 21:57:44 2012 +0200 @@ -95,4 +95,8 @@ time_t ggp_date_from_iso8601(const gchar *str); +uint64_t * ggp_uint64dup(uint64_t val); + +gint ggp_int64_compare(gconstpointer a, gconstpointer b); + #endif /* _GGP_UTILS_H */