libpurple/protocols/gg/image-prpl.c

changeset 35819
cc375b914104
parent 35360
29d788c72350
child 35820
9b4fccc8ceac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/image-prpl.c	Fri Apr 11 00:40:16 2014 +0200
@@ -0,0 +1,342 @@
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * Rewritten from scratch during Google Summer of Code 2012
+ * by Tomek Wasilczyk (http://www.wasilczyk.pl).
+ *
+ * Previously implemented by:
+ *  - Arkadiusz Miskiewicz <misiek@pld.org.pl> - first implementation (2001);
+ *  - Bartosz Oler <bartosz@bzimage.us> - reimplemented during GSoC 2005;
+ *  - Krzysztof Klinikowski <grommasher@gmail.com> - some parts (2009-2011).
+ *
+ * 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 "image-prpl.h"
+
+#include <debug.h>
+#include <glibcompat.h>
+
+#include "gg.h"
+#include "utils.h"
+
+#include <image-store.h>
+
+struct _ggp_image_session_data
+{
+	GHashTable *got_images;
+	GHashTable *incoming_images;
+	GHashTable *sent_images;
+};
+
+typedef struct
+{
+	guint id; /* TODO: store image ref, not id */
+	gchar *conv_name; /* TODO: callback */
+} ggp_image_sent;
+
+typedef struct
+{
+	GList *listeners;
+} ggp_image_requested;
+
+typedef struct
+{
+	ggp_image_request_cb cb;
+	gpointer user_data;
+} ggp_image_requested_listener;
+
+static void
+image_unref_by_id(guint id)
+{
+	/* TODO: store image ref, not id */
+	PurpleImage *img = purple_image_store_get(id);
+	g_object_unref(img);
+}
+
+static void ggp_image_got_free(gpointer data)
+{
+	guint id = GPOINTER_TO_INT(data);
+	image_unref_by_id(id); /* TODO: store image ref, not id */
+}
+
+static void ggp_image_sent_free(gpointer data)
+{
+	ggp_image_sent *sent_image = (ggp_image_sent*)data;
+	image_unref_by_id(sent_image->id);
+	g_free(sent_image->conv_name);
+	g_free(sent_image);
+}
+
+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;
+}
+
+void ggp_image_setup(PurpleConnection *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;
+
+	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_session_data *sdata = ggp_image_get_sdata(gc);
+
+	g_hash_table_destroy(sdata->got_images);
+	g_hash_table_destroy(sdata->incoming_images);
+	g_hash_table_destroy(sdata->sent_images);
+	g_free(sdata);
+}
+
+ggp_image_prepare_result ggp_image_prepare(PurpleConversation *conv,
+	const guint stored_id, uint64_t *id)
+{
+	PurpleConnection *gc = purple_conversation_get_connection(conv);
+	ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
+	PurpleImage *image = purple_image_store_get(stored_id);
+	size_t image_size;
+	gconstpointer image_data;
+	uint32_t image_crc;
+	ggp_image_sent *sent_image;
+
+	if (!image) {
+		purple_debug_error("gg", "ggp_image_prepare: image %d "
+			"not found in image store\n", stored_id);
+		return GGP_IMAGE_PREPARE_FAILURE;
+	}
+
+	image_size = purple_image_get_size(image);
+
+	if (image_size > GGP_IMAGE_SIZE_MAX) {
+		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;
+	}
+
+	g_object_ref(image);
+	image_data = purple_image_get_data(image);
+	image_crc = gg_crc32(0, image_data, image_size);
+
+	purple_debug_info("gg", "ggp_image_prepare: image prepared "
+		"[id=%d, crc=%u, size=%" G_GSIZE_FORMAT "]\n",
+		stored_id, image_crc, image_size);
+
+	*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;
+}
+
+void ggp_image_recv(PurpleConnection *gc,
+	const struct gg_event_image_reply *image_reply)
+{
+	ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
+	PurpleImage *img;
+	guint stored_id;
+	ggp_image_requested *req;
+	GList *it;
+	uint64_t id;
+
+	img = purple_image_new_from_data(
+		g_memdup(image_reply->image, image_reply->size),
+		image_reply->size);
+	purple_image_set_friendly_filename(img, image_reply->filename);
+	stored_id = purple_image_store_add(img);
+	g_object_unref(img);
+
+	id = ggp_image_params_to_id(image_reply->crc32, image_reply->size);
+
+	purple_debug_info("gg", "ggp_image_recv: got image "
+		"[stored_id=%d, crc=%u, size=%u, filename=%s, id="
+		GGP_IMAGE_ID_FORMAT "]\n",
+		stored_id,
+		image_reply->crc32,
+		image_reply->size,
+		image_reply->filename,
+		id);
+
+	g_hash_table_insert(sdata->got_images, ggp_uint64dup(id),
+		GINT_TO_POINTER(stored_id));
+
+	req = g_hash_table_lookup(sdata->incoming_images, &id);
+	if (!req) {
+		purple_debug_warning("gg", "ggp_image_recv: "
+			"image " GGP_IMAGE_ID_FORMAT " wasn't requested\n",
+			id);
+		return;
+	}
+
+	it = g_list_first(req->listeners);
+	while (it) {
+		ggp_image_requested_listener *listener = it->data;
+		it = g_list_next(it);
+
+		listener->cb(gc, id, stored_id, listener->user_data);
+	}
+	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_session_data *sdata = ggp_image_get_sdata(gc);
+	ggp_image_sent *sent_image;
+	PurpleImage *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",
+		image_request->sender,
+		image_request->crc32,
+		image_request->size);
+
+	id = ggp_image_params_to_id(image_request->crc32, image_request->size);
+
+	sent_image = g_hash_table_lookup(sdata->sent_images, &id);
+
+	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");
+		return;
+	}
+
+	purple_debug_misc("gg", "ggp_image_send: requested image found "
+		"[id=" GGP_IMAGE_ID_FORMAT ", stored id=%d, conv=%s]\n",
+		id,
+		sent_image->id,
+		sent_image->conv_name);
+
+	image = purple_image_store_get(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(sdata->sent_images,
+			GINT_TO_POINTER(image_request->crc32));
+		return;
+	}
+
+	/* TODO: check allowed recipients */
+	gg_filename = g_strdup_printf(GGP_IMAGE_ID_FORMAT, id);
+	gg_image_reply(accdata->session, image_request->sender,
+		gg_filename,
+		purple_image_get_data(image),
+		purple_image_get_size(image));
+	g_free(gg_filename);
+
+	conv = purple_conversations_find_with_account(
+		sent_image->conv_name,
+		purple_connection_get_account(gc));
+	if (conv != NULL) {
+		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;
+
+	if (size > GGP_IMAGE_SIZE_MAX && crc <= GGP_IMAGE_SIZE_MAX) {
+		uint32_t tmp;
+		purple_debug_warning("gg", "ggp_image_request: "
+			"crc and size are swapped!\n");
+		tmp = crc;
+		crc = size;
+		size = tmp;
+	}
+
+	req = g_hash_table_lookup(sdata->incoming_images, &id);
+	if (!req) {
+		req = g_new0(ggp_image_requested, 1);
+		g_hash_table_insert(sdata->incoming_images,
+			ggp_uint64dup(id), req);
+		purple_debug_info("gg", "ggp_image_request: "
+			"requesting image " GGP_IMAGE_ID_FORMAT "\n", id);
+		if (gg_image_request(accdata->session, uin, size, crc) != 0)
+			purple_debug_error("gg", "ggp_image_request: failed\n");
+	} else {
+		purple_debug_info("gg", "ggp_image_request: "
+			"image " GGP_IMAGE_ID_FORMAT " already requested\n",
+			id);
+	}
+
+	listener = g_new0(ggp_image_requested_listener, 1);
+	listener->cb = cb;
+	listener->user_data = user_data;
+	req->listeners = g_list_append(req->listeners, listener);
+}
+
+int ggp_image_get_cached(PurpleConnection *gc, uint64_t id)
+{
+	ggp_image_session_data *sdata = ggp_image_get_sdata(gc);
+
+	return GPOINTER_TO_INT(g_hash_table_lookup(sdata->got_images, &id));
+}

mercurial