changeset 24494:3667342896ce

merge of '2f409bcdde5ebbb1edf097aeb4b4366d7c71518d' and '52e5d841d66390cfc5a59e356e2a53b2e2b9df8a'
author John Bailey <rekkanoryo@rekkanoryo.org>
date Sun, 23 Nov 2008 07:31:20 +0000
parents 3f80f211417e (current diff) c83ee78ecbe7 (diff)
children 637aee10adcb 776da7ede774
files
diffstat 2 files changed, 300 insertions(+), 194 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/gg/gg.c	Sun Nov 23 04:12:43 2008 +0000
+++ b/libpurple/protocols/gg/gg.c	Sun Nov 23 07:31:20 2008 +0000
@@ -61,7 +61,6 @@
  *
  * @return Zero if proxy setup is valid, otherwise -1.
  */
-/* static int ggp_setup_proxy(PurpleAccount *account) {{{ */
 static int ggp_setup_proxy(PurpleAccount *account)
 {
 	PurpleProxyInfo *gpi;
@@ -88,11 +87,7 @@
 
 	return 0;
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_async_token_handler(gpointer _gc, gint fd, PurpleInputCondition cond) {{{ */
 static void ggp_async_token_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
@@ -157,11 +152,7 @@
 	token->cb = NULL;
 	cb(gc);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_token_request(PurpleConnection *gc, GGPTokenCallback cb) {{{ */
 static void ggp_token_request(PurpleConnection *gc, GGPTokenCallback cb)
 {
 	PurpleAccount *account;
@@ -199,7 +190,6 @@
  *
  * @param Current action handler.
  */
-/* static void ggp_action_buddylist_get(PurplePluginAction *action) {{{ */
 static void ggp_action_buddylist_get(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -209,14 +199,12 @@
 
 	gg_userlist_request(info->session, GG_USERLIST_GET, NULL);
 }
-/* }}} */
 
 /**
  * Upload the buddylist to the server.
  *
  * @param action Current action handler.
  */
-/* static void ggp_action_buddylist_put(PurplePluginAction *action) {{{ */
 static void ggp_action_buddylist_put(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -232,14 +220,12 @@
 	gg_userlist_request(info->session, GG_USERLIST_PUT, buddylist);
 	g_free(buddylist);
 }
-/* }}} */
 
 /**
  * Delete buddylist from the server.
  *
  * @param action Current action handler.
  */
-/* static void ggp_action_buddylist_delete(PurplePluginAction *action) {{{ */
 static void ggp_action_buddylist_delete(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -249,11 +235,7 @@
 
 	gg_userlist_request(info->session, GG_USERLIST_PUT, NULL);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *file) {{{ */
 static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *filename)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
@@ -284,11 +266,7 @@
 
 	g_free(buddylist);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_buddylist_load_ok(PurpleConnection *gc, gchar *file) {{{ */
 static void ggp_callback_buddylist_load_ok(PurpleConnection *gc, gchar *file)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
@@ -334,11 +312,7 @@
 			purple_connection_get_account(gc), NULL, NULL,
 			gc);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_action_buddylist_load(PurplePluginAction *action) {{{ */
 static void ggp_action_buddylist_load(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -349,11 +323,7 @@
 			purple_connection_get_account(gc), NULL, NULL,
 			gc);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_register_account_ok(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_register_account_ok(PurpleConnection *gc,
 					     PurpleRequestFields *fields)
 {
@@ -435,11 +405,7 @@
 	g_free(token->id);
 	g_free(token);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_register_account_cancel(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_register_account_cancel(PurpleConnection *gc,
 						 PurpleRequestFields *fields)
 {
@@ -453,11 +419,7 @@
 	g_free(token);
 
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_register_user_dialog(PurpleConnection *gc) {{{ */
 static void ggp_register_user_dialog(PurpleConnection *gc)
 {
 	PurpleAccount *account;
@@ -510,13 +472,9 @@
 		purple_connection_get_account(gc), NULL, NULL,
 		gc);
 }
-/* }}} */
 
 /* ----- PUBLIC DIRECTORY SEARCH ---------------------------------------- */
 
-/*
- */
-/* static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data) {{{ */
 static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data)
 {
 	GGPInfo *info = gc->proto_data;
@@ -533,21 +491,13 @@
 	ggp_search_add(info->searches, seq, form);
 	purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u", seq);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data) {{{ */
 static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data)
 {
 	purple_blist_request_add_buddy(purple_connection_get_account(gc),
 				     g_list_nth_data(row, 0), NULL, NULL);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_im(PurpleConnection *gc, GList *row, gpointer user_data) {{{ */
 static void ggp_callback_im(PurpleConnection *gc, GList *row, gpointer user_data)
 {
 	PurpleAccount *account;
@@ -560,11 +510,7 @@
 	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
 	purple_conversation_present(conv);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	GGPInfo *info = gc->proto_data;
@@ -611,11 +557,7 @@
 	ggp_search_add(info->searches, seq, form);
 	purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u", seq);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_find_buddies(PurplePluginAction *action) {{{ */
 static void ggp_find_buddies(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
@@ -672,13 +614,9 @@
 		purple_connection_get_account(gc), NULL, NULL,
 		gc);
 }
-/* }}} */
 
 /* ----- CHANGE PASSWORD ------------------------------------------------ */
 
-/*
- */
-/* static void ggp_callback_change_passwd_ok(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_change_passwd_ok(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	PurpleAccount *account;
@@ -750,11 +688,7 @@
 	g_free(info->token->data);
 	g_free(info->token);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_change_passwd_dialog(PurpleConnection *gc) {{{ */
 static void ggp_change_passwd_dialog(PurpleConnection *gc)
 {
 	PurpleRequestFields *fields;
@@ -811,24 +745,16 @@
 
 	g_free(msg);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_change_passwd(PurplePluginAction *action) {{{ */
 static void ggp_change_passwd(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
 
 	ggp_token_request(gc, ggp_change_passwd_dialog);
 }
-/* }}} */
 
 /* ----- CONFERENCES ---------------------------------------------------- */
 
-/*
- */
-/* static void ggp_callback_add_to_chat_ok(PurpleConnection *gc, PurpleRequestFields *fields) {{{ */
 static void ggp_callback_add_to_chat_ok(PurpleConnection *gc, PurpleRequestFields *fields)
 {
 	GGPInfo *info = gc->proto_data;
@@ -842,11 +768,7 @@
 	ggp_confer_participants_add_uin(gc, sel->data, info->tmp_buddy);
 	info->tmp_buddy = 0;
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored) {{{ */
 static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored)
 {
 	PurpleBuddy *buddy;
@@ -892,13 +814,9 @@
 			gc);
 	g_free(msg);
 }
-/* }}} */
 
 /* ----- BLOCK BUDDIES -------------------------------------------------- */
 
-/*
- */
-/* static void ggp_bmenu_block(PurpleBlistNode *node, gpointer ignored) {{{ */
 static void ggp_bmenu_block(PurpleBlistNode *node, gpointer ignored)
 {
 	PurpleConnection *gc;
@@ -924,7 +842,6 @@
 		purple_debug_info("gg", "send: uin=%d; mode=BLOCKED\n", uin);
 	}
 }
-/* }}} */
 
 /* ---------------------------------------------------------------------- */
 /* ----- INTERNAL CALLBACKS --------------------------------------------- */
@@ -943,7 +860,6 @@
  * @param status ID of the status.
  * @param descr  Description.
  */
-/* static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin, int status, const char *descr) {{{ */
 static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin,
 				       int status, const char *descr)
 {
@@ -983,11 +899,7 @@
 	g_free(from);
 	g_free(msg);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_sr_close_cb(gpointer user_data) {{{ */
 static void ggp_sr_close_cb(gpointer user_data)
 {
 	GGPSearchForm *form = user_data;
@@ -997,7 +909,6 @@
 	purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u", form->seq);
 	ggp_search_form_destroy(form);
 }
-/* }}} */
 
 /**
  * Translate a status' ID to a more user-friendly name.
@@ -1006,7 +917,6 @@
  *
  * @return The user-friendly name of the status.
  */
-/* static const char *ggp_status_by_id(unsigned int id) {{{ */
 static const char *ggp_status_by_id(unsigned int id)
 {
 	const char *st;
@@ -1029,11 +939,7 @@
 
 	return st;
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_pubdir_handle_info(PurpleConnection *gc, gg_pubdir50_t req, GGPSearchForm *form) {{{ */
 static void ggp_pubdir_handle_info(PurpleConnection *gc, gg_pubdir50_t req,
 				   GGPSearchForm *form)
 {
@@ -1092,11 +998,7 @@
 	g_free(who);
 	purple_notify_user_info_destroy(user_info);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_pubdir_handle_full(PurpleConnection *gc, gg_pubdir50_t req, GGPSearchForm *form) {{{ */
 static void ggp_pubdir_handle_full(PurpleConnection *gc, gg_pubdir50_t req,
 				   GGPSearchForm *form)
 {
@@ -1196,11 +1098,7 @@
 		purple_notify_searchresults_new_rows(gc, results, form->window);
 	}
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req) {{{ */
 static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req)
 {
 	GGPInfo *info = gc->proto_data;
@@ -1239,15 +1137,56 @@
 			break;
 	}
 }
-/* }}} */
+
+static void ggp_recv_image_handler(PurpleConnection *gc, const struct gg_event *ev)
+{
+	gint imgid = 0;
+	GGPInfo *info = gc->proto_data;
+	GList *entry = g_list_first(info->pending_richtext_messages);
+	gchar *handlerid = g_strdup_printf("IMGID_HANDLER-%i", ev->event.image_reply.crc32);
+
+	imgid = purple_imgstore_add_with_id(
+		g_memdup(ev->event.image_reply.image, ev->event.image_reply.size),
+		ev->event.image_reply.size,
+		ev->event.image_reply.filename);
+
+	purple_debug_info("gg", "ggp_recv_image_handler: got image with crc32: %u\n", ev->event.image_reply.crc32);
+
+	while(entry) {
+		if (strstr((gchar *)entry->data, handlerid) != NULL) {
+			gchar **split = g_strsplit((gchar *)entry->data, handlerid, 3);
+			gchar *text = g_strdup_printf("%s%i%s", split[0], imgid, split[1]);
+			purple_debug_info("gg", "ggp_recv_image_handler: found message matching crc32: %s\n", (gchar *)entry->data);
+			g_strfreev(split);
+			info->pending_richtext_messages = g_list_remove(info->pending_richtext_messages, entry->data);
+			/* We don't have any more images to download */
+			if (strstr(text, "<IMG ID=\"IMGID_HANDLER") == NULL) {
+				gchar *buf = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
+				serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, ev->event.msg.time);
+				g_free(buf);
+				purple_debug_info("gg", "ggp_recv_image_handler: richtext message: %s\n", text);
+				g_free(text);
+				break;
+			}
+			info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, text);
+			break;
+		}
+		entry = g_list_next(entry);
+	}
+	g_free(handlerid);
+
+	return;
+}
+
 
 /**
  * Dispatch a message received from a buddy.
  *
  * @param gc PurpleConnection.
  * @param ev Gadu-Gadu event structure.
+ *
+ * Image receiving, some code borrowed from Kadu http://www.kadu.net
  */
-/* static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev) {{{ */
 static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev)
 {
 	GGPInfo *info = gc->proto_data;
@@ -1264,7 +1203,109 @@
 	msg = g_markup_escape_text(tmp, -1);
 	g_free(tmp);
 
-	purple_debug_info("gg", "msg form (%s): %s (class = %d; rcpt_count = %d)\n",
+	/* We got richtext message */
+	if (ev->event.msg.formats_length)
+	{
+		gboolean got_image = FALSE, bold = FALSE, italic = FALSE, under = FALSE;
+		char *cformats = (char *)ev->event.msg.formats;
+		char *cformats_end = cformats + ev->event.msg.formats_length;
+		gint increased_len = 0;
+		struct gg_msg_richtext_format *actformat;
+		struct gg_msg_richtext_image *actimage;
+		GString *message = g_string_new(msg);
+		gchar *handlerid;
+
+		purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->event.msg.formats_length);
+
+		while (cformats < cformats_end)
+		{
+			gint byteoffset;
+			actformat = (struct gg_msg_richtext_format *)cformats;
+			cformats += sizeof(struct gg_msg_richtext_format);
+			byteoffset = g_utf8_offset_to_pointer(message->str, actformat->position + increased_len) - message->str;
+
+			if(actformat->position == 0 && actformat->font == 0) {
+				purple_debug_warning("gg", "ggp_recv_message_handler: bogus formatting (inc: %i)\n", increased_len);
+				continue;
+			}
+			purple_debug_info("gg", "ggp_recv_message_handler: format at pos: %i, image:%i, bold:%i, italic: %i, under:%i (inc: %i)\n",
+				actformat->position,
+				(actformat->font & GG_FONT_IMAGE) != 0,
+				(actformat->font & GG_FONT_BOLD) != 0,
+				(actformat->font & GG_FONT_ITALIC) != 0,
+				(actformat->font & GG_FONT_UNDERLINE) != 0,
+				increased_len);
+
+			if (actformat->font & GG_FONT_IMAGE) {
+				got_image = TRUE;
+				actimage = (struct gg_msg_richtext_image*)(cformats);
+				cformats += sizeof(struct gg_msg_richtext_image);
+				purple_debug_info("gg", "ggp_recv_message_handler: image received, size: %d, crc32: %i\n", actimage->size, actimage->crc32);
+
+				/* Checking for errors, image size shouldn't be
+				 * larger than 255.000 bytes */
+				if (actimage->size > 255000) {
+					purple_debug_warning("gg", "ggp_recv_message_handler: received image large than 255 kb\n");
+					continue;
+				}
+
+				gg_image_request(info->session, ev->event.msg.sender,
+					actimage->size, actimage->crc32);
+
+				handlerid = g_strdup_printf("<IMG ID=\"IMGID_HANDLER-%i\">", actimage->crc32);
+				g_string_insert(message, byteoffset, handlerid);
+				increased_len += strlen(handlerid);
+				g_free(handlerid);
+				continue;
+			}
+
+			if (actformat->font & GG_FONT_BOLD) {
+				if (bold == FALSE) {
+					g_string_insert(message, byteoffset, "<b>");
+					increased_len += 3;
+					bold = TRUE;
+				}
+			} else if (bold) {
+				g_string_insert(message, byteoffset, "</b>");
+				increased_len += 4;
+				bold = FALSE;
+			}
+
+			if (actformat->font & GG_FONT_ITALIC) {
+				if (italic == FALSE) {
+					g_string_insert(message, byteoffset, "<i>");
+					increased_len += 3;
+					italic = TRUE;
+				}
+			} else if (italic) {
+				g_string_insert(message, byteoffset, "</i>");
+				increased_len += 4;
+				italic = FALSE;
+			}
+
+			if (actformat->font & GG_FONT_UNDERLINE) {
+				if (under == FALSE) {
+					g_string_insert(message, byteoffset, "<u>");
+					increased_len += 3;
+					under = TRUE;
+				}
+			} else if (under) {
+				g_string_insert(message, byteoffset, "</u>");
+				increased_len += 4;
+				under = FALSE;
+			}
+		}
+
+		msg = message->str;
+		g_string_free(message, FALSE);
+
+		if (got_image) {
+			info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, msg);
+			return;
+		}
+	}
+
+	purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d)\n",
 			from, msg, ev->event.msg.msgclass,
 			ev->event.msg.recipients_count);
 
@@ -1301,11 +1342,32 @@
 	g_free(msg);
 	g_free(from);
 }
-/* }}} */
+
+static void ggp_send_image_handler(PurpleConnection *gc, const struct gg_event *ev)
+{
+	GGPInfo *info = gc->proto_data;
+	PurpleStoredImage *image;
+	gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, &ev->event.image_request.crc32));
+
+	purple_debug_info("gg", "ggp_send_image_handler: image request received, crc32: %u\n", ev->event.image_request.crc32);
 
-/*
- */
-/* static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond) {{{ */
+	if(imgid)
+	{
+		if((image = purple_imgstore_find_by_id(imgid))) {
+			gint image_size = purple_imgstore_get_size(image);
+			gconstpointer image_bin = purple_imgstore_get_data(image);
+			const char *image_filename = purple_imgstore_get_filename(image);
+
+			purple_debug_info("gg", "ggp_send_image_handler: sending image imgid: %i, crc: %u\n", imgid, ev->event.image_request.crc32);
+			gg_image_reply(info->session, (unsigned long int)ev->event.image_request.sender, image_filename, image_bin, image_size);
+			purple_imgstore_unref(image);
+		} else {
+			purple_debug_error("gg", "ggp_send_image_handler: image imgid: %i, crc: %u in hash but not found in imgstore!\n", imgid, ev->event.image_request.crc32);
+		}
+		g_hash_table_remove(info->pending_images, &ev->event.image_request.crc32);
+	}
+}
+
 static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
@@ -1330,11 +1392,18 @@
 			ggp_recv_message_handler(gc, ev);
 			break;
 		case GG_EVENT_ACK:
+			/* Changing %u to %i fixes compiler warning */
 			purple_debug_info("gg",
-				"message sent to: %u, delivery status=%d, seq=%d\n",
+				"ggp_callback_recv: message sent to: %i, delivery status=%d, seq=%d\n",
 				ev->event.ack.recipient, ev->event.ack.status,
 				ev->event.ack.seq);
 			break;
+		case GG_EVENT_IMAGE_REPLY:
+			ggp_recv_image_handler(gc, ev);
+			break;
+		case GG_EVENT_IMAGE_REQUEST:
+			ggp_send_image_handler(gc, ev);
+			break;
 		case GG_EVENT_NOTIFY:
 		case GG_EVENT_NOTIFY_DESCR:
 			{
@@ -1426,11 +1495,7 @@
 
 	gg_free_event(ev);
 }
-/* }}} */
 
-/*
- */
-/* static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond) {{{ */
 static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
@@ -1519,20 +1584,16 @@
 
 	gg_free_event(ev);
 }
-/* }}} */
 
 /* ---------------------------------------------------------------------- */
 /* ----- PurplePluginProtocolInfo ----------------------------------------- */
 /* ---------------------------------------------------------------------- */
 
-/* static const char *ggp_list_icon(PurpleAccount *account, PurpleBuddy *buddy) {{{ */
 static const char *ggp_list_icon(PurpleAccount *account, PurpleBuddy *buddy)
 {
 	return "gadu-gadu";
 }
-/* }}} */
 
-/* static char *ggp_status_text(PurpleBuddy *b) {{{ */
 static char *ggp_status_text(PurpleBuddy *b)
 {
 	PurpleStatus *status;
@@ -1558,20 +1619,21 @@
 		return text;
 	}
 }
-/* }}} */
 
-/* static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) {{{ */
 static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
 	PurpleStatus *status;
 	char *text, *tmp;
-	const char *msg, *name;
+	const char *msg, *name, *alias;
 
 	g_return_if_fail(b != NULL);
 
 	status = purple_presence_get_active_status(purple_buddy_get_presence(b));
 	msg = purple_status_get_attr_string(status, "message");
 	name = purple_status_get_name(status);
+	alias = purple_buddy_get_alias(b);
+
+	purple_notify_user_info_add_pair (user_info, _("Alias"), alias);
 
 	if (msg != NULL) {
 		text = g_markup_escape_text(msg, -1);
@@ -1588,9 +1650,7 @@
 		purple_notify_user_info_add_pair(user_info, _("Status"), name);
 	}
 }
-/* }}} */
 
-/* static GList *ggp_status_types(PurpleAccount *account) {{{ */
 static GList *ggp_status_types(PurpleAccount *account)
 {
 	PurpleStatusType *type;
@@ -1634,9 +1694,7 @@
 
 	return types;
 }
-/* }}} */
 
-/* static GList *ggp_blist_node_menu(PurpleBlistNode *node) {{{ */
 static GList *ggp_blist_node_menu(PurpleBlistNode *node)
 {
 	PurpleMenuAction *act;
@@ -1666,9 +1724,7 @@
 
 	return m;
 }
-/* }}} */
 
-/* static GList *ggp_chat_info(PurpleConnection *gc) {{{ */
 static GList *ggp_chat_info(PurpleConnection *gc)
 {
 	GList *m = NULL;
@@ -1682,9 +1738,7 @@
 
 	return m;
 }
-/* }}} */
 
-/* static void ggp_login(PurpleAccount *account) {{{ */
 static void ggp_login(PurpleAccount *account)
 {
 	PurpleConnection *gc;
@@ -1706,11 +1760,14 @@
 	info->chats_count = 0;
 	info->token = NULL;
 	info->searches = ggp_search_new();
+	info->pending_richtext_messages = NULL;
+	info->pending_images = g_hash_table_new(g_int_hash, g_int_equal);
 
 	gc->proto_data = info;
 
 	glp->uin = ggp_get_uin(account);
 	glp->password = (char *)purple_account_get_password(account);
+	glp->image_size = 255;
 
 	presence = purple_account_get_presence(account);
 	status = purple_presence_get_active_status(presence);
@@ -1730,9 +1787,7 @@
 	gc->inpa = purple_input_add(info->session->fd, PURPLE_INPUT_READ,
 				  ggp_async_login_handler, gc);
 }
-/* }}} */
 
-/* static void ggp_close(PurpleConnection *gc) {{{ */
 static void ggp_close(PurpleConnection *gc)
 {
 
@@ -1760,6 +1815,8 @@
 		purple_notify_close_with_handle(gc);
 
 		ggp_search_destroy(info->searches);
+		g_list_free(info->pending_richtext_messages);
+		g_hash_table_destroy(info->pending_images);
 		g_free(info);
 		gc->proto_data = NULL;
 	}
@@ -1771,25 +1828,108 @@
 
 	purple_debug_info("gg", "Connection closed.\n");
 }
-/* }}} */
 
-/* static int ggp_send_im(PurpleConnection *gc, const char *who, const char *msg, PurpleMessageFlags flags) {{{ */
 static int ggp_send_im(PurpleConnection *gc, const char *who, const char *msg,
 		       PurpleMessageFlags flags)
 {
 	GGPInfo *info = gc->proto_data;
 	char *tmp, *plain;
-	int ret = 0;
+	int ret = 1;
+	unsigned char format[1024];
+	unsigned int format_length = sizeof(struct gg_msg_richtext);
+	gint pos = 0;
+	GData *attribs;
+	const char *start, *end = NULL, *last;
 
-	if (strlen(msg) == 0) {
+	if (msg == NULL || *msg == '\0') {
 		return 0;
 	}
 
-	purple_debug_info("gg", "ggp_send_im: msg = %s\n", msg);
-	plain = purple_unescape_html(msg);
+	last = msg;
+
+	/* Check if the message is richtext */
+	/* TODO: Check formatting, too */
+	if(purple_markup_find_tag("img", last, &start, &end, &attribs)) {
+
+		GString *string_buffer = g_string_new(NULL);
+		struct gg_msg_richtext fmt;
+
+		do {
+			PurpleStoredImage *image;
+			const char *id;
+
+			/* Add text before the image */
+			if(start - last) {
+				pos = pos + g_utf8_strlen(last, start - last);
+				g_string_append_len(string_buffer, last, start - last);
+			}
+
+			if((id = g_datalist_get_data(&attribs, "id")) && (image = purple_imgstore_find_by_id(atoi(id)))) {
+				struct gg_msg_richtext_format actformat;
+				struct gg_msg_richtext_image actimage;
+				gint image_size = purple_imgstore_get_size(image);
+				gconstpointer image_bin = purple_imgstore_get_data(image);
+				const char *image_filename = purple_imgstore_get_filename(image);
+				uint32_t crc32 = gg_crc32(0, image_bin, image_size);
+
+				g_hash_table_insert(info->pending_images, &crc32, GINT_TO_POINTER(atoi(id)));
+				purple_imgstore_ref(image);
+				purple_debug_info("gg", "ggp_send_im_richtext: got crc: %i for imgid: %i\n", crc32, atoi(id));
+
+				actformat.font = GG_FONT_IMAGE;
+				actformat.position = pos;
+
+				actimage.unknown1 = 0x0109;
+				actimage.size = gg_fix32(image_size);
+				actimage.crc32 = gg_fix32(crc32);
+
+				if (actimage.size > 255000) {
+					purple_debug_warning("gg", "ggp_send_im_richtext: image over 255kb!\n");
+					continue;
+				}
+
+				purple_debug_info("gg", "ggp_send_im_richtext: adding images to richtext, size: %i, crc32: %u, name: %s\n", actimage.size, actimage.crc32, image_filename);
+
+				memcpy(format + format_length, &actformat, sizeof(actformat));
+				format_length += sizeof(actformat);
+				memcpy(format + format_length, &actimage, sizeof(actimage));
+				format_length += sizeof(actimage);
+			} else {
+				purple_debug_error("gg", "ggp_send_im_richtext: image not found in the image store!");
+			}
+
+			last = end + 1;
+			g_datalist_clear(&attribs);
+
+		} while(purple_markup_find_tag("img", last, &start, &end, &attribs));
+
+		/* Add text after the images */
+		if(last && *last) {
+			pos = pos + g_utf8_strlen(last, -1);
+			g_string_append(string_buffer, last);
+		}
+
+		fmt.flag = 2;
+		fmt.length = format_length - sizeof(fmt);
+		memcpy(format, &fmt, sizeof(fmt));
+
+		purple_debug_info("gg", "ggp_send_im: richtext msg = %s\n", string_buffer->str);
+		plain = purple_unescape_html(string_buffer->str);
+		g_string_free(string_buffer, TRUE);
+	} else {
+		purple_debug_info("gg", "ggp_send_im: msg = %s\n", msg);
+		plain = purple_unescape_html(msg);
+	}
+
 	tmp = charset_convert(plain, "UTF-8", "CP1250");
 
-	if (NULL == tmp || strlen(tmp) == 0) {
+	if (tmp && (format_length - sizeof(struct gg_msg_richtext))) {
+		if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) {
+			ret = -1;
+		} else {
+			ret = 1;
+		}
+	} else if (NULL == tmp || *tmp == 0) {
 		ret = 0;
 	} else if (strlen(tmp) > GG_MSG_MAXSIZE) {
 		ret = -E2BIG;
@@ -1805,9 +1945,7 @@
 
 	return ret;
 }
-/* }}} */
 
-/* static void ggp_get_info(PurpleConnection *gc, const char *name) { {{{ */
 static void ggp_get_info(PurpleConnection *gc, const char *name)
 {
 	GGPInfo *info = gc->proto_data;
@@ -1825,9 +1963,7 @@
 	ggp_search_add(info->searches, seq, form);
 	purple_debug_info("gg", "ggp_get_info(): Added seq %u", seq);
 }
-/* }}} */
 
-/* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
 static int ggp_to_gg_status(PurpleStatus *status, char **msg)
 {
 	const char *status_id = purple_status_get_id(status);
@@ -1872,9 +2008,7 @@
 		return new_status;
 	}
 }
-/* }}} */
 
-/* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
 static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
 {
 	PurpleConnection *gc;
@@ -1900,9 +2034,7 @@
 	ggp_status_fake_to_self(account);
 
 }
-/* }}} */
 
-/* static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {{{ */
 static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
 {
 	PurpleAccount *account;
@@ -1915,9 +2047,7 @@
 		ggp_status_fake_to_self(account);
 	}
 }
-/* }}} */
 
-/* static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {{{ */
 static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
 						 PurpleGroup *group)
 {
@@ -1925,9 +2055,7 @@
 
 	gg_remove_notify(info->session, ggp_str_to_uin(buddy->name));
 }
-/* }}} */
 
-/* static void ggp_join_chat(PurpleConnection *gc, GHashTable *data) {{{ */
 static void ggp_join_chat(PurpleConnection *gc, GHashTable *data)
 {
 	GGPInfo *info = gc->proto_data;
@@ -1960,15 +2088,11 @@
 				purple_account_get_username(account), NULL,
 				PURPLE_CBFLAGS_NONE, TRUE);
 }
-/* }}} */
 
-/* static char *ggp_get_chat_name(GHashTable *data) { {{{ */
 static char *ggp_get_chat_name(GHashTable *data) {
 	return g_strdup(g_hash_table_lookup(data, "name"));
 }
-/* }}} */
 
-/* static int ggp_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) {{{ */
 static int ggp_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
 {
 	PurpleConversation *conv;
@@ -2020,9 +2144,7 @@
 
 	return 0;
 }
-/* }}} */
 
-/* static void ggp_keepalive(PurpleConnection *gc) {{{ */
 static void ggp_keepalive(PurpleConnection *gc)
 {
 	GGPInfo *info = gc->proto_data;
@@ -2037,9 +2159,7 @@
 			_("Not connected to the server."));
 	}
 }
-/* }}} */
 
-/* static void ggp_register_user(PurpleAccount *account) {{{ */
 static void ggp_register_user(PurpleAccount *account)
 {
 	PurpleConnection *gc = purple_account_get_connection(account);
@@ -2049,9 +2169,7 @@
 
 	ggp_token_request(gc, ggp_register_user_dialog);
 }
-/* }}} */
 
-/* static GList *ggp_actions(PurplePlugin *plugin, gpointer context) {{{ */
 static GList *ggp_actions(PurplePlugin *plugin, gpointer context)
 {
 	GList *m = NULL;
@@ -2091,19 +2209,15 @@
 
 	return m;
 }
-/* }}} */
 
-/* static gboolean ggp_offline_message(const PurpleBuddy *buddy) {{{ */
 static gboolean ggp_offline_message(const PurpleBuddy *buddy)
 {
 	return TRUE;
 }
-/* }}} */
 
-/* prpl_info setup {{{ */
 static PurplePluginProtocolInfo prpl_info =
 {
-	OPT_PROTO_REGISTER_NOSCREENNAME,
+	OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_IM_IMAGE,
 	NULL,				/* user_splits */
 	NULL,				/* protocol_options */
 	{"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY},	/* icon_spec */
@@ -2173,36 +2287,34 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL
 };
-/* }}} */
 
-/* PurplePluginInfo setup {{{ */
 static PurplePluginInfo info = {
-	PURPLE_PLUGIN_MAGIC,		/* magic */
-	PURPLE_MAJOR_VERSION,		/* major_version */
-	PURPLE_MINOR_VERSION,		/* minor_version */
-	PURPLE_PLUGIN_PROTOCOL,		/* plugin type */
-	NULL,				/* ui_requirement */
-	0,				/* flags */
-	NULL,				/* dependencies */
+	PURPLE_PLUGIN_MAGIC,			/* magic */
+	PURPLE_MAJOR_VERSION,			/* major_version */
+	PURPLE_MINOR_VERSION,			/* minor_version */
+	PURPLE_PLUGIN_PROTOCOL,			/* plugin type */
+	NULL,					/* ui_requirement */
+	0,					/* flags */
+	NULL,					/* dependencies */
 	PURPLE_PRIORITY_DEFAULT,		/* priority */
 
-	"prpl-gg",			/* id */
-	"Gadu-Gadu",			/* name */
-	DISPLAY_VERSION,		/* version */
+	"prpl-gg",				/* id */
+	"Gadu-Gadu",				/* name */
+	DISPLAY_VERSION,			/* version */
 
 	N_("Gadu-Gadu Protocol Plugin"),	/* summary */
 	N_("Polish popular IM"),		/* description */
-	"boler@sourceforge.net",	/* author */
-	PURPLE_WEBSITE,			/* homepage */
+	"boler@sourceforge.net",		/* author */
+	PURPLE_WEBSITE,				/* homepage */
 
-	NULL,				/* load */
-	NULL,				/* unload */
-	NULL,				/* destroy */
+	NULL,					/* load */
+	NULL,					/* unload */
+	NULL,					/* destroy */
 
-	NULL,				/* ui_info */
-	&prpl_info,			/* extra_info */
-	NULL,				/* prefs_info */
-	ggp_actions,			/* actions */
+	NULL,					/* ui_info */
+	&prpl_info,				/* extra_info */
+	NULL,					/* prefs_info */
+	ggp_actions,				/* actions */
 
 	/* padding */
 	NULL,
@@ -2210,9 +2322,7 @@
 	NULL,
 	NULL
 };
-/* }}} */
 
-/* static void purple_gg_debug_handler(int level, const char * format, va_list args) {{{ */
 static void purple_gg_debug_handler(int level, const char * format, va_list args) {
 	PurpleDebugLevel purple_level;
 	char *msg = g_strdup_vprintf(format, args);
@@ -2235,11 +2345,7 @@
 	purple_debug(purple_level, "gg", "%s", msg);
 	g_free(msg);
 }
-/* }}} */
 
-/*
- */
-/* static void init_plugin(PurplePlugin *plugin) {{{ */
 static void init_plugin(PurplePlugin *plugin)
 {
 	PurpleAccountOption *option;
@@ -2253,7 +2359,6 @@
 
 	gg_debug_handler = purple_gg_debug_handler;
 }
-/* }}} */
 
 PURPLE_INIT_PLUGIN(gg, init_plugin, info);
 
--- a/libpurple/protocols/gg/gg.h	Sun Nov 23 04:12:43 2008 +0000
+++ b/libpurple/protocols/gg/gg.h	Sun Nov 23 07:31:20 2008 +0000
@@ -65,9 +65,10 @@
 	uin_t tmp_buddy;
 	int chats_count;
 
+	GList *pending_richtext_messages;
+	GHashTable *pending_images;
 } GGPInfo;
 
-
 #endif /* _PURPLE_GG_H */
 
 /* vim: set ts=8 sts=0 sw=8 noet: */