Merged pidgin/main into default

Sun, 28 May 2017 13:26:27 +0300

author
Arkadiy Illarionov <qarkai@gmail.com>
date
Sun, 28 May 2017 13:26:27 +0300
changeset 38886
c1fb4e53da4e
parent 38885
0d588a4d383e (current diff)
parent 38299
770365ded9f8 (diff)
child 38887
826f5da7b56c

Merged pidgin/main into default

libpurple/buddyicon.c file | annotate | diff | comparison | revisions
libpurple/log.c file | annotate | diff | comparison | revisions
--- a/libpurple/buddyicon.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/buddyicon.c	Sun May 28 13:26:27 2017 +0300
@@ -510,7 +510,7 @@
 	if (icon->img)
 	{
 		if (len != NULL)
-			*len = purple_image_get_size(icon->img);
+			*len = purple_image_get_data_size(icon->img);
 
 		return purple_image_get_data(icon->img);
 	}
--- a/libpurple/conversation.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/conversation.c	Sun May 28 13:26:27 2017 +0300
@@ -837,16 +837,14 @@
 	return purple_protocol_client_iface_get_max_message_size(protocol, conv);
 }
 
-PurpleSmiley *
-purple_conversation_add_remote_smiley(PurpleConversation *conv,
-	const gchar *shortcut)
-{
-	PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
-	PurpleSmiley *smiley;
+void
+purple_conversation_add_smiley(PurpleConversation *conv, PurpleSmiley *smiley) {
+	PurpleConversationPrivate *priv = NULL;
 
-	g_return_val_if_fail(priv != NULL, NULL);
-	g_return_val_if_fail(shortcut != NULL, NULL);
-	g_return_val_if_fail(shortcut[0] != '\0', NULL);
+	g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
+	g_return_if_fail(smiley);
+
+	priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
 
 	if (priv->remote_smileys == NULL) {
 		priv->remote_smileys = purple_smiley_list_new();
@@ -854,42 +852,33 @@
 			"drop-failed-remotes", TRUE, NULL);
 	}
 
-	smiley = purple_smiley_list_get_by_shortcut(
-		priv->remote_smileys, shortcut);
-
-	/* smiley was already added */
-	if (smiley)
-		return NULL;
-
-	smiley = purple_smiley_new_remote(shortcut);
+	if(purple_smiley_list_get_by_shortcut(
+		priv->remote_smileys,
+		purple_smiley_get_shortcut(smiley)))
+	{
+		/* smiley was already added */
+		return;
+	}
 
 	if (!purple_smiley_list_add(priv->remote_smileys, smiley)) {
 		purple_debug_error("conversation", "failed adding remote "
 			"smiley to the list");
-		g_object_unref(smiley);
-		return NULL;
 	}
-
-	/* priv->remote_smileys holds the only one ref */
-	g_object_unref(smiley);
-	return smiley;
 }
 
 PurpleSmiley *
