diff libpurple/protocols/msn/cmdproc.c @ 25377:0ffa511ac165

Re-combine large (multi-part) messages on MSN since we seem to say that we do support receiving chunked messages. References #393, though not the same as the patch in the ticket.
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Tue, 27 Jan 2009 05:22:10 +0000
parents e069c16db597
children 1977e930ab03
line wrap: on
line diff
--- a/libpurple/protocols/msn/cmdproc.c	Tue Jan 27 04:54:13 2009 +0000
+++ b/libpurple/protocols/msn/cmdproc.c	Tue Jan 27 05:22:10 2009 +0000
@@ -35,6 +35,9 @@
 	cmdproc->txqueue = g_queue_new();
 	cmdproc->history = msn_history_new();
 
+	cmdproc->multiparts = g_hash_table_new_full(g_str_hash, g_str_equal,
+	                                            NULL, (GDestroyNotify)msn_message_unref);
+
 	return cmdproc;
 }
 
@@ -53,6 +56,8 @@
 	if (cmdproc->last_cmd != NULL)
 		msn_command_destroy(cmdproc->last_cmd);
 
+	g_hash_table_destroy(cmdproc->multiparts);
+
 	g_free(cmdproc);
 }
 
@@ -235,6 +240,61 @@
 msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
 	MsnMsgTypeCb cb;
+	const char *messageId = NULL;
+
+	/* Multi-part messages */
+	if ((messageId = msn_message_get_attr(msg, "Message-ID")) != NULL) {
+		const char *chunk_text = msn_message_get_attr(msg, "Chunks");
+		guint chunk;
+		if (chunk_text != NULL) {
+			chunk = strtol(chunk_text, NULL, 10);
+			/* 1024 chunks of ~1300 bytes is ~1MB, which seems OK to prevent 
+			   some random client causing pidgin to hog a ton of memory.
+			   Probably should figure out the maximum that the official client
+			   actually supports, though. */
+			if (chunk > 0 && chunk < 1024) {
+				msg->total_chunks = chunk;
+				msg->received_chunks = 1;
+				g_hash_table_insert(cmdproc->multiparts, (gpointer)messageId, msn_message_ref(msg));
+				purple_debug_info("msn", "Received chunked message, messageId: '%s', total chunks: %d\n",
+				                  messageId, chunk);
+			} else {
+				purple_debug_error("msn", "MessageId '%s' has too many chunks: %d\n", messageId, chunk);
+			}
+			return;
+		} else {
+			chunk_text = msn_message_get_attr(msg, "Chunk");
+			if (chunk_text != NULL) {
+				MsnMessage *first = g_hash_table_lookup(cmdproc->multiparts, messageId);
+				chunk = strtol(chunk_text, NULL, 10);
+				if (first == NULL) {
+					purple_debug_error("msn",
+					                   "Unable to find first chunk of messageId '%s' to correspond with chunk %d.\n",
+					                   messageId, chunk+1);
+				} else if (first->received_chunks == chunk) {
+					/* Chunk is from 1 to total-1 (doesn't count first one) */
+					purple_debug_info("msn", "Received chunk %d of %d, messageId: '%s'\n",
+					                  chunk+1, first->total_chunks, messageId);
+					first->body = g_realloc(first->body, first->body_len + msg->body_len);
+					memcpy(first->body + first->body_len, msg->body, msg->body_len);
+					first->body_len += msg->body_len;
+					first->received_chunks++;
+					if (first->received_chunks != first->total_chunks)
+						return;
+					else
+						/* We're done! Send it along... The caller takes care of
+						   freeing the old one. */
+						msg = first;
+				} else {
+					/* TODO: Can you legitimately receive chunks out of order? */
+					g_hash_table_remove(cmdproc->multiparts, messageId);
+					return;
+				}
+			} else {
+				purple_debug_error("msn", "Received MessageId '%s' with no chunk number!\n", messageId);
+			}
+		}
+	}
 
 	if (msn_message_get_content_type(msg) == NULL)
 	{
@@ -245,15 +305,14 @@
 	cb = g_hash_table_lookup(cmdproc->cbs_table->msgs,
 							 msn_message_get_content_type(msg));
 
-	if (cb == NULL)
-	{
+	if (cb != NULL)
+		cb(cmdproc, msg);
+	else
 		purple_debug_warning("msn", "Unhandled content-type '%s'\n",
 						   msn_message_get_content_type(msg));
 
-		return;
-	}
-
-	cb(cmdproc, msg);
+	if (messageId != NULL)
+		g_hash_table_remove(cmdproc->multiparts, messageId);
 }
 
 void