-purple_conversation_get_remote_smiley(PurpleConversation *conv,
-	const gchar *shortcut)
-{
-	PurpleConversationPrivate *priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
+purple_conversation_get_smiley(PurpleConversation *conv, const gchar *shortcut) {
+	PurpleConversationPrivate *priv = NULL;
 
-	g_return_val_if_fail(priv != NULL, NULL);
-	g_return_val_if_fail(shortcut != NULL, NULL);
-	g_return_val_if_fail(shortcut[0] != '\0', NULL);
+	g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
+	g_return_val_if_fail(shortcut, NULL);
+
+	priv = PURPLE_CONVERSATION_GET_PRIVATE(conv);
 
 	if (priv->remote_smileys == NULL)
 		return NULL;
 
-	return purple_smiley_list_get_by_shortcut(
-		priv->remote_smileys, shortcut);
+	return purple_smiley_list_get_by_shortcut(priv->remote_smileys, shortcut);
 }
 
 PurpleSmileyList *
--- a/libpurple/conversation.h	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/conversation.h	Sun May 28 13:26:27 2017 +0300
@@ -652,40 +652,24 @@
 purple_conversation_get_max_message_size(PurpleConversation *conv);
 
 /**
- * purple_conversation_add_remote_smiley:
+ * purple_conversation_add_smiley:
  * @conv: The conversation that receives new smiley.
- * @shortcut: The shortcut for the new smiley.
- *
- * Adds new smiley to the list of remote smileys for this conversation.
- * The smiley image have to be written and closed, when data is ready.
+ * @smiley: The smiley.
  *
- * You can retrieve smiley's #PurpleImage object using #purple_smiley_get_image
- * and set its data with #purple_image_transfer_write
- * and #purple_image_transfer_close.
- *
- * Returns: (transfer none): New smiley, or %NULL if it's already being
- *          retrieved (or possibly, in case of error).
+ * Adds a smiley to the list of smileys that belong to this conversation.
  */
-PurpleSmiley *
-purple_conversation_add_remote_smiley(PurpleConversation *conv,
-	const gchar *shortcut);
+void purple_conversation_add_smiley(PurpleConversation *conv, PurpleSmiley *smiley);
 
 /**
- * purple_conversation_get_remote_smiley:
+ * purple_conversation_get_smiley:
  * @conv: The conversation.
  * @shortcut: The shortcut.
  *
- * Lookups for the remote smiley previously added to this conversation.
- *
- * You may use this function when you receive the smiley data, but it's
- * better just to store and use the reference returned by
- * #purple_conversation_add_remote_smiley.
+ * Lookups for the smiley previously added to this conversation.
  *
  * Returns: (transfer none): The smiley, or %NULL if it doesn't exists.
  */
-PurpleSmiley *
-purple_conversation_get_remote_smiley(PurpleConversation *conv,
-	const gchar *shortcut);
+PurpleSmiley *purple_conversation_get_smiley(PurpleConversation *conv, const gchar *shortcut);
 
 /**
  * purple_conversation_get_remote_smileys:
--- a/libpurple/image-store.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/image-store.c	Sun May 28 13:26:27 2017 +0300
@@ -194,16 +194,14 @@
 gchar *
 purple_image_store_get_uri(PurpleImage *image)
 {
-	gboolean is_ready;
 	const gchar *path;
 	guint img_id;
 
 	g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
 
-	is_ready = purple_image_is_ready(image);
 	path = purple_image_get_path(image);
 
-	if (is_ready && path)
+	if (path)
 		return g_filename_to_uri(path, NULL, NULL);
 
 	img_id = purple_image_store_add_weak(image);
--- a/libpurple/image.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/image.c	Sun May 28 13:26:27 2017 +0300
@@ -30,176 +30,186 @@
 
 typedef struct {
 	gchar *path;
-	GString *contents;
+
+	GBytes *contents;
 
 	const gchar *extension;
 	const gchar *mime;
 	gchar *gen_filename;
 	gchar *friendly_filename;
-
-	gboolean is_ready;
-	gboolean has_failed;
 } PurpleImagePrivate;
 
-enum
-{
+enum {
 	PROP_0,
-	PROP_IS_READY,
-	PROP_HAS_FAILED,
+	PROP_CONTENTS,
+	PROP_SIZE,
 	PROP_LAST
 };
 
-enum
-{
-	SIG_READY,
-	SIG_FAILED,
-	SIG_LAST
-};
-
-static GObjectClass *parent_class;
-static guint signals[SIG_LAST];
 static GParamSpec *properties[PROP_LAST];
 
 /******************************************************************************
- * Internal logic
+ * Helpers
  ******************************************************************************/
+static void
+_purple_image_set_contents(PurpleImage *image, GBytes *bytes) {
+	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+	if(priv->contents)
+		g_bytes_unref(priv->contents);
+
+	priv->contents = (bytes) ? g_bytes_ref(bytes) : NULL;
+}
+
+/******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+G_DEFINE_TYPE_WITH_PRIVATE(PurpleImage, purple_image, G_TYPE_OBJECT);
 
 static void
-has_failed(PurpleImage *image)
-{
-	gboolean ready_changed;
-	PurpleImagePrivate *priv;
-	priv = PURPLE_IMAGE_GET_PRIVATE(image);
-
-	g_return_if_fail(!priv->has_failed);
-
-	priv->has_failed = TRUE;
-	ready_changed = (priv->is_ready != FALSE);
-	priv->is_ready = FALSE;
-
-	if (priv->contents) {
-		g_string_free(priv->contents, TRUE);
-		priv->contents = NULL;
-	}
-
-	if (ready_changed) {
-		g_object_notify_by_pspec(G_OBJECT(image),
-			properties[PROP_IS_READY]);
-	}
-	g_object_notify_by_pspec(G_OBJECT(image), properties[PROP_HAS_FAILED]);
-	g_signal_emit(image, signals[SIG_FAILED], 0);
+purple_image_init(PurpleImage *image) {
 }
 
 static void
-became_ready(PurpleImage *image)
+purple_image_finalize(GObject *obj) {
+	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(obj);
+
+	if(priv->contents)
+		g_bytes_unref(priv->contents);
+
+	g_free(priv->path);
+	g_free(priv->gen_filename);
+	g_free(priv->friendly_filename);
+
+	G_OBJECT_CLASS(purple_image_parent_class)->finalize(obj);
+}
+
+static void
+purple_image_set_property(GObject *obj, guint param_id,
+                          const GValue *value, GParamSpec *pspec)
 {
-	PurpleImagePrivate *priv;
-	priv = PURPLE_IMAGE_GET_PRIVATE(image);
+	PurpleImage *image = PURPLE_IMAGE(obj);
 
-	g_return_if_fail(!priv->has_failed);
-	g_return_if_fail(!priv->is_ready);
+	switch (param_id) {
+		case PROP_CONTENTS:
+			_purple_image_set_contents(image, g_value_get_boxed(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
 
-	priv->is_ready = TRUE;
+static void
+purple_image_get_property(GObject *obj, guint param_id, GValue *value,
+                          GParamSpec *pspec)
+{
+	PurpleImage *image = PURPLE_IMAGE(obj);
 
-	g_object_notify_by_pspec(G_OBJECT(image), properties[PROP_IS_READY]);
-	g_signal_emit(image, signals[SIG_READY], 0);
+	switch (param_id) {
+		case PROP_CONTENTS:
+			g_value_set_boxed(value, purple_image_get_contents(image));
+			break;
+		case PROP_SIZE:
+			g_value_set_uint64(value, purple_image_get_data_size(image));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
 }
 
 static void
-steal_contents(PurpleImagePrivate *priv, gpointer data, gsize length)
-{
-	g_return_if_fail(priv != NULL);
-	g_return_if_fail(priv->contents == NULL);
-	g_return_if_fail(data != NULL);
-	g_return_if_fail(length > 0);
+purple_image_class_init(PurpleImageClass *klass) {
+	GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
+
+	gobj_class->finalize = purple_image_finalize;
+	gobj_class->get_property = purple_image_get_property;
+	gobj_class->set_property = purple_image_set_property;
 
-	priv->contents = g_string_new(NULL);
-	g_free(priv->contents->str);
-	priv->contents->str = data;
-	priv->contents->len = priv->contents->allocated_len = length;
+	properties[PROP_CONTENTS] = g_param_spec_boxed(
+		"contents",
+		"contents",
+		"The contents of the image stored in a GBytes",
+		G_TYPE_BYTES,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
+	);
+
+	properties[PROP_SIZE] = g_param_spec_uint64(
+		"size",
+		"size",
+		"The size of the image in bytes",
+		0,
+		G_MAXUINT64,
+		0,
+		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS
+	);
+
+	g_object_class_install_properties(gobj_class, PROP_LAST, properties);
 }
 
-static void
-fill_data(PurpleImage *image)
-{
-	PurpleImagePrivate *priv;
-	GError *error = NULL;
-	gchar *contents;
-	gsize length;
-
-	priv = PURPLE_IMAGE_GET_PRIVATE(image);
-	if (priv->contents)
-		return;
-
-	if (!priv->is_ready)
-		return;
-
-	g_return_if_fail(priv->path);
-	(void)g_file_get_contents(priv->path, &contents, &length, &error);
-	if (error) {
-		purple_debug_error("image", "failed to read '%s' image: %s",
-			priv->path, error->message);
-		g_error_free(error);
-
-		has_failed(image);
-		return;
-	}
-
-	steal_contents(priv, contents, length);
-}
-
-
 /******************************************************************************
- * API implementation
+ * API
  ******************************************************************************/
-
 PurpleImage *
-purple_image_new_from_file(const gchar *path, gboolean be_eager)
-{
-	PurpleImagePrivate *priv;
-	PurpleImage *img;
-
-	g_return_val_if_fail(path != NULL, NULL);
-	g_return_val_if_fail(g_file_test(path, G_FILE_TEST_EXISTS), NULL);
-
-	img = g_object_new(PURPLE_TYPE_IMAGE, NULL);
-	purple_image_set_friendly_filename(img, path);
-	priv = PURPLE_IMAGE_GET_PRIVATE(img);
-	priv->path = g_strdup(path);
-
-	if (be_eager) {
-		fill_data(img);
-		if (!priv->contents) {
-			g_object_unref(img);
-			return NULL;
-		}
-
-		g_assert(priv->is_ready && !priv->has_failed);
-	}
-
-	return img;
+purple_image_new_from_bytes(GBytes *bytes) {
+	return g_object_new(
+		PURPLE_TYPE_IMAGE,
+		"contents", bytes,
+		NULL
+	);
 }
 
 PurpleImage *
-purple_image_new_from_data(gpointer data, gsize length)
-{
-	PurpleImage *img;
-	PurpleImagePrivate *priv;
+purple_image_new_from_file(const gchar *path, GError **error) {
+	PurpleImage *image = NULL;
+	GBytes *bytes = NULL;
+	gchar *contents = NULL;
+	gsize length = 0;
+
+	if(!g_file_get_contents(path, &contents, &length, error)) {
+		return NULL;
+	}
+
+	bytes = g_bytes_new_take(contents, length);
+
+	image = purple_image_new_from_bytes(bytes);
+
+	g_bytes_unref(bytes);
+
+	return image;
+}
 
-	g_return_val_if_fail(data != NULL, NULL);
-	g_return_val_if_fail(length > 0, NULL);
+PurpleImage *
+purple_image_new_from_data(const guint8 *data, gsize length) {
+	PurpleImage *image;
+	GBytes *bytes = NULL;
+
+	bytes = g_bytes_new(data, length);
+
+	image = purple_image_new_from_bytes(bytes);
+
+	g_bytes_unref(bytes);
 
-	img = g_object_new(PURPLE_TYPE_IMAGE, NULL);
-	priv = PURPLE_IMAGE_GET_PRIVATE(img);
+	return image;
+}
+
+PurpleImage *
+purple_image_new_take_data(guint8 *data, gsize length) {
+	PurpleImage *image;
+	GBytes *bytes = NULL;
 
-	steal_contents(priv, data, length);
+	bytes = g_bytes_new_take(data, length);
+
+	image = purple_image_new_from_bytes(bytes);
 
-	return img;
+	g_bytes_unref(bytes);
+
+	return image;
 }
 
 gboolean
-purple_image_save(PurpleImage *image, const gchar *path)
-{
+purple_image_save(PurpleImage *image, const gchar *path) {
 	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
 	gconstpointer data;
 	gsize len;
@@ -210,7 +220,7 @@
 	g_return_val_if_fail(path[0] != '\0', FALSE);
 
 	data = purple_image_get_data(image);
-	len = purple_image_get_size(image);
+	len = purple_image_get_data_size(image);
 
 	g_return_val_if_fail(data != NULL, FALSE);
 	g_return_val_if_fail(len > 0, FALSE);
@@ -222,9 +232,22 @@
 	return succ;
 }
 
+GBytes *
+purple_image_get_contents(const PurpleImage *image) {
+	PurpleImagePrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
+
+	priv = PURPLE_IMAGE_GET_PRIVATE(image);
+
+	if(priv->contents)
+		return g_bytes_ref(priv->contents);
+
+	return NULL;
+}
+
 const gchar *
-purple_image_get_path(PurpleImage *image)
-{
+purple_image_get_path(PurpleImage *image) {
 	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
 
 	g_return_val_if_fail(priv != NULL, NULL);
@@ -232,58 +255,36 @@
 	return priv->path;
 }
 
-gboolean
-purple_image_is_ready(PurpleImage *image)
-{
-	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
-
-	g_return_val_if_fail(priv != NULL, FALSE);
-
-	return priv->is_ready;
-}
+gsize
+purple_image_get_data_size(PurpleImage *image) {
+	PurpleImagePrivate *priv;
 
-gboolean
-purple_image_has_failed(PurpleImage *image)
-{
-	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
-
-	g_return_val_if_fail(priv != NULL, TRUE);
+	g_return_val_if_fail(PURPLE_IS_IMAGE(image), 0);
 
-	return priv->has_failed;
-}
-
-gsize
-purple_image_get_size(PurpleImage *image)
-{
-	PurpleImagePrivate *priv;
 	priv = PURPLE_IMAGE_GET_PRIVATE(image);
 
-	g_return_val_if_fail(priv != NULL, 0);
-	g_return_val_if_fail(priv->is_ready, 0);
+	if(priv->contents)
+		return g_bytes_get_size(priv->contents);
 
-	fill_data(image);
-	g_return_val_if_fail(priv->contents, 0);
-
-	return priv->contents->len;
+	return 0;
 }
 
 gconstpointer
-purple_image_get_data(PurpleImage *image)
-{
-	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
+purple_image_get_data(PurpleImage *image) {
+	PurpleImagePrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
 
-	g_return_val_if_fail(priv != NULL, NULL);
-	g_return_val_if_fail(priv->is_ready, NULL);
+	priv = PURPLE_IMAGE_GET_PRIVATE(image);
 
-	fill_data(image);
-	g_return_val_if_fail(priv->contents, NULL);
+	if(priv->contents)
+		return g_bytes_get_data(priv->contents, NULL);
 
-	return priv->contents->str;
+	return NULL;
 }
 
 const gchar *
-purple_image_get_extension(PurpleImage *image)
-{
+purple_image_get_extension(PurpleImage *image) {
 	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
 	gconstpointer data;
 
@@ -292,7 +293,7 @@
 	if (priv->extension)
 		return priv->extension;
 
-	if (purple_image_get_size(image) < 4)
+	if (purple_image_get_data_size(image) < 4)
 		return NULL;
 
 	data = purple_image_get_data(image);
@@ -317,8 +318,7 @@
 }
 
 const gchar *
-purple_image_get_mimetype(PurpleImage *image)
-{
+purple_image_get_mimetype(PurpleImage *image) {
 	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
 	const gchar *ext = purple_image_get_extension(image);
 
@@ -346,8 +346,7 @@
 }
 
 const gchar *
-purple_image_generate_filename(PurpleImage *image)
-{
+purple_image_generate_filename(PurpleImage *image) {
 	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
 	gconstpointer data;
 	gsize len;
@@ -361,7 +360,7 @@
 
 	ext = purple_image_get_extension(image);
 	data = purple_image_get_data(image);
-	len = purple_image_get_size(image);
+	len = purple_image_get_data_size(image);
 
 	g_return_val_if_fail(ext != NULL, NULL);
 	g_return_val_if_fail(data != NULL, NULL);
@@ -375,8 +374,7 @@
 }
 
 void
-purple_image_set_friendly_filename(PurpleImage *image, const gchar *filename)
-{
+purple_image_set_friendly_filename(PurpleImage *image, const gchar *filename) {
 	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
 	gchar *newname;
 	const gchar *escaped;
@@ -400,223 +398,17 @@
 }
 
 const gchar *
-purple_image_get_friendly_filename(PurpleImage *image)
-{
-	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
-
-	g_return_val_if_fail(priv != NULL, NULL);
-
-	if (G_UNLIKELY(!priv->friendly_filename)) {
-		const gchar *newname = purple_image_generate_filename(image);
-		gsize newname_len = strlen(newname);
-
-		if (newname_len < 10)
-			return NULL;
-
-		/* let's use last 6 characters from checksum + 4 characters
-		 * from file ext */
-		newname += newname_len - 10;
-		priv->friendly_filename = g_strdup(newname);
-	}
+purple_image_get_friendly_filename(PurpleImage *image) {
+	PurpleImagePrivate *priv = NULL;
 
-	if (G_UNLIKELY(priv->is_ready &&
-		strchr(priv->friendly_filename, '.') == NULL))
-	{
-		const gchar *ext = purple_image_get_extension(image);
-		gchar *tmp;
-		if (!ext)
-			return priv->friendly_filename;
-
-		tmp = g_strdup_printf("%s.%s", priv->friendly_filename, ext);
-		g_free(priv->friendly_filename);
-		priv->friendly_filename = tmp;
-	}
-
-	return priv->friendly_filename;
-}
-
-PurpleImage *
-purple_image_transfer_new(void)
-{
-	PurpleImage *img;
-	PurpleImagePrivate *priv;
+	g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
 
-	img = g_object_new(PURPLE_TYPE_IMAGE, NULL);
-	priv = PURPLE_IMAGE_GET_PRIVATE(img);
-
-	priv->is_ready = FALSE;
-	priv->contents = g_string_new(NULL);
-
-	return img;
-}
-
-void
-purple_image_transfer_write(PurpleImage *image, gconstpointer data,
-	gsize length)
-{
-	PurpleImagePrivate *priv =
-		PURPLE_IMAGE_GET_PRIVATE(image);
-
-	g_return_if_fail(priv != NULL);
-	g_return_if_fail(!priv->has_failed);
-	g_return_if_fail(!priv->is_ready);
-	g_return_if_fail(priv->contents != NULL);
-	g_return_if_fail(data != NULL || length == 0);
+	priv = PURPLE_IMAGE_GET_PRIVATE(image);
 
-	if (length == 0)
-		return;
-
-	g_string_append_len(priv->contents, (const gchar*)data, length);
-}
-
-void
-purple_image_transfer_close(PurpleImage *image)
-{
-	PurpleImagePrivate *priv =
-		PURPLE_IMAGE_GET_PRIVATE(image);
-
-	g_return_if_fail(priv != NULL);
-	g_return_if_fail(!priv->has_failed);
-	g_return_if_fail(!priv->is_ready);
-	g_return_if_fail(priv->contents != NULL);
-
-	if (priv->contents->len == 0) {
-		purple_debug_error("image", "image is empty");
-		has_failed(image);
-		return;
+	if(priv->friendly_filename) {
+		return priv->friendly_filename;
 	}
 
-	became_ready(image);
-}
-
-void
-purple_image_transfer_failed(PurpleImage *image)
-{
-	PurpleImagePrivate *priv =
-		PURPLE_IMAGE_GET_PRIVATE(image);
-
-	g_return_if_fail(priv != NULL);
-	g_return_if_fail(!priv->has_failed);
-	g_return_if_fail(!priv->is_ready);
-
-	has_failed(image);
-}
-
-/******************************************************************************
- * Object stuff
- ******************************************************************************/
-
-static void
-purple_image_init(GTypeInstance *instance, gpointer klass)
-{
-	PurpleImage *image = PURPLE_IMAGE(instance);
-	PurpleImagePrivate *priv =
-		PURPLE_IMAGE_GET_PRIVATE(image);
-
-	priv->contents = NULL;
-	priv->is_ready = TRUE;
-	priv->has_failed = FALSE;
-}
-
-static void
-purple_image_finalize(GObject *obj)
-{
-	PurpleImage *image = PURPLE_IMAGE(obj);
-	PurpleImagePrivate *priv =
-		PURPLE_IMAGE_GET_PRIVATE(image);
-
-	if (priv->contents)
-		g_string_free(priv->contents, TRUE);
-	g_free(priv->path);
-	g_free(priv->gen_filename);
-	g_free(priv->friendly_filename);
-
-	G_OBJECT_CLASS(parent_class)->finalize(obj);
-}
-
-static void
-purple_image_get_property(GObject *object, guint par_id, GValue *value,
-	GParamSpec *pspec)
-{
-	PurpleImage *image = PURPLE_IMAGE(object);
-	PurpleImagePrivate *priv = PURPLE_IMAGE_GET_PRIVATE(image);
-
-	switch (par_id) {
-		case PROP_IS_READY:
-			g_value_set_boolean(value, priv->is_ready);
-			break;
-		case PROP_HAS_FAILED:
-			g_value_set_boolean(value, priv->has_failed);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
-			break;
-	}
+	return purple_image_generate_filename(image);
 }
-
-static void
-purple_image_class_init(PurpleImageClass *klass)
-{
-	GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
-
-	parent_class = g_type_class_peek_parent(klass);
-
-	g_type_class_add_private(klass, sizeof(PurpleImagePrivate));
-
-	gobj_class->finalize = purple_image_finalize;
-	gobj_class->get_property = purple_image_get_property;
-
-	properties[PROP_IS_READY] = g_param_spec_boolean("is-ready",
-		"Is ready", "The image is ready to be displayed. Image may "
-		"change the state to failed in a single case: if it's backed "
-		"by a file and that file fails to load",
-		TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-	properties[PROP_HAS_FAILED] = g_param_spec_boolean("has-failed",
-		"Has hailed", "The remote host has failed to send the image",
-		FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties(gobj_class, PROP_LAST, properties);
-
-	/**
-	 * PurpleImage::failed:
-	 * @image: a image that failed to transfer.
-	 *
-	 * Called when a @image fails to be transferred. It's guaranteed to be
-	 * fired at most once for a particular @image.
-	 */
-	signals[SIG_FAILED] = g_signal_new("failed", G_OBJECT_CLASS_TYPE(klass),
-		0, 0, NULL, NULL,
-		g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
-
-	/**
-	 * PurpleImage::ready:
-	 * @image: a image that became ready.
-	 *
-	 * Called when a @image becames ready to be displayed. It's guaranteed to be
-	 * fired at most once for a particular @image.
-	 */
-	signals[SIG_READY] = g_signal_new("ready", G_OBJECT_CLASS_TYPE(klass),
-		0, 0, NULL, NULL,
-		g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
-}
-
-GType
-purple_image_get_type(void)
-{
-	static GType type = 0;
-
-	if (G_UNLIKELY(type == 0)) {
-		static const GTypeInfo info = {
-			.class_size = sizeof(PurpleImageClass),
-			.class_init = (GClassInitFunc)purple_image_class_init,
-			.instance_size = sizeof(PurpleImage),
-			.instance_init = purple_image_init,
-		};
-
-		type = g_type_register_static(G_TYPE_OBJECT,
-			"PurpleImage", &info, 0);
-	}
-
-	return type;
-}
+ 
--- a/libpurple/image.h	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/image.h	Sun May 28 13:26:27 2017 +0300
@@ -19,8 +19,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
  */
 
-#ifndef _PURPLE_IMAGE_H_
-#define _PURPLE_IMAGE_H_
+#ifndef PURPLE_IMAGE_H
+#define PURPLE_IMAGE_H
+
 /**
  * SECTION:image
  * @include:image.h
@@ -39,13 +40,13 @@
 
 #include <glib-object.h>
 
-typedef struct _PurpleImage PurpleImage;
+typedef struct _PurpleImage      PurpleImage;
 typedef struct _PurpleImageClass PurpleImageClass;
 
 #define PURPLE_TYPE_IMAGE            (purple_image_get_type())
-#define PURPLE_IMAGE(smiley)         (G_TYPE_CHECK_INSTANCE_CAST((smiley), PURPLE_TYPE_IMAGE, PurpleImage))
+#define PURPLE_IMAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_IMAGE, PurpleImage))
 #define PURPLE_IMAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_IMAGE, PurpleImageClass))
-#define PURPLE_IS_IMAGE(smiley)      (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PURPLE_TYPE_IMAGE))
+#define PURPLE_IS_IMAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_IMAGE))
 #define PURPLE_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_IMAGE))
 #define PURPLE_IMAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_IMAGE, PurpleImageClass))
 
@@ -54,20 +55,12 @@
  *
  * An image data container.
  */
-struct _PurpleImage
-{
+struct _PurpleImage {
 	/*< private >*/
 	GObject parent;
 };
 
-/**
- * PurpleImageClass:
- *
- * Base class for #PurpleImage objects.
- */
-struct _PurpleImageClass
-{
-	/*< private >*/
+struct _PurpleImageClass {
 	GObjectClass parent_class;
 
 	void (*purple_reserved1)(void);
@@ -83,13 +76,22 @@
  *
  * Returns: the #GType for an image.
  */
-GType
-purple_image_get_type(void);
+GType purple_image_get_type(void);
+
+/**
+ * purple_image_new_from_bytes:
+ * @bytes: (transfer none) A #GBytes containing the raw image data.
+ *
+ * Loads a raw image data as a new #PurpleImage object.
+ *
+ * Returns: the new #PurpleImage.
+ */
+PurpleImage *purple_image_new_from_bytes(GBytes *bytes);
 
 /**
  * purple_image_new_from_file:
  * @path: the path to the image file.
- * @be_eager: %TRUE, if file should be loaded now, %FALSE when needed.
+ * @error: (optional) An optional return address for a #GError
  *
  * Loads an image file as a new #PurpleImage object. The @path must exists, be
  * readable and should point to a valid image file. If you don't set @be_eager
@@ -98,8 +100,7 @@
  *
  * Returns: the new #PurpleImage.
  */
-PurpleImage *
-purple_image_new_from_file(const gchar *path, gboolean be_eager);
+PurpleImage *purple_image_new_from_file(const gchar *path, GError **error);
 
 /**
  * purple_image_new_from_data:
@@ -113,8 +114,21 @@
  *
  * Returns: the new #PurpleImage.
  */
-PurpleImage *
-purple_image_new_from_data(gpointer data, gsize length);
+PurpleImage *purple_image_new_from_data(const guint8 *data, gsize length);
+
+/**
+ * purple_image_new_take_data:
+ * @data: (transfer full) the pointer to the image data buffer.
+ * @length: the length of @data.
+ *
+ * Creates a new #PurpleImage object with contents of @data buffer.
+ *
+ * The @data buffer is owned by #PurpleImage object, so you might want
+ * to #g_memdup it first.
+ *
+ * Returns: the new #PurpleImage.
+ */
+PurpleImage *purple_image_new_take_data(guint8 *data, gsize length);
 
 /**
  * purple_image_save:
@@ -125,8 +139,18 @@
  *
  * Returns: %TRUE if succeeded, %FALSE otherwise.
  */
-gboolean
-purple_image_save(PurpleImage *image, const gchar *path);
+gboolean purple_image_save(PurpleImage *image, const gchar *path);
+
+/**
+ * purple_image_get_contents:
+ * @image: The #PurpleImage.
+ *
+ * Returns a new reference to the #GBytes that contains the image data.
+ *
+ * Returns: (transfer full): A #GBytes containing the image data.
+ */
+GBytes *purple_image_get_contents(const PurpleImage *image);
+
 
 /**
  * purple_image_get_path:
@@ -137,46 +161,17 @@
  *
  * Returns: the physical path of the @image, or %NULL.
  */
-const gchar *
-purple_image_get_path(PurpleImage *image);
+const gchar *purple_image_get_path(PurpleImage *image);
 
 /**
- * purple_image_is_ready:
- * @image: the image.
- *
- * Checks, if the @image data is ready to be displayed. Remote image data may
- * not be accessible at the moment, so you should check it before using
- * #purple_image_get_data. If the image is not ready yet, you may wait for
- * #PurpleImage::ready signal.
- *
- * Returns: %TRUE, if the @image is ready.
- */
-gboolean
-purple_image_is_ready(PurpleImage *image);
-
-/**
- * purple_image_has_failed:
- * @image: the image.
- *
- * Checks, if the @image has failed to load its data. It can be caused either by
- * a remote failure (and #purple_image_transfer_failed call) or local file being
- * removed (see #purple_image_new_from_file).
- *
- * Returns: %TRUE, if the @image has failed to load.
- */
-gboolean
-purple_image_has_failed(PurpleImage *image);
-
-/**
- * purple_image_get_size:
+ * purple_image_get_data_size:
  * @image: the image.
  *
  * Returns the size of @image's data.
  *
  * Returns: the size of data, or 0 in case of failure.
  */
-gsize
-purple_image_get_size(PurpleImage *image);
+gsize purple_image_get_data_size(PurpleImage *image);
 
 /**
  * purple_image_get_data:
@@ -186,8 +181,7 @@
  *
  * Returns: (transfer none): the @image data.
  */
-gconstpointer
-purple_image_get_data(PurpleImage *image);
+gconstpointer purple_image_get_data(PurpleImage *image);
 
 /**
  * purple_image_get_extension:
@@ -197,8 +191,7 @@
  *
  * Returns: (transfer none): the file extension suitable for @image format.
  */
-const gchar *
-purple_image_get_extension(PurpleImage *image);
+const gchar *purple_image_get_extension(PurpleImage *image);
 
 /**
  * purple_image_get_mimetype:
@@ -208,8 +201,7 @@
  *
  * Returns: (transfer none): the mime-type suitable for @image format.
  */
-const gchar *
-purple_image_get_mimetype(PurpleImage *image);
+const gchar *purple_image_get_mimetype(PurpleImage *image);
 
 /**
  * purple_image_generate_filename:
@@ -221,8 +213,7 @@
  *
  * Returns: (transfer none): the generated file name.
  */
-const gchar *
-purple_image_generate_filename(PurpleImage *image);
+const gchar *purple_image_generate_filename(PurpleImage *image);
 
 /**
  * purple_image_set_friendly_filename:
@@ -236,8 +227,7 @@
  * The provided @filename may either be a full path, or contain
  * filesystem-unfriendly characters, because it will be reformatted.
  */
-void
-purple_image_set_friendly_filename(PurpleImage *image, const gchar *filename);
+void purple_image_set_friendly_filename(PurpleImage *image, const gchar *filename);
 
 /**
  * purple_image_get_friendly_filename:
@@ -252,54 +242,8 @@
  *
  * Returns: (transfer none): the friendly filename.
  */
-const gchar *
-purple_image_get_friendly_filename(PurpleImage *image);
-
-/**
- * purple_image_transfer_new:
- *
- * Creates a new remote image, with a possibility to load data with
- * #purple_image_transfer_write and #purple_image_transfer_close. In case of
- * failure, you should call #urple_image_transfer_failed.
- *
- * Returns: the new image object.
- */
-PurpleImage *
-purple_image_transfer_new(void);
-
-/**
- * purple_image_transfer_write:
- * @image: the image.
- * @data: the incoming data buffer.
- * @length: the length of @data.
- *
- * Adds a chunk of data to the internal receive buffer. Called when receiving
- * a remote file.
- */
-void
-purple_image_transfer_write(PurpleImage *image, gconstpointer data,
-	gsize length);
-
-/**
- * purple_image_transfer_close:
- * @image: the image.
- *
- * Marks a remote @image as ready to be displayed. Called when finishing
- * transfer of remote file. You may call this only once for a certain @image.
- */
-void
-purple_image_transfer_close(PurpleImage *image);
-
-/**
- * purple_image_transfer_failed:
- * @image: the image.
- *
- * Marks a remote @image as failed to transfer. Called on error in remote file
- * transfer. You may call this only once for a certain @image.
- */
-void
-purple_image_transfer_failed(PurpleImage *image);
+const gchar *purple_image_get_friendly_filename(PurpleImage *image);
 
 G_END_DECLS
 
-#endif /* _PURPLE_IMAGE_H_ */
+#endif /* PURPLE_IMAGE_H */
--- a/libpurple/log.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/log.c	Sun May 28 13:26:27 2017 +0300
@@ -845,7 +845,7 @@
 			}
 
 			image_data       = purple_image_get_data(image);
-			image_byte_count = purple_image_get_size(image);
+			image_byte_count = purple_image_get_data_size(image);
 			dir              = purple_log_get_log_dir(log->type, log->name, log->account);
 			new_filename = purple_image_generate_filename(image);
 
--- a/libpurple/protocols/bonjour/mdns_common.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/bonjour/mdns_common.c	Sun May 28 13:26:27 2017 +0300
@@ -186,7 +186,7 @@
 		gsize avatar_len;
 
 		avatar_data = purple_image_get_data(img);
-		avatar_len = purple_image_get_size(img);
+		avatar_len = purple_image_get_data_size(img);
 
 		if (_mdns_set_buddy_icon_data(data, avatar_data, avatar_len)) {
 			g_free(data->phsh);
--- a/libpurple/protocols/gg/avatar.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/gg/avatar.c	Sun May 28 13:26:27 2017 +0300
@@ -354,7 +354,7 @@
 	own_data->img = NULL;
 
 	img_data = purple_base64_encode(purple_image_get_data(img),
-		purple_image_get_size(img));
+		purple_image_get_data_size(img));
 	img_data_e = g_uri_escape_string(img_data, NULL, FALSE);
 	g_free(img_data);
 	request_data = g_strdup_printf("uin=%d&photo=%s", uin, img_data_e);
--- a/libpurple/protocols/gg/image-prpl.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/gg/image-prpl.c	Sun May 28 13:26:27 2017 +0300
@@ -104,7 +104,7 @@
 
 	g_return_val_if_fail(image, GGP_IMAGE_PREPARE_FAILURE);
 
-	image_size = purple_image_get_size(image);
+	image_size = purple_image_get_data_size(image);
 
 	if (image_size > GGP_IMAGE_SIZE_MAX) {
 		purple_debug_warning("gg", "ggp_image_prepare: image "
@@ -153,12 +153,13 @@
 		image_reply->crc32, image_reply->size,
 		image_reply->filename, id);
 
+	img = purple_image_new_from_data(
+		(const guint8 *)image_reply->image,
+		image_reply->size
+	);
 	purple_image_set_friendly_filename(img, image_reply->filename);
 
-	purple_image_transfer_write(img,
-		g_memdup(image_reply->image, image_reply->size),
-		image_reply->size);
-	purple_image_transfer_close(img);
+	g_hash_table_insert(sdata->recv_images, &id, img);
 }
 
 void ggp_image_send(PurpleConnection *gc,
@@ -205,7 +206,7 @@
 	gg_image_reply(accdata->session, image_request->sender,
 		gg_filename,
 		purple_image_get_data(sent_image->image),
-		purple_image_get_size(sent_image->image));
+		purple_image_get_data_size(sent_image->image));
 	g_free(gg_filename);
 
 	conv = purple_conversations_find_with_account(
@@ -247,8 +248,7 @@
 	}
 
 
-	img = purple_image_transfer_new();
-	g_hash_table_insert(sdata->recv_images, ggp_uint64dup(id), img);
+	g_hash_table_insert(sdata->recv_images, ggp_uint64dup(id), NULL);
 
 	purple_debug_info("gg", "ggp_image_request: requesting image "
 		GGP_IMAGE_ID_FORMAT, id);
--- a/libpurple/protocols/jabber/buddy.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/jabber/buddy.c	Sun May 28 13:26:27 2017 +0300
@@ -474,7 +474,9 @@
 		if (image != NULL) {
 			js->initial_avatar_hash = jabber_calculate_data_hash(
 				purple_image_get_data(image),
-				purple_image_get_size(image), "sha1");
+				purple_image_get_data_size(image),
+				"sha1"
+			);
 			g_object_unref(image);
 		} else {
 			js->initial_avatar_hash = NULL;
@@ -514,7 +516,7 @@
 		}
 
 		avatar_data = purple_image_get_data(img);
-		avatar_len = purple_image_get_size(img);
+		avatar_len = purple_image_get_data_size(img);
 		/* Get rid of an old PHOTO if one exists.
 		 * TODO: This may want to be modified to remove all old PHOTO
 		 * children, at the moment some people have managed to get
--- a/libpurple/protocols/jabber/jabber.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/jabber/jabber.c	Sun May 28 13:26:27 2017 +0300
@@ -1137,7 +1137,9 @@
 	if (image != NULL) {
 		js->initial_avatar_hash = jabber_calculate_data_hash(
 			purple_image_get_data(image),
-			purple_image_get_size(image), "sha1");
+			purple_image_get_data_size(image),
+			"sha1"
+		);
 		g_object_unref(image);
 	}
 
--- a/libpurple/protocols/jabber/message.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/jabber/message.c	Sun May 28 13:26:27 2017 +0300
@@ -41,6 +41,11 @@
 
 #include <string.h>
 
+typedef struct {
+	PurpleConversation *conv;
+	gchar *shortcut;
+} JabberMessageRemoteSmileyAddData;
+
 void jabber_message_free(JabberMessage *jm)
 {
 	g_free(jm->from);
@@ -487,63 +492,75 @@
 }
 
 static void
-jabber_message_remote_smiley_got(JabberData *data, gchar *alt, gpointer _smiley)
-{
-	PurpleSmiley *smiley = _smiley;
-	PurpleImage *image = purple_smiley_get_image(smiley);
+jabber_message_remote_smiley_got(JabberData *jdata, gchar *alt, gpointer d) {
+	JabberMessageRemoteSmileyAddData *data = (JabberMessageRemoteSmileyAddData *)d;
 
-	g_free(alt); /* we really don't need it */
+	if (jdata) {
+		PurpleSmiley *smiley = NULL;
 
-	if (data) {
 		purple_debug_info("jabber",
 			"smiley data retrieved successfully");
-		purple_image_transfer_write(image, jabber_data_get_data(data),
-			jabber_data_get_size(data));
-		purple_image_transfer_close(image);
+
+		smiley = purple_smiley_new_from_data(
+			data->shortcut,
+			jabber_data_get_data(jdata),
+			jabber_data_get_size(jdata)
+		);
+
+		purple_conversation_add_smiley(data->conv, smiley);
+
+		g_object_unref(G_OBJECT(smiley));
 	} else {
 		purple_debug_error("jabber", "failed retrieving smiley data");
-		purple_image_transfer_failed(image);
 	}
 
-	g_object_unref(smiley);
+	g_object_unref(G_OBJECT(data->conv));
+	g_free(data->shortcut);
+	g_slice_free(JabberMessageRemoteSmileyAddData, data);
 }
 
 static void
 jabber_message_remote_smiley_add(JabberStream *js, PurpleConversation *conv,
 	const gchar *from, const gchar *shortcut, const gchar *cid)
 {
-	PurpleSmiley *smiley;
-	const JabberData *jdata;
+	PurpleSmiley *smiley = NULL;
+	const JabberData *jdata = NULL;
 
 	purple_debug_misc("jabber", "about to add remote smiley %s to the conv",
 		shortcut);
 
-	smiley = purple_conversation_add_remote_smiley(conv, shortcut);
-	if (!smiley) {
+	smiley = purple_conversation_get_smiley(conv, shortcut);
+	if(PURPLE_IS_SMILEY(smiley)) {
 		purple_debug_misc("jabber", "smiley was already present");
 		return;
 	}
 
 	/* TODO: cache lookup by "cid" */
-
 	jdata = jabber_data_find_remote_by_cid(js, from, cid);
 	if (jdata) {
-		PurpleImage *image = purple_smiley_get_image(smiley);
-
 		purple_debug_info("jabber", "smiley data is already known");
 
-		purple_image_transfer_write(image, jabber_data_get_data(jdata),
-			jabber_data_get_size(jdata));
-		purple_image_transfer_close(image);
+		smiley = purple_smiley_new_from_data(
+			shortcut,
+			jabber_data_get_data(jdata),
+			jabber_data_get_size(jdata)
+		);
+
+		purple_conversation_add_smiley(conv, smiley);
+
+		g_object_unref(G_OBJECT(smiley));
 	} else {
-		gchar *alt = g_strdup(shortcut); /* it it really necessary? */
+		JabberMessageRemoteSmileyAddData *data = NULL;
+
+		data = g_slice_new(JabberMessageRemoteSmileyAddData);
+		data->conv = g_object_ref(G_OBJECT(conv));
+		data->shortcut = g_strdup(shortcut);
 
 		purple_debug_info("jabber", "smiley data is unknown, "
 			"need to request it");
 
-		g_object_ref(smiley);
-		jabber_data_request(js, cid, from, alt, FALSE,
-			jabber_message_remote_smiley_got, smiley);
+		jabber_data_request(js, cid, from, NULL, FALSE,
+			jabber_message_remote_smiley_got, data);
 	}
 }
 
@@ -914,19 +931,11 @@
 
 	for (it = found_smileys; it; it = it_next) {
 		PurpleSmiley *smiley = it->data;
-		PurpleImage *smiley_image;
 		gboolean valid = TRUE;
 
 		it_next = g_list_next(it);
 
-		smiley_image = purple_smiley_get_image(smiley);
-		if (!smiley_image) {
-			valid = FALSE;
-			purple_debug_warning("jabber", "broken smiley %s",
-				purple_smiley_get_shortcut(smiley));
-		} else if (purple_image_get_size(smiley_image) >
-			JABBER_DATA_MAX_SIZE)
-		{
+		if (purple_image_get_data_size(PURPLE_IMAGE(smiley)) > JABBER_DATA_MAX_SIZE) {
 			has_too_large_smiley = TRUE;
 			valid = FALSE;
 			purple_debug_warning("jabber", "Refusing to send "
@@ -950,7 +959,6 @@
 
 	for (it = found_smileys; it; it = g_list_next(it)) {
 		PurpleSmiley *smiley = it->data;
-		PurpleImage *smiley_image;
 		const gchar *shortcut = purple_smiley_get_shortcut(smiley);
 		const gchar *mimetype;
 		JabberData *jdata;
@@ -959,10 +967,7 @@
 		if (jabber_data_find_local_by_alt(shortcut))
 			continue;
 
-		smiley_image = purple_smiley_get_image(smiley);
-		g_assert(smiley_image != NULL);
-
-		mimetype = purple_image_get_mimetype(smiley_image);
+		mimetype = purple_image_get_mimetype(PURPLE_IMAGE(smiley));
 		if (!mimetype) {
 			purple_debug_error("jabber",
 				"unknown mime type for image");
@@ -970,8 +975,8 @@
 		}
 
 		jdata = jabber_data_create_from_data(
-			purple_image_get_data(smiley_image),
-			purple_image_get_size(smiley_image),
+			purple_image_get_data(PURPLE_IMAGE(smiley)),
+			purple_image_get_data_size(PURPLE_IMAGE(smiley)),
 			mimetype, FALSE, jm->js);
 
 		purple_debug_info("jabber", "cache local smiley alt=%s, cid=%s",
--- a/libpurple/protocols/jabber/useravatar.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/jabber/useravatar.c	Sun May 28 13:26:27 2017 +0300
@@ -130,7 +130,7 @@
 			} ihdr;
 		} *png = NULL;
 
-		if (purple_image_get_size(img) > sizeof(*png))
+		if (purple_image_get_data_size(img) > sizeof(*png))
 			png = purple_image_get_data(img);
 
 		/* check if the data is a valid png file (well, at least to some extent) */
@@ -156,10 +156,10 @@
 			/* compute the sha1 hash */
 			char *hash = jabber_calculate_data_hash(
 				purple_image_get_data(img),
-				purple_image_get_size(img), "sha1");
+				purple_image_get_data_size(img), "sha1");
 			char *base64avatar = purple_base64_encode(
 				purple_image_get_data(img),
-				purple_image_get_size(img));
+				purple_image_get_data_size(img));
 
 			publish = purple_xmlnode_new("publish");
 			purple_xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_DATA);
@@ -177,7 +177,7 @@
 			g_free(base64avatar);
 
 			lengthstring = g_strdup_printf("%" G_GSIZE_FORMAT,
-				purple_image_get_size(img));
+				purple_image_get_data_size(img));
 			widthstring = g_strdup_printf("%u", width);
 			heightstring = g_strdup_printf("%u", height);
 
--- a/libpurple/protocols/oscar/family_oservice.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/oscar/family_oservice.c	Sun May 28 13:26:27 2017 +0300
@@ -928,7 +928,7 @@
 									"Uploading icon to icon server\n");
 					aim_bart_upload(od,
 						purple_image_get_data(img),
-						purple_image_get_size(img));
+						purple_image_get_data_size(img));
 					g_object_unref(img);
 				}
 			}
--- a/libpurple/protocols/oscar/oscar.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/oscar/oscar.c	Sun May 28 13:26:27 2017 +0300
@@ -1422,7 +1422,7 @@
 	if ((img != NULL) &&
 	    (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) {
 		gconstpointer data = purple_image_get_data(img);
-		size_t len = purple_image_get_size(img);
+		size_t len = purple_image_get_data_size(img);
 		purple_debug_info("oscar",
 				"Sending buddy icon to %s (%" G_GSIZE_FORMAT " bytes)\n",
 				userinfo->bn, len);
@@ -2546,7 +2546,7 @@
 			purple_debug_info("oscar",
 				"Uploading icon to icon server");
 			aim_bart_upload(od, purple_image_get_data(img),
-				purple_image_get_size(img));
+				purple_image_get_data_size(img));
 			g_object_unref(img);
 		}
 		od->set_icon = FALSE;
@@ -3057,7 +3057,7 @@
 		/* ... if it refers to a valid purple image ... */
 		if (image) {
 			/* ... append the message from start to the tag ... */
-			unsigned long size = purple_image_get_size(image);
+			unsigned long size = purple_image_get_data_size(image);
 			const gchar *filename = purple_image_get_friendly_filename(image);
 			gconstpointer imgdata = purple_image_get_data(image);
 
@@ -3205,7 +3205,7 @@
 		img = purple_buddy_icons_find_account_icon(account);
 		if (img) {
 			gconstpointer data = purple_image_get_data(img);
-			args.iconlen = purple_image_get_size(img);
+			args.iconlen = purple_image_get_data_size(img);
 			args.iconsum = aimutil_iconsum(data, args.iconlen);
 			args.iconstamp = purple_buddy_icons_get_account_icon_timestamp(account);
 
@@ -5241,7 +5241,7 @@
 		PurpleHash *hash;
 		guchar md5[16];
 		gconstpointer data = purple_image_get_data(img);
-		size_t len = purple_image_get_size(img);
+		size_t len = purple_image_get_data_size(img);
 
 		hash = purple_md5_hash_new();
 		purple_hash_append(hash, data, len);
--- a/libpurple/protocols/sametime/sametime.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/sametime/sametime.c	Sun May 28 13:26:27 2017 +0300
@@ -3878,7 +3878,7 @@
 
       /* obtain and base64 encode the image data, and put it in the
 	 mime part */
-      size = purple_image_get_size(img);
+      size = purple_image_get_data_size(img);
       data = purple_base64_encode(purple_image_get_data(img), size);
       purple_mime_part_set_data(part, data);
       g_free(data);
--- a/libpurple/protocols/silc/buddy.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/silc/buddy.c	Sun May 28 13:26:27 2017 +0300
@@ -1751,7 +1751,7 @@
 
 	silc_mime_add_field(mime, "Content-Type", type);
 	silc_mime_add_data(mime, purple_image_get_data(img),
-		purple_image_get_size(img));
+		purple_image_get_data_size(img));
 
 	silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_USER_ICON,
 		mime, sizeof(*mime));
--- a/libpurple/protocols/silc/util.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/protocols/silc/util.c	Sun May 28 13:26:27 2017 +0300
@@ -623,7 +623,7 @@
 		if (uri)
 			image = purple_image_store_get_from_uri(uri);
 		if (uri) {
-			unsigned long imglen = purple_image_get_size(image);
+			unsigned long imglen = purple_image_get_data_size(image);
 			gconstpointer img = purple_image_get_data(image);
 			const gchar *type;
 
--- a/libpurple/smiley-custom.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/smiley-custom.c	Sun May 28 13:26:27 2017 +0300
@@ -1,4 +1,4 @@
-/* purpleOB
+/* 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
@@ -133,7 +133,7 @@
 		purple_xmlnode_set_attrib(smiley_node, "shortcut",
 			purple_smiley_get_shortcut(smiley));
 		basename = g_path_get_basename(purple_image_get_path(
-				purple_smiley_get_image(smiley)));
+				PURPLE_IMAGE(smiley)));
 		purple_xmlnode_set_attrib(smiley_node, "filename", basename);
 		g_free(basename);
 	}
@@ -175,7 +175,7 @@
 
 	return g_compute_checksum_for_data(G_CHECKSUM_SHA1,
 		purple_image_get_data(img),
-		purple_image_get_size(img));
+		purple_image_get_data_size(img));
 }
 
 
@@ -262,14 +262,13 @@
 	g_object_ref(smiley);
 	purple_smiley_list_remove(smileys_list, smiley);
 
-	path = purple_image_get_path(purple_smiley_get_image(smiley));
+	path = purple_image_get_path(PURPLE_IMAGE(smiley));
 
 	other_smileys = purple_smiley_list_get_unique(smileys_list);
 	is_unique = TRUE;
 	for (it = other_smileys; it; it = g_list_next(it)) {
 		PurpleSmiley *other = it->data;
-		const gchar *other_path = purple_image_get_path(
-			purple_smiley_get_image(other));
+		const gchar *other_path = purple_image_get_path(PURPLE_IMAGE(other));
 		if (g_strcmp0(other_path, path) == 0) {
 			is_unique = FALSE;
 			break;
--- a/libpurple/smiley-list.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/smiley-list.c	Sun May 28 13:26:27 2017 +0300
@@ -47,9 +47,106 @@
 	PROP_LAST
 };
 
-static GObjectClass *parent_class;
 static GParamSpec *properties[PROP_LAST];
 
+/*******************************************************************************
+ * Object stuff
+ ******************************************************************************/
+G_DEFINE_TYPE_WITH_PRIVATE(PurpleSmileyList, purple_smiley_list, G_TYPE_OBJECT);
+
+static void
+purple_smiley_list_init(PurpleSmileyList *list) {
+	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+
+	priv->trie = purple_trie_new();
+	priv->path_map = g_hash_table_new_full(g_str_hash, g_str_equal,
+		g_free, NULL);
+	priv->shortcut_map = g_hash_table_new_full(g_str_hash, g_str_equal,
+		g_free, NULL);
+
+	PURPLE_DBUS_REGISTER_POINTER(list, PurpleSmileyList);
+}
+
+static void
+purple_smiley_list_finalize(GObject *obj) {
+	PurpleSmileyList *sl = PURPLE_SMILEY_LIST(obj);
+	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
+	GList *it;
+
+	g_object_unref(priv->trie);
+	g_hash_table_destroy(priv->path_map);
+	g_hash_table_destroy(priv->shortcut_map);
+
+	for (it = priv->smileys; it; it = g_list_next(it)) {
+		PurpleSmiley *smiley = it->data;
+		g_object_set_data(G_OBJECT(smiley), "purple-smiley-list", NULL);
+		g_object_set_data(G_OBJECT(smiley), "purple-smiley-list-elem", NULL);
+		g_object_unref(smiley);
+	}
+	g_list_free(priv->smileys);
+
+	PURPLE_DBUS_UNREGISTER_POINTER(sl);
+
+	G_OBJECT_CLASS(purple_smiley_list_parent_class)->finalize(obj);
+}
+
+static void
+purple_smiley_list_get_property(GObject *obj, guint param_id, GValue *value,
+                                GParamSpec *pspec)
+{
+	PurpleSmileyList *sl = PURPLE_SMILEY_LIST(obj);
+	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
+
+	switch (param_id) {
+		case PROP_DROP_FAILED_REMOTES:
+			g_value_set_boolean(value, priv->drop_failed_remotes);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_smiley_list_set_property(GObject *obj, guint param_id,
+                                const GValue *value, GParamSpec *pspec)
+{
+	PurpleSmileyList *sl = PURPLE_SMILEY_LIST(obj);
+	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
+
+	switch (param_id) {
+		case PROP_DROP_FAILED_REMOTES:
+			priv->drop_failed_remotes = g_value_get_boolean(value);
+			/* XXX: we could scan for remote smiley's on our list
+			 * and change the setting, but we don't care that much.
+			 */
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_smiley_list_class_init(PurpleSmileyListClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->get_property = purple_smiley_list_get_property;
+	obj_class->set_property = purple_smiley_list_set_property;
+	obj_class->finalize = purple_smiley_list_finalize;
+
+	properties[PROP_DROP_FAILED_REMOTES] = g_param_spec_boolean(
+		"drop-failed-remotes", "Drop failed PurpleRemoteSmileys",
+		"Watch added remote smileys and remove them from the list, "
+		"if they change their state to failed", FALSE,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
 static void
 _list_append2(GList **head_p, GList **tail_p, gpointer data)
 {
@@ -89,40 +186,20 @@
 static const gchar *
 smiley_get_uniqid(PurpleSmiley *smiley)
 {
-	return purple_image_get_path(purple_smiley_get_image(smiley));
+	return purple_image_get_path(PURPLE_IMAGE(smiley));
 }
 
 /*******************************************************************************
  * API implementation
  ******************************************************************************/
-
 PurpleSmileyList *
-purple_smiley_list_new(void)
-{
+purple_smiley_list_new(void) {
 	return g_object_new(PURPLE_TYPE_SMILEY_LIST, NULL);
 }
 
-static void
-remote_smiley_failed(PurpleImage *smiley_img, gpointer _list)
-{
-	PurpleSmileyList *list = _list;
-	PurpleSmiley *smiley;
-
-	smiley = g_object_get_data(G_OBJECT(smiley_img),
-		"purple-smiley-list-smiley");
-
-	purple_debug_info("smiley-list", "remote smiley '%s' has failed, "
-		"removing it from the list...",
-		purple_smiley_get_shortcut(smiley));
-
-	purple_smiley_list_remove(list, smiley);
-}
-
 gboolean
-purple_smiley_list_add(PurpleSmileyList *list, PurpleSmiley *smiley)
-{
+purple_smiley_list_add(PurpleSmileyList *list, PurpleSmiley *smiley) {
 	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
-	PurpleImage *smiley_img;
 	const gchar *smiley_path;
 	gboolean succ;
 	gchar *shortcut_escaped;
@@ -168,14 +245,6 @@
 
 	g_hash_table_insert(priv->shortcut_map, g_strdup(shortcut), smiley);
 
-	smiley_img = purple_smiley_get_image(smiley);
-	if (priv->drop_failed_remotes && !purple_image_is_ready(smiley_img)) {
-		g_object_set_data(G_OBJECT(smiley_img),
-			"purple-smiley-list-smiley", smiley);
-		g_signal_connect_object(smiley_img, "failed",
-			G_CALLBACK(remote_smiley_failed), list, 0);
-	}
-
 	smiley_path = smiley_get_uniqid(smiley);
 
 	/* TODO: add to the table, when the smiley sets the path */
@@ -191,8 +260,7 @@
 }
 
 void
-purple_smiley_list_remove(PurpleSmileyList *list, PurpleSmiley *smiley)
-{
+purple_smiley_list_remove(PurpleSmileyList *list, PurpleSmiley *smiley) {
 	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
 	GList *list_elem, *it;
 	const gchar *shortcut, *path;
@@ -239,8 +307,7 @@
 }
 
 gboolean
-purple_smiley_list_is_empty(PurpleSmileyList *list)
-{
+purple_smiley_list_is_empty(const PurpleSmileyList *list) {
 	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
 
 	g_return_val_if_fail(priv != NULL, TRUE);
@@ -250,7 +317,7 @@
 
 PurpleSmiley *
 purple_smiley_list_get_by_shortcut(PurpleSmileyList *list,
-	const gchar *shortcut)
+	                               const gchar *shortcut)
 {
 	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
 
@@ -260,8 +327,7 @@
 }
 
 PurpleTrie *
-purple_smiley_list_get_trie(PurpleSmileyList *list)
-{
+purple_smiley_list_get_trie(PurpleSmileyList *list) {
 	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
 
 	g_return_val_if_fail(priv != NULL, NULL);
@@ -270,8 +336,7 @@
 }
 
 GList *
-purple_smiley_list_get_unique(PurpleSmileyList *list)
-{
+purple_smiley_list_get_unique(PurpleSmileyList *list) {
 	GList *unique = NULL, *it;
 	GHashTable *unique_map;
 	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
@@ -300,134 +365,12 @@
 }
 
 GList *
-purple_smiley_list_get_all(PurpleSmileyList *list)
-{
-	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
+purple_smiley_list_get_all(PurpleSmileyList *list) {
+	PurpleSmileyListPrivate *priv = NULL;
 
-	g_return_val_if_fail(priv != NULL, NULL);
+	g_return_val_if_fail(PURPLE_IS_SMILEY_LIST(list), NULL);
+
+	priv = PURPLE_SMILEY_LIST_GET_PRIVATE(list);
 
 	return g_hash_table_get_values(priv->shortcut_map);
 }
-
-
-/*******************************************************************************
- * Object stuff
- ******************************************************************************/
-
-static void
-purple_smiley_list_init(GTypeInstance *instance, gpointer klass)
-{
-	PurpleSmileyList *sl = PURPLE_SMILEY_LIST(instance);
-	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
-
-	priv->trie = purple_trie_new();
-	priv->path_map = g_hash_table_new_full(g_str_hash, g_str_equal,
-		g_free, NULL);
-	priv->shortcut_map = g_hash_table_new_full(g_str_hash, g_str_equal,
-		g_free, NULL);
-
-	PURPLE_DBUS_REGISTER_POINTER(sl, PurpleSmileyList);
-}
-
-static void
-purple_smiley_list_finalize(GObject *obj)
-{
-	PurpleSmileyList *sl = PURPLE_SMILEY_LIST(obj);
-	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
-	GList *it;
-
-	g_object_unref(priv->trie);
-	g_hash_table_destroy(priv->path_map);
-	g_hash_table_destroy(priv->shortcut_map);
-
-	for (it = priv->smileys; it; it = g_list_next(it)) {
-		PurpleSmiley *smiley = it->data;
-		g_object_set_data(G_OBJECT(smiley), "purple-smiley-list", NULL);
-		g_object_set_data(G_OBJECT(smiley), "purple-smiley-list-elem", NULL);
-		g_object_unref(smiley);
-	}
-	g_list_free(priv->smileys);
-
-	PURPLE_DBUS_UNREGISTER_POINTER(sl);
-
-	G_OBJECT_CLASS(parent_class)->finalize(obj);
-}
-
-static void
-purple_smiley_list_get_property(GObject *object, guint par_id, GValue *value,
-	GParamSpec *pspec)
-{
-	PurpleSmileyList *sl = PURPLE_SMILEY_LIST(object);
-	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
-
-	switch (par_id) {
-		case PROP_DROP_FAILED_REMOTES:
-			g_value_set_boolean(value, priv->drop_failed_remotes);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_smiley_list_set_property(GObject *object, guint par_id, const GValue *value,
-	GParamSpec *pspec)
-{
-	PurpleSmileyList *sl = PURPLE_SMILEY_LIST(object);
-	PurpleSmileyListPrivate *priv = PURPLE_SMILEY_LIST_GET_PRIVATE(sl);
-
-	switch (par_id) {
-		case PROP_DROP_FAILED_REMOTES:
-			priv->drop_failed_remotes = g_value_get_boolean(value);
-			/* XXX: we could scan for remote smiley's on our list
-			 * and change the setting, but we don't care that much.
-			 */
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_smiley_list_class_init(PurpleSmileyListClass *klass)
-{
-	GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
-
-	parent_class = g_type_class_peek_parent(klass);
-
-	g_type_class_add_private(klass, sizeof(PurpleSmileyListPrivate));
-
-	gobj_class->get_property = purple_smiley_list_get_property;
-	gobj_class->set_property = purple_smiley_list_set_property;
-	gobj_class->finalize = purple_smiley_list_finalize;
-
-	properties[PROP_DROP_FAILED_REMOTES] = g_param_spec_boolean(
-		"drop-failed-remotes", "Drop failed PurpleRemoteSmileys",
-		"Watch added remote smileys and remove them from the list, "
-		"if they change their state to failed", FALSE,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties(gobj_class, PROP_LAST, properties);
-}
-
-GType
-purple_smiley_list_get_type(void)
-{
-	static GType type = 0;
-
-	if (G_UNLIKELY(type == 0)) {
-		static const GTypeInfo info = {
-			.class_size = sizeof(PurpleSmileyListClass),
-			.class_init = (GClassInitFunc)purple_smiley_list_class_init,
-			.instance_size = sizeof(PurpleSmileyList),
-			.instance_init = purple_smiley_list_init,
-		};
-
-		type = g_type_register_static(G_TYPE_OBJECT,
-			"PurpleSmileyList", &info, 0);
-	}
-
-	return type;
-}
--- a/libpurple/smiley-list.h	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/smiley-list.h	Sun May 28 13:26:27 2017 +0300
@@ -19,8 +19,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
  */
 
-#ifndef _PURPLE_SMILEY_LIST_H_
-#define _PURPLE_SMILEY_LIST_H_
+#ifndef PURPLE_SMILEY_LIST_H
+#define PURPLE_SMILEY_LIST_H
+
 /**
  * SECTION:smiley-list
  * @include:smiley-list.h
@@ -42,9 +43,9 @@
 typedef struct _PurpleSmileyListClass PurpleSmileyListClass;
 
 #define PURPLE_TYPE_SMILEY_LIST            (purple_smiley_list_get_type())
-#define PURPLE_SMILEY_LIST(smiley)         (G_TYPE_CHECK_INSTANCE_CAST((smiley), PURPLE_TYPE_SMILEY_LIST, PurpleSmileyList))
+#define PURPLE_SMILEY_LIST(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_SMILEY_LIST, PurpleSmileyList))
 #define PURPLE_SMILEY_LIST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SMILEY_LIST, PurpleSmileyListClass))
-#define PURPLE_IS_SMILEY_LIST(smiley)      (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PURPLE_TYPE_SMILEY_LIST))
+#define PURPLE_IS_SMILEY_LIST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_SMILEY_LIST))
 #define PURPLE_IS_SMILEY_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SMILEY_LIST))
 #define PURPLE_SMILEY_LIST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SMILEY_LIST, PurpleSmileyListClass))
 
@@ -53,19 +54,12 @@
  *
  * A container for #PurpleSmiley's.
  */
-struct _PurpleSmileyList
-{
+struct _PurpleSmileyList {
 	/*< private >*/
 	GObject parent;
 };
 
-/**
- * PurpleSmileyListClass:
- *
- * Base class for #PurpleSmileyList objects.
- */
-struct _PurpleSmileyListClass
-{
+struct _PurpleSmileyListClass {
 	/*< private >*/
 	GObjectClass parent_class;
 
@@ -82,8 +76,7 @@
  *
  * Returns: the #GType for a smiley list.
  */
-GType
-purple_smiley_list_get_type(void);
+GType purple_smiley_list_get_type(void);
 
 /**
  * purple_smiley_list_new:
@@ -96,8 +89,7 @@
  *
  * Returns: newly created #PurpleSmileyList.
  */
-PurpleSmileyList *
-purple_smiley_list_new(void);
+PurpleSmileyList *purple_smiley_list_new(void);
 
 /**
  * purple_smiley_list_add:
@@ -112,8 +104,7 @@
  *
  * Returns: %TRUE if the @smiley was successfully added to the list.
  */
-gboolean
-purple_smiley_list_add(PurpleSmileyList *list, PurpleSmiley *smiley);
+gboolean purple_smiley_list_add(PurpleSmileyList *list, PurpleSmiley *smiley);
 
 /**
  * purple_smiley_list_remove:
@@ -123,8 +114,7 @@
  * Removes a @smiley from the @list. If @smiley's reference count drops to zero,
  * it's destroyed.
  */
-void
-purple_smiley_list_remove(PurpleSmileyList *list, PurpleSmiley *smiley);
+void purple_smiley_list_remove(PurpleSmileyList *list, PurpleSmiley *smiley);
 
 /**
  * purple_smiley_list_is_empty:
@@ -134,8 +124,7 @@
  *
  * Returns: %TRUE if the @list is empty, %FALSE otherwise.
  */
-gboolean
-purple_smiley_list_is_empty(PurpleSmileyList *list);
+gboolean purple_smiley_list_is_empty(const PurpleSmileyList *list);
 
 /**
  * purple_smiley_list_get_by_shortcut:
@@ -146,9 +135,7 @@
  *
  * Returns: a #PurpleSmiley if the smiley was found, %NULL otherwise.
  */
-PurpleSmiley *
-purple_smiley_list_get_by_shortcut(PurpleSmileyList *list,
-	const gchar *shortcut);
+PurpleSmiley *purple_smiley_list_get_by_shortcut(PurpleSmileyList *list, const gchar *shortcut);
 
 /**
  * purple_smiley_list_get_trie:
@@ -161,8 +148,7 @@
  *
  * Returns: (transfer none): a #PurpleTrie for contained smileys.
  */
-PurpleTrie *
-purple_smiley_list_get_trie(PurpleSmileyList *list);
+PurpleTrie *purple_smiley_list_get_trie(PurpleSmileyList *list);
 
 /**
  * purple_smiley_list_get_unique:
@@ -173,8 +159,7 @@
  * Returns: (transfer container): the #GList of unique smileys. Use #g_list_free
  *          when done using it.
  */
-GList *
-purple_smiley_list_get_unique(PurpleSmileyList *list_);
+GList *purple_smiley_list_get_unique(PurpleSmileyList *list_);
 
 /**
  * purple_smiley_list_get_all:
@@ -185,9 +170,8 @@
  * Returns: (transfer container): the #GList of smileys. Use #g_list_free
  *          when done using it.
  */
-GList *
-purple_smiley_list_get_all(PurpleSmileyList *list_);
+GList *purple_smiley_list_get_all(PurpleSmileyList *list_);
 
 G_END_DECLS
 
-#endif /* _PURPLE_SMILEY_H_ */
+#endif /* PURPLE_SMILEY_LIST_H */
--- a/libpurple/smiley.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/smiley.c	Sun May 28 13:26:27 2017 +0300
@@ -19,20 +19,14 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#include "internal.h"
-#include "glibcompat.h"
+#include "smiley.h"
 #include "dbus-maybe.h"
-#include "debug.h"
-#include "smiley.h"
-#include "util.h"
-#include "xmlnode.h"
 
 #define PURPLE_SMILEY_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_SMILEY, PurpleSmileyPrivate))
 
 typedef struct {
 	gchar *shortcut;
-	PurpleImage *image;
 } PurpleSmileyPrivate;
 
 enum
@@ -42,175 +36,169 @@
 	PROP_LAST
 };
 
-static GObjectClass *parent_class;
 static GParamSpec *properties[PROP_LAST];
 
 /*******************************************************************************
- * API implementation
+ * Helpers
+ ******************************************************************************/
+static void
+_purple_smiley_set_shortcut(PurpleSmiley *smiley, const gchar *shortcut) {
+	PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
+
+	g_free(priv->shortcut);
+
+	priv->shortcut = g_strdup(shortcut);
+
+	g_object_notify(G_OBJECT(smiley), "shortcut");
+}
+
+/*******************************************************************************
+ * Object stuff
  ******************************************************************************/
+G_DEFINE_TYPE_WITH_PRIVATE(PurpleSmiley, purple_smiley, PURPLE_TYPE_IMAGE);
 
+static void
+purple_smiley_init(PurpleSmiley *smiley) {
+	PURPLE_DBUS_REGISTER_POINTER(smiley, PurpleSmiley);
+}
+
+static void
+purple_smiley_finalize(GObject *obj) {
+	PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(obj);
+
+	g_free(priv->shortcut);
+
+	PURPLE_DBUS_UNREGISTER_POINTER(smiley);
+
+	G_OBJECT_CLASS(purple_smiley_parent_class)->finalize(obj);
+}
+
+static void
+purple_smiley_get_property(GObject *obj, guint param_id, GValue *value,
+                           GParamSpec *pspec)
+{
+	PurpleSmiley *smiley = PURPLE_SMILEY(obj);
+
+	switch (param_id) {
+		case PROP_SHORTCUT:
+			g_value_set_string(value, purple_smiley_get_shortcut(smiley));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_smiley_set_property(GObject *obj, guint param_id, const GValue *value,
+                           GParamSpec *pspec)
+{
+	PurpleSmiley *smiley = PURPLE_SMILEY(obj);
+
+	switch (param_id) {
+		case PROP_SHORTCUT:
+			_purple_smiley_set_shortcut(smiley, g_value_get_string(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_smiley_class_init(PurpleSmileyClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->get_property = purple_smiley_get_property;
+	obj_class->set_property = purple_smiley_set_property;
+	obj_class->finalize = purple_smiley_finalize;
+
+	properties[PROP_SHORTCUT] = g_param_spec_string(
+		"shortcut",
+		"Shortcut",
+		"A non-escaped textual representation of a smiley.",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
+	);
+
+	g_object_class_install_properties(obj_class, PROP_LAST, properties);
+}
+
+/*******************************************************************************
+ * API
+ ******************************************************************************/
 PurpleSmiley *
 purple_smiley_new(const gchar *shortcut, const gchar *path)
 {
-	PurpleSmiley *smiley;
-	PurpleSmileyPrivate *priv;
+	PurpleSmiley *smiley = NULL;
+	GBytes *bytes = NULL;
+	gchar *contents = NULL;
+	gsize length = 0;
 
 	g_return_val_if_fail(shortcut != NULL, NULL);
 	g_return_val_if_fail(path != NULL, NULL);
 
-	smiley = g_object_new(PURPLE_TYPE_SMILEY,
-		"shortcut", shortcut,
-		NULL);
-	priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
-
-	priv->image = purple_image_new_from_file(path, FALSE);
-	if (!priv->image) {
-		purple_debug_error("smiley", "Couldn't load smiley data ");
-		g_object_unref(smiley);
+	if(!g_file_get_contents(path, &contents, &length, NULL)) {
 		return NULL;
 	}
 
+	bytes = g_bytes_new_take(contents, length);
+
+	smiley = g_object_new(
+		PURPLE_TYPE_SMILEY,
+		"contents", bytes,
+		"shortcut", shortcut,
+		NULL
+	);
+
+	g_bytes_unref(bytes);
+
 	return smiley;
 }
 
 PurpleSmiley *
-purple_smiley_new_remote(const gchar *shortcut)
+purple_smiley_new_from_data(const gchar *shortcut,
+                            const guint8 *data,
+                            gsize length)
 {
-	PurpleSmiley *smiley;
-	PurpleSmileyPrivate *priv;
+	PurpleSmiley *smiley = NULL;
+	GBytes *bytes = NULL;
 
 	g_return_val_if_fail(shortcut != NULL, NULL);
 
-	smiley = g_object_new(PURPLE_TYPE_SMILEY,
+	bytes = g_bytes_new(data, length);
+
+	smiley = g_object_new(
+		PURPLE_TYPE_SMILEY,
 		"shortcut", shortcut,
-		NULL);
-	priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
+		"contents", bytes,
+		NULL
+	);
 
-	priv->image = purple_image_transfer_new();
+	g_bytes_unref(bytes);
 
 	return smiley;
+
+}
+
+PurpleSmiley *
+purple_smiley_new_remote(const gchar *shortcut) {
+	g_return_val_if_fail(shortcut != NULL, NULL);
+
+	return g_object_new(
+		PURPLE_TYPE_SMILEY,
+		"shortcut", shortcut,
+		NULL
+	);
 }
 
 const gchar *
-purple_smiley_get_shortcut(const PurpleSmiley *smiley)
-{
-	PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
+purple_smiley_get_shortcut(const PurpleSmiley *smiley) {
+	PurpleSmileyPrivate *priv = NULL;
 
-	g_return_val_if_fail(priv != NULL, NULL);
+	g_return_val_if_fail(PURPLE_IS_SMILEY(smiley), NULL);
+
+	priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
 
 	return priv->shortcut;
 }
-
-PurpleImage *
-purple_smiley_get_image(PurpleSmiley *smiley)
-{
-	PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
-
-	g_return_val_if_fail(priv != NULL, NULL);
-	g_return_val_if_fail(priv->image != NULL, NULL);
-
-	return priv->image;
-}
-
-
-/*******************************************************************************
- * Object stuff
- ******************************************************************************/
-
-static void
-purple_smiley_init(GTypeInstance *instance, gpointer klass)
-{
-	PurpleSmiley *smiley = PURPLE_SMILEY(instance);
-	PURPLE_DBUS_REGISTER_POINTER(smiley, PurpleSmiley);
-}
-
-static void
-purple_smiley_finalize(GObject *obj)
-{
-	PurpleSmiley *smiley = PURPLE_SMILEY(obj);
-	PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
-
-	g_free(priv->shortcut);
-
-	if (priv->image)
-		g_object_unref(priv->image);
-
-	PURPLE_DBUS_UNREGISTER_POINTER(smiley);
-
-	G_OBJECT_CLASS(parent_class)->finalize(obj);
-}
-
-static void
-purple_smiley_get_property(GObject *object, guint par_id, GValue *value,
-	GParamSpec *pspec)
-{
-	PurpleSmiley *smiley = PURPLE_SMILEY(object);
-	PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
-
-	switch (par_id) {
-		case PROP_SHORTCUT:
-			g_value_set_string(value, priv->shortcut);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_smiley_set_property(GObject *object, guint par_id, const GValue *value,
-	GParamSpec *pspec)
-{
-	PurpleSmiley *smiley = PURPLE_SMILEY(object);
-	PurpleSmileyPrivate *priv = PURPLE_SMILEY_GET_PRIVATE(smiley);
-
-	switch (par_id) {
-		case PROP_SHORTCUT:
-			g_free(priv->shortcut);
-			priv->shortcut = g_strdup(g_value_get_string(value));
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, par_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_smiley_class_init(PurpleSmileyClass *klass)
-{
-	GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
-
-	parent_class = g_type_class_peek_parent(klass);
-
-	g_type_class_add_private(klass, sizeof(PurpleSmileyPrivate));
-
-	gobj_class->get_property = purple_smiley_get_property;
-	gobj_class->set_property = purple_smiley_set_property;
-	gobj_class->finalize = purple_smiley_finalize;
-
-	properties[PROP_SHORTCUT] = g_param_spec_string("shortcut", "Shortcut",
-		"A non-escaped textual representation of a smiley.", NULL,
-		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties(gobj_class, PROP_LAST, properties);
-}
-
-GType
-purple_smiley_get_type(void)
-{
-	static GType type = 0;
-
-	if (G_UNLIKELY(type == 0)) {
-		static const GTypeInfo info = {
-			.class_size = sizeof(PurpleSmileyClass),
-			.class_init = (GClassInitFunc)purple_smiley_class_init,
-			.instance_size = sizeof(PurpleSmiley),
-			.instance_init = purple_smiley_init,
-		};
-
-		type = g_type_register_static(G_TYPE_OBJECT,
-			"PurpleSmiley", &info, 0);
-	}
-
-	return type;
-}
--- a/libpurple/smiley.h	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/smiley.h	Sun May 28 13:26:27 2017 +0300
@@ -19,8 +19,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
  */
 
-#ifndef _PURPLE_SMILEY_H_
-#define _PURPLE_SMILEY_H_
+#ifndef PURPLE_SMILEY_H
+#define PURPLE_SMILEY_H
+
 /**
  * SECTION:smiley
  * @include:smiley.h
@@ -45,9 +46,9 @@
 typedef struct _PurpleSmileyClass PurpleSmileyClass;
 
 #define PURPLE_TYPE_SMILEY            (purple_smiley_get_type())
-#define PURPLE_SMILEY(smiley)         (G_TYPE_CHECK_INSTANCE_CAST((smiley), PURPLE_TYPE_SMILEY, PurpleSmiley))
+#define PURPLE_SMILEY(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_SMILEY, PurpleSmiley))
 #define PURPLE_SMILEY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
-#define PURPLE_IS_SMILEY(smiley)      (G_TYPE_CHECK_INSTANCE_TYPE((smiley), PURPLE_TYPE_SMILEY))
+#define PURPLE_IS_SMILEY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_SMILEY))
 #define PURPLE_IS_SMILEY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_SMILEY))
 #define PURPLE_SMILEY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_SMILEY, PurpleSmileyClass))
 
@@ -56,10 +57,9 @@
  *
  * A generic smiley. It can either be a theme smiley, or a custom smiley.
  */
-struct _PurpleSmiley
-{
+struct _PurpleSmiley {
 	/*< private >*/
-	GObject parent;
+	PurpleImage parent;
 };
 
 /**
@@ -67,10 +67,9 @@
  *
  * Base class for #PurpleSmiley objects.
  */
-struct _PurpleSmileyClass
-{
+struct _PurpleSmileyClass {
 	/*< private >*/
-	GObjectClass parent_class;
+	PurpleImageClass parent_class;
 
 	/*< private >*/
 	void (*purple_reserved1)(void);
@@ -86,8 +85,7 @@
  *
  * Returns: the #GType for a smiley.
  */
-GType
-purple_smiley_get_type(void);
+GType purple_smiley_get_type(void);
 
 /**
  * purple_smiley_new:
@@ -99,8 +97,19 @@
  *
  * Returns: the new #PurpleSmiley.
  */
-PurpleSmiley *
-purple_smiley_new(const gchar *shortcut, const gchar *path);
+PurpleSmiley *purple_smiley_new(const gchar *shortcut, const gchar *path);
+
+/**
+ * purple_smiley_new_from_data:
+ * @shortcut: The smiley shortcut (unescaped).
+ * @data: The raw data of the image.
+ * @length: The length of @data in bytes.
+ *
+ * Creates new smiley from @data.
+ *
+ * Returns: A new #PurpleSmiley.
+ */
+PurpleSmiley *purple_smiley_new_from_data(const gchar *shortcut, const guint8 *data, gsize length);
 
 /**
  * purple_smiley_new_remote:
@@ -112,8 +121,7 @@
  *
  * Returns: the new remote #PurpleSmiley.
  */
-PurpleSmiley *
-purple_smiley_new_remote(const gchar *shortcut);
+PurpleSmiley *purple_smiley_new_remote(const gchar *shortcut);
 
 /**
  * purple_smiley_get_shortcut:
@@ -124,23 +132,8 @@
  *
  * Returns: the unescaped shortcut.
  */
-const gchar *
-purple_smiley_get_shortcut(const PurpleSmiley *smiley);
-
-/**
- * purple_smiley_get_image:
- * @smiley: the smiley.
- *
- * Returns the image contents for a @smiley. It may not be ready for remote
- * smileys, so check it with #purple_image_is_ready.
- *
- * If you want to save it, increase a ref count for the returned object.
- *
- * Returns: (transfer none): the image contents for a @smiley.
- */
-PurpleImage *
-purple_smiley_get_image(PurpleSmiley *smiley);
+const gchar *purple_smiley_get_shortcut(const PurpleSmiley *smiley);
 
 G_END_DECLS
 
-#endif /* _PURPLE_SMILEY_H_ */
+#endif /* PURPLE_SMILEY_H */
--- a/libpurple/tests/.hgignore	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/tests/.hgignore	Sun May 28 13:26:27 2017 +0300
@@ -3,6 +3,8 @@
 ^test_sha(1|256)$
 ^test_des3?$
 ^test_hmac$
+^test_image$
+^test_smiley$
 ^test_trie$
 ^test_util$
 ^test_xmlnode$
--- a/libpurple/tests/Makefile.am	Thu Mar 23 19:11:37 2017 +0300
+++ b/libpurple/tests/Makefile.am	Sun May 28 13:26:27 2017 +0300
@@ -9,15 +9,16 @@
 	test_des \
 	test_des3 \
 	test_hmac \
+	test_image \
 	test_md4 \
 	test_md5 \
 	test_sha1 \
 	test_sha256 \
+	test_smiley \
 	test_trie \
 	test_util \
 	test_xmlnode
 
-
 test_des_SOURCES=test_des.c
 test_des_LDADD=$(COMMON_LIBS)
 
@@ -27,6 +28,9 @@
 test_hmac_SOURCES=test_hmac.c
 test_hmac_LDADD=$(COMMON_LIBS)
 
+test_image_SOURCES=test_image.c
+test_image_LDADD=$(COMMON_LIBS)
+
 test_md4_SOURCES=test_md4.c
 test_md4_LDADD=$(COMMON_LIBS)
 
@@ -39,6 +43,9 @@
 test_sha256_SOURCES=test_sha256.c
 test_sha256_LDADD=$(COMMON_LIBS)
 
+test_smiley_SOURCES=test_smiley.c
+test_smiley_LDADD=$(COMMON_LIBS)
+
 test_trie_SOURCES=test_trie.c
 test_trie_LDADD=$(COMMON_LIBS)
 
@@ -51,9 +58,13 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/libpurple \
 	-I$(top_builddir)/libpurple \
+	-DTEST_DATA_DIR=\"$(srcdir)/data\" \
 	$(DEBUG_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(GPLUGIN_CFLAGS) \
 	$(PLUGIN_CFLAGS) \
 	$(DBUS_CFLAGS) \
 	$(NSS_CFLAGS)
+
+EXTRA_DIST = \
+	data/test-image.png
Binary file libpurple/tests/data/test-image.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_image.c	Sun May 28 13:26:27 2017 +0300
@@ -0,0 +1,132 @@
+/*
+ * 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
+ *
+ * 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 <glib.h>
+
+#include <purple.h>
+
+// generated via:
+// $ cat test-image.png | hexdump -v -e '1 1 "0x%02x," " "' | xargs -n 8 echo
+static const gsize test_image_data_len = 160;
+static const guint8 test_image_data[] = {
+	0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
+	0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
+	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+	0x08, 0x02, 0x00, 0x00, 0x00, 0xfd, 0xd4, 0x9a,
+	0x73, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
+	0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b,
+	0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00,
+	0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe0,
+	0x0a, 0x02, 0x16, 0x30, 0x22, 0x28, 0xa4, 0xc9,
+	0xdd, 0x00, 0x00, 0x00, 0x1d, 0x69, 0x54, 0x58,
+	0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65,
+	0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74,
+	0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x64, 0x2e,
+	0x65, 0x07, 0x00, 0x00, 0x00, 0x16, 0x49, 0x44,
+	0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff,
+	0x3f, 0x03, 0x03, 0x03, 0xe3, 0xb3, 0x4c, 0xb5,
+	0x9b, 0x4e, 0x0b, 0x00, 0x2f, 0xa9, 0x06, 0x2f,
+	0x8a, 0xd1, 0xc6, 0xb3, 0x00, 0x00, 0x00, 0x00,
+	0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+_test_image(PurpleImage *image,
+            const guint8 *edata,
+            gsize elen,
+            const gchar *ext,
+            const gchar *mimetype)
+{
+	GBytes *bytes = NULL;
+	const guint8 *adata = NULL;
+	gsize alen;
+
+	g_assert(PURPLE_IS_IMAGE(image));
+
+	bytes = purple_image_get_contents(image);
+	adata = g_bytes_get_data(bytes, &alen);
+	g_assert_cmpmem(adata, alen, edata, elen);
+	g_bytes_unref(bytes);
+
+	g_assert_cmpstr(purple_image_get_extension(image), ==, ext);
+	g_assert_cmpstr(purple_image_get_mimetype(image), ==, mimetype);
+
+	g_object_unref(G_OBJECT(image));
+}
+
+static void
+test_image_new_from_data(void) {
+	PurpleImage *image = purple_image_new_from_data(
+		test_image_data,
+		test_image_data_len
+	);
+
+	_test_image(
+		image,
+		test_image_data,
+		test_image_data_len,
+		"png",
+		"image/png"
+	);
+}
+
+static void
+test_image_new_from_file(void) {
+	PurpleImage *image = NULL;
+	GError *error = NULL;
+	gchar *path = NULL;
+	gchar *edata = NULL;
+	gsize elen;
+
+	path = g_build_filename(TEST_DATA_DIR, "test-image.png", NULL);
+	image = purple_image_new_from_file(path, &error);
+	g_assert_no_error(error);
+
+	g_file_get_contents(path, &edata, &elen, &error);
+	g_free(path);
+	g_assert_no_error(error);
+
+	_test_image(
+		image,
+		(guint8 *)edata,
+		elen,
+		"png",
+		"image/png"
+	);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar **argv) {
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/image/new-from-data", test_image_new_from_data);
+	g_test_add_func("/image/new-from-file", test_image_new_from_file);
+
+	return g_test_run();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_smiley.c	Sun May 28 13:26:27 2017 +0300
@@ -0,0 +1,145 @@
+/*
+ * 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
+ *
+ * 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 <glib.h>
+
+#include <purple.h>
+
+// generated via:
+// $ cat test-image.png | hexdump -v -e '1 1 "0x%02x," " "' | xargs -n 8 echo
+static const gsize test_image_data_len = 160;
+static const guint8 test_image_data[] = {
+	0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
+	0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
+	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+	0x08, 0x02, 0x00, 0x00, 0x00, 0xfd, 0xd4, 0x9a,
+	0x73, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
+	0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b,
+	0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00,
+	0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe0,
+	0x0a, 0x02, 0x16, 0x30, 0x22, 0x28, 0xa4, 0xc9,
+	0xdd, 0x00, 0x00, 0x00, 0x1d, 0x69, 0x54, 0x58,
+	0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65,
+	0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74,
+	0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x64, 0x2e,
+	0x65, 0x07, 0x00, 0x00, 0x00, 0x16, 0x49, 0x44,
+	0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff,
+	0x3f, 0x03, 0x03, 0x03, 0xe3, 0xb3, 0x4c, 0xb5,
+	0x9b, 0x4e, 0x0b, 0x00, 0x2f, 0xa9, 0x06, 0x2f,
+	0x8a, 0xd1, 0xc6, 0xb3, 0x00, 0x00, 0x00, 0x00,
+	0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+_test_smiley(PurpleSmiley *smiley,
+             const guint8 *edata,
+             gsize elen,
+             const gchar *ext,
+             const gchar *mimetype,
+             const gchar *shortcut)
+{
+	GBytes *bytes = NULL;
+	const guint8 *adata = NULL;
+	gsize alen;
+
+	g_assert(PURPLE_IS_SMILEY(PURPLE_IMAGE(smiley)));
+
+	bytes = purple_image_get_contents(PURPLE_IMAGE(smiley));
+	adata = g_bytes_get_data(bytes, &alen);
+	g_assert_cmpmem(adata, alen, edata, elen);
+	g_bytes_unref(bytes);
+
+	g_assert_cmpstr(
+		purple_image_get_extension(PURPLE_IMAGE(smiley)),
+		==,
+		ext
+	);
+	g_assert_cmpstr(
+		purple_image_get_mimetype(PURPLE_IMAGE(smiley)),
+		==,
+		mimetype
+	);
+	g_assert_cmpstr(purple_smiley_get_shortcut(smiley), ==, shortcut);
+
+	g_object_unref(G_OBJECT(smiley));
+}
+
+static void
+test_smiley_new_from_data(void) {
+	PurpleSmiley *smiley = purple_smiley_new_from_data(
+		":-X",
+		test_image_data,
+		test_image_data_len
+	);
+
+	_test_smiley(
+		smiley,
+		test_image_data,
+		test_image_data_len,
+		"png",
+		"image/png",
+		":-X"
+	);
+}
+
+static void
+test_smiley_new_from_file(void) {
+	PurpleSmiley *smiley = NULL;
+	GError *error = NULL;
+	gchar *path = NULL;
+	gchar *edata = NULL;
+	gsize elen;
+
+	path = g_build_filename(TEST_DATA_DIR, "test-image.png", NULL);
+	smiley = purple_smiley_new("^_^", path);
+	g_assert_no_error(error);
+
+	g_file_get_contents(path, &edata, &elen, &error);
+	g_free(path);
+	g_assert_no_error(error);
+
+	_test_smiley(
+		smiley,
+		(guint8 *)edata,
+		elen,
+		"png",
+		"image/png",
+		"^_^"
+	);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar **argv) {
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_func("/smiley/new-from-data", test_smiley_new_from_data);
+	g_test_add_func("/smiley/new-from-file", test_smiley_new_from_file);
+
+	return g_test_run();
+}
--- a/pidgin/gtkaccount.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/gtkaccount.c	Sun May 28 13:26:27 2017 +0300
@@ -183,7 +183,7 @@
 	}
 
 	if (new_icon_path != NULL) {
-		dialog->icon_img = purple_image_new_from_file(new_icon_path, TRUE);
+		dialog->icon_img = purple_image_new_from_file(new_icon_path, NULL);
 		purple_debug_warning("gtkaccount", "data was not necessary");
 		g_free(data);
 	} else if (data != NULL) {
@@ -735,7 +735,7 @@
 		img = purple_buddy_icons_find_account_icon(dialog->account);
 		if (img)
 		{
-			len = purple_image_get_size(img);
+			len = purple_image_get_data_size(img);
 			data = g_memdup(purple_image_get_data(img), len);
 		}
 		set_dialog_icon(dialog, data, len,
@@ -1374,7 +1374,7 @@
 		{
 			if (dialog->icon_img)
 			{
-				size_t len = purple_image_get_size(dialog->icon_img);
+				size_t len = purple_image_get_data_size(dialog->icon_img);
 				purple_buddy_icons_set_account_icon(account,
 					g_memdup(purple_image_get_data(dialog->icon_img), len), len);
 				purple_account_set_buddy_icon_path(account,
@@ -2133,7 +2133,7 @@
 				const char *path;
 				path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
 				if ((path != NULL) && (*path != '\0')) {
-					img = purple_image_new_from_file(path, TRUE);
+					img = purple_image_new_from_file(path, NULL);
 				}
 			}
 		} else {
--- a/pidgin/gtkblist.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/gtkblist.c	Sun May 28 13:26:27 2017 +0300
@@ -2692,7 +2692,7 @@
 
 	if (custom_img) {
 		data = purple_image_get_data(custom_img);
-		len = purple_image_get_size(custom_img);
+		len = purple_image_get_data_size(custom_img);
 	}
 
 	if (data == NULL) {
@@ -2715,7 +2715,7 @@
 			account ? purple_account_get_username(account) : "(no account)",
 			account ? purple_account_get_protocol_id(account) : "(no account)",
 			buddy ? purple_buddy_get_name(buddy) : "(no buddy)",
-			custom_img ? purple_image_get_size(custom_img) : 0);
+			custom_img ? purple_image_get_data_size(custom_img) : 0);
 		if (custom_img)
 			g_object_unref(custom_img);
 		return NULL;
--- a/pidgin/gtkconv.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/gtkconv.c	Sun May 28 13:26:27 2017 +0300
@@ -4181,7 +4181,7 @@
 	g_snprintf(filename, sizeof(filename), "%s.png", stock_name);
 	path = g_build_filename(PURPLE_DATADIR, "pixmaps", "pidgin",
 		"e2ee", "16", filename, NULL);
-	image = purple_image_new_from_file(path, FALSE);
+	image = purple_image_new_from_file(path, NULL);
 	g_free(path);
 
 	g_hash_table_insert(e2ee_stock, g_strdup(stock_name), image);
@@ -6536,14 +6536,12 @@
 pidgin_conv_write_smiley(GString *out, PurpleSmiley *smiley,
 	PurpleConversation *conv, gpointer _proto_name)
 {
-	PurpleImage *image;
 	gchar *escaped_shortcut;
 	gchar *uri;
 
 	escaped_shortcut = g_markup_escape_text(
 		purple_smiley_get_shortcut(smiley), -1);
-	image = purple_smiley_get_image(smiley);
-	uri = purple_image_store_get_uri(image);
+	uri = purple_image_store_get_uri(PURPLE_IMAGE(smiley));
 
 	g_string_append_printf(out,
 		"<img class=\"emoticon\" alt=\"%s\" title=\"%s\" "
@@ -6592,11 +6590,14 @@
 
 	full = g_match_info_fetch(info, 0);
 
+#warning fix this
+#if 0
 	if (purple_image_is_ready(image)) {
 		g_string_append(result, full);
 		g_free(full);
 		return FALSE;
 	}
+#endif
 
 	/* search for alt */
 	alt = strstr(full, "alt=\"");
@@ -7859,7 +7860,7 @@
 			if (custom_img) {
 				/* There is a custom icon for this user */
 				data = purple_image_get_data(custom_img);
-				len = purple_image_get_size(custom_img);
+				len = purple_image_get_data_size(custom_img);
 			}
 		}
 	}
--- a/pidgin/gtksmiley-manager.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/gtksmiley-manager.c	Sun May 28 13:26:27 2017 +0300
@@ -163,7 +163,7 @@
 	if (!filename)
 		return;
 
-	image = purple_image_new_from_file(filename, TRUE);
+	image = purple_image_new_from_file(filename, NULL);
 	if (!image)
 		return;
 
@@ -249,8 +249,7 @@
 	}
 
 	if (edit_dialog->new_image == NULL) {
-		edit_dialog->new_image =
-			purple_smiley_get_image(edit_dialog->smiley);
+		edit_dialog->new_image = PURPLE_IMAGE(edit_dialog->smiley);
 		g_return_if_fail(edit_dialog->new_image);
 	}
 
@@ -392,7 +391,7 @@
 
 	if (smiley) {
 		edit_dialog->filename = g_strdup(purple_image_get_path(
-			purple_smiley_get_image(smiley)));
+			PURPLE_IMAGE(smiley)));
 		gtk_entry_set_text(edit_dialog->shortcut,
 			purple_smiley_get_shortcut(smiley));
 	}
@@ -506,7 +505,7 @@
 			return;
 		}
 
-		image = purple_image_new_from_file(filename, TRUE);
+		image = purple_image_new_from_file(filename, NULL);
 		if (!image) {
 			purple_debug_warning("gtksmiley-manager",
 				"dropped file is not a valid image");
@@ -587,8 +586,7 @@
 	smiley_image = g_object_get_data(G_OBJECT(smiley),
 		"pidgin-smiley-manager-list-thumb");
 	if (smiley_image == NULL) {
-		smiley_image = pidgin_pixbuf_from_image(
-			purple_smiley_get_image(smiley));
+		smiley_image = pidgin_pixbuf_from_image(PURPLE_IMAGE(smiley));
 		smiley_image = pidgin_pixbuf_scale_down(smiley_image,
 			22, 22, GDK_INTERP_BILINEAR, TRUE);
 		g_object_set_data_full(G_OBJECT(smiley),
--- a/pidgin/gtkstatusbox.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/gtkstatusbox.c	Sun May 28 13:26:27 2017 +0300
@@ -412,7 +412,7 @@
 		PurpleImage *img = NULL;
 
 		if (filename && *filename)
-			img = purple_image_new_from_file(filename, TRUE);
+			img = purple_image_new_from_file(filename, NULL);
 
 		pidgin_status_box_set_buddy_icon(status_box, img);
 		if (img)
@@ -1450,7 +1450,7 @@
 
 		/* Even if no accounts were processed, load the icon that was set. */
 		if (filename != NULL)
-			img = purple_image_new_from_file(filename, TRUE);
+			img = purple_image_new_from_file(filename, NULL);
 	}
 
 	pidgin_status_box_set_buddy_icon(box, img);
@@ -2191,12 +2191,12 @@
 		g_signal_connect(G_OBJECT(loader), "size-prepared", G_CALLBACK(pixbuf_size_prepared_cb), NULL);
 		if (!gdk_pixbuf_loader_write(loader,
 				purple_image_get_data(status_box->buddy_icon_img),
-				purple_image_get_size(status_box->buddy_icon_img),
+				purple_image_get_data_size(status_box->buddy_icon_img),
 				&error) || error)
 		{
 			purple_debug_warning("gtkstatusbox",
 				"gdk_pixbuf_loader_write() failed with size=%"
-				G_GSIZE_FORMAT ": %s", purple_image_get_size(
+				G_GSIZE_FORMAT ": %s", purple_image_get_data_size(
 					status_box->buddy_icon_img),
 				error ? error->message : "(no error message)");
 			if (error)
@@ -2205,7 +2205,7 @@
 			purple_debug_warning("gtkstatusbox",
 				"gdk_pixbuf_loader_close() failed for image of "
 				"size %" G_GSIZE_FORMAT ": %s",
-				purple_image_get_size(status_box->buddy_icon_img),
+				purple_image_get_data_size(status_box->buddy_icon_img),
 				error ? error->message : "(no error message)");
 			if (error)
 				g_error_free(error);
--- a/pidgin/gtkutils.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/gtkutils.c	Sun May 28 13:26:27 2017 +0300
@@ -3077,7 +3077,7 @@
 pidgin_pixbuf_from_image(PurpleImage *image)
 {
 	return pidgin_pixbuf_from_data(purple_image_get_data(image),
-		purple_image_get_size(image));
+		purple_image_get_data_size(image));
 }
 
 GdkPixbuf *pidgin_pixbuf_new_from_file(const gchar *filename)
--- a/pidgin/gtkwebview.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/gtkwebview.c	Sun May 28 13:26:27 2017 +0300
@@ -206,7 +206,7 @@
 
 			b64 = purple_base64_encode(
 				purple_image_get_data(img),
-				purple_image_get_size(img));
+				purple_image_get_data_size(img));
 			type = purple_image_get_mimetype(img);
 			src = g_strdup_printf("data:%s;base64,%s", type, b64);
 			g_free(b64);
--- a/pidgin/gtkwebviewtoolbar.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/gtkwebviewtoolbar.c	Sun May 28 13:26:27 2017 +0300
@@ -134,9 +134,14 @@
 
 	if (strcmp(str, "inherit") == 0) {
 		return FALSE;
-	} else {
-		return gdk_rgba_parse(color, str);
 	}
+
+	if (!gdk_rgba_parse(color, str)) {
+		return FALSE;
+	}
+
+	/* FALSE for fully transparent color (same behavior as with "inherit") */
+	return color->alpha > 0;
 }
 
 static gchar*
@@ -562,7 +567,7 @@
 	if (filename == NULL)
 		return;
 
-	img = purple_image_new_from_file(filename, TRUE);
+	img = purple_image_new_from_file(filename, NULL);
 
 	if (!img) {
 		gchar *buf = g_strdup_printf(_("Failed to store image: %s"),
@@ -633,15 +638,13 @@
 insert_smiley_text(GtkWidget *widget, PidginWebViewToolbar *toolbar)
 {
 	PurpleSmiley *smiley;
-	PurpleImage *image;
 	guint image_id;
 	gchar *escaped_smiley, *smiley_html;
 	const gchar *smiley_class;
 
 	smiley = g_object_get_data(G_OBJECT(widget), "smiley");
 	smiley_class = g_object_get_data(G_OBJECT(widget), "smiley-class");
-	image = purple_smiley_get_image(smiley);
-	image_id = purple_image_store_add(image);
+	image_id = purple_image_store_add(PURPLE_IMAGE(smiley));
 
 	escaped_smiley = g_markup_escape_text(
 		purple_smiley_get_shortcut(smiley), -1);
@@ -694,8 +697,7 @@
 			continue;
 		}
 
-		pixbuf = pidgin_pixbuf_from_image(
-			purple_smiley_get_image(smiley));
+		pixbuf = pidgin_pixbuf_from_image(PURPLE_IMAGE(smiley));
 		pixbuf = pidgin_pixbuf_scale_down(pixbuf,
 			24, 24, GDK_INTERP_BILINEAR, TRUE);
 
--- a/pidgin/plugins/imgupload.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/plugins/imgupload.c	Sun May 28 13:26:27 2017 +0300
@@ -122,7 +122,7 @@
 
 	/* TODO: make it a plain, multipart/form-data request */
 	img_data = purple_base64_encode(purple_image_get_data(image),
-		purple_image_get_size(image));
+		purple_image_get_data_size(image));
 	img_data_e = g_uri_escape_string(img_data, NULL, FALSE);
 	g_free(img_data);
 	req_data = g_strdup_printf("type=base64&image=%s", img_data_e);
--- a/pidgin/plugins/screencap.c	Thu Mar 23 19:11:37 2017 +0300
+++ b/pidgin/plugins/screencap.c	Sun May 28 13:26:27 2017 +0300
@@ -119,11 +119,11 @@
 
 static gboolean
 scrncap_pixbuf_to_image_cb(const gchar *buf, gsize count, GError **error,
-	gpointer _image)
+	gpointer data)
 {
-	PurpleImage *image = PURPLE_IMAGE(_image);
+	PurpleImage *image = *(PurpleImage **)data;
 
-	purple_image_transfer_write(image, buf, count);
+	image = purple_image_new_from_data(buf, count);
 
 	return TRUE;
 }
@@ -131,16 +131,12 @@
 static PurpleImage *
 scrncap_pixbuf_to_image(GdkPixbuf *pixbuf)
 {
-	PurpleImage *image;
+	PurpleImage *image = NULL;
 	GError *error = NULL;
 
-	image = purple_image_transfer_new();
-
-	gdk_pixbuf_save_to_callback(pixbuf, scrncap_pixbuf_to_image_cb, image,
+	gdk_pixbuf_save_to_callback(pixbuf, scrncap_pixbuf_to_image_cb, &image,
 		"png", &error, NULL);
 
-	purple_image_transfer_close(image);
-
 	if (error != NULL) {
 		purple_debug_error("screencap", "Failed saving an image: %s",
 			error->message);
@@ -149,14 +145,8 @@
 		return NULL;
 	}
 
-	if (purple_image_is_ready(image)) {
-		if (purple_image_get_extension(image) == NULL) {
-			purple_debug_error("screencap", "Invalid image format");
-			g_object_unref(image);
-			return NULL;
-		}
-	} else {
-		purple_debug_error("screencap", "Image is not ready");
+	if (purple_image_get_extension(image) == NULL) {
+		purple_debug_error("screencap", "Invalid image format");
 		g_object_unref(image);
 		return NULL;
 	}

mercurial