diff libpurple/protocols/msn/p2p.c @ 31558:ce968e115c95

propagate from branch 'im.pidgin.cpw.masca.p2p' (head 33ca865dacb9e5bcf763d06f6a42cbaca337cc64) to branch 'im.pidgin.pidgin' (head 92f47f4e8b0cbb107fd97e1ab814d1cedbf109ad)
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Fri, 06 May 2011 06:25:14 +0000
parents f518effe7395
children 968103d932d6
line wrap: on
line diff
--- a/libpurple/protocols/msn/p2p.c	Tue Jan 04 05:05:06 2011 +0000
+++ b/libpurple/protocols/msn/p2p.c	Fri May 06 06:25:14 2011 +0000
@@ -23,77 +23,295 @@
  */
 
 #include "internal.h"
+#include "debug.h"
 
 #include "p2p.h"
+#include "tlv.h"
 #include "msnutils.h"
 
-MsnP2PHeader *
-msn_p2p_header_from_wire(const char *wire)
+MsnP2PInfo *
+msn_p2p_info_new(MsnP2PVersion version)
+{
+	MsnP2PInfo *info = g_new0(MsnP2PInfo, 1);
+	info->version = version;
+
+	switch (version) {
+		case MSN_P2P_VERSION_ONE:
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", version);
+			g_free(info);
+			info = NULL;
+	}
+
+	return info;
+}
+
+MsnP2PInfo *
+msn_p2p_info_dup(MsnP2PInfo *info)
 {
-	MsnP2PHeader *header;
+	MsnP2PInfo *new_info = g_new0(MsnP2PInfo, 1);
+
+	new_info->version = info->version;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			*new_info = *info;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			*new_info = *info;
+			new_info->header.v2.header_tlv = msn_tlvlist_copy(info->header.v2.header_tlv);
+			new_info->header.v2.data_tlv = msn_tlvlist_copy(info->header.v2.data_tlv);
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+			g_free(new_info);
+			new_info = NULL;
+	}
 
-	header = g_new(MsnP2PHeader, 1);
+	return new_info;
+}
+
+void
+msn_p2p_info_free(MsnP2PInfo *info)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			/* Nothing to do! */
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			msn_tlvlist_free(info->header.v2.header_tlv);
+			msn_tlvlist_free(info->header.v2.data_tlv);
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	g_free(info);
+}
 
-	header->session_id = msn_pop32le(wire);
-	header->id         = msn_pop32le(wire);
-	header->offset     = msn_pop64le(wire);
-	header->total_size = msn_pop64le(wire);
-	header->length     = msn_pop32le(wire);
-	header->flags      = msn_pop32le(wire);
-	header->ack_id     = msn_pop32le(wire);
-	header->ack_sub_id = msn_pop32le(wire);
-	header->ack_size   = msn_pop64le(wire);
+size_t
+msn_p2p_header_from_wire(MsnP2PInfo *info, const char *wire, size_t max_len)
+{
+	size_t len = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE: {
+			MsnP2PHeader *header = &info->header.v1;
+
+			if (max_len < P2P_PACKET_HEADER_SIZE) {
+				/* Invalid packet length */
+				len = 0;
+				break;
+			}
+
+			header->session_id = msn_pop32le(wire);
+			header->id         = msn_pop32le(wire);
+			header->offset     = msn_pop64le(wire);
+			header->total_size = msn_pop64le(wire);
+			header->length     = msn_pop32le(wire);
+			header->flags      = msn_pop32le(wire);
+			header->ack_id     = msn_pop32le(wire);
+			header->ack_sub_id = msn_pop32le(wire);
+			header->ack_size   = msn_pop64le(wire);
+
+			len = P2P_PACKET_HEADER_SIZE;
+			break;
+		}
+
+		case MSN_P2P_VERSION_TWO: {
+			MsnP2Pv2Header *header = &info->header.v2;
 
-	return header;
+			header->header_len = msn_pop8(wire);
+			header->opcode = msn_pop8(wire);
+			header->message_len = msn_pop16be(wire);
+			header->base_id = msn_pop32be(wire);
+			if (header->header_len + header->message_len + P2P_PACKET_FOOTER_SIZE > max_len) {
+				/* Invalid header and data length */
+				len = 0;
+				break;
+			}
+
+			if (header->header_len > 8) {
+				header->header_tlv = msn_tlvlist_read(wire, header->header_len - 8);
+				wire += header->header_len - 8;
+			}
+
+			if (header->message_len > 0) {
+				/* Parse Data packet */
+
+				header->data_header_len = msn_pop8(wire);
+				if (header->data_header_len > header->message_len) {
+					/* Invalid data header length */
+					len = 0;
+					break;
+				}
+				header->data_tf = msn_pop8(wire);
+				header->package_number = msn_pop16be(wire);
+				header->session_id = msn_pop32be(wire);
+
+				if (header->data_header_len > 8) {
+					header->data_tlv = msn_tlvlist_read(wire, header->data_header_len - 8);
+					wire += header->data_header_len - 8;
+				}
+			}
+
+			len = header->header_len + header->message_len;
+
+			break;
+		}
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return len;
 }
 
 char *
-msn_p2p_header_to_wire(MsnP2PHeader *header)
+msn_p2p_header_to_wire(MsnP2PInfo *info, size_t *len)
 {
-	char *wire;
+	char *wire = NULL;
 	char *tmp;
-	
-	tmp = wire = g_new(char, P2P_PACKET_HEADER_SIZE);
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE: {
+			MsnP2PHeader *header = &info->header.v1;
+			tmp = wire = g_new(char, P2P_PACKET_HEADER_SIZE);
+
+			msn_push32le(tmp, header->session_id);
+			msn_push32le(tmp, header->id);
+			msn_push64le(tmp, header->offset);
+			msn_push64le(tmp, header->total_size);
+			msn_push32le(tmp, header->length);
+			msn_push32le(tmp, header->flags);
+			msn_push32le(tmp, header->ack_id);
+			msn_push32le(tmp, header->ack_sub_id);
+			msn_push64le(tmp, header->ack_size);
+
+			if (len)
+				*len = P2P_PACKET_HEADER_SIZE;
+
+			break;
+		}
+
+		case MSN_P2P_VERSION_TWO: {
+			MsnP2Pv2Header *header = &info->header.v2;
+			char *header_wire = NULL;
+			char *data_header_wire = NULL;
+
+			if (header->header_tlv != NULL)
+				header_wire = msn_tlvlist_write(header->header_tlv, (size_t *)&header->header_len);
+			else
+				header->header_len = 0;
 
-	msn_push32le(tmp, header->session_id);
-	msn_push32le(tmp, header->id);
-	msn_push64le(tmp, header->offset);
-	msn_push64le(tmp, header->total_size);
-	msn_push32le(tmp, header->length);
-	msn_push32le(tmp, header->flags);
-	msn_push32le(tmp, header->ack_id);
-	msn_push32le(tmp, header->ack_sub_id);
-	msn_push64le(tmp, header->ack_size);
+			if (header->data_tlv != NULL)
+				data_header_wire = msn_tlvlist_write(header->data_tlv, (size_t *)&header->data_header_len);
+			else
+				header->data_header_len = 0;
+
+			tmp = wire = g_new(char, 16 + header->header_len + header->data_header_len);
+
+			msn_push8(tmp, header->header_len + 8);
+			msn_push8(tmp, header->opcode);
+			msn_push16be(tmp, header->data_header_len + 8 + header->message_len);
+			msn_push32be(tmp, header->base_id);
+
+			if (header_wire != NULL) {
+				memcpy(tmp, header_wire, header->header_len);
+				tmp += header->header_len;
+			}
+
+			msn_push8(tmp, header->data_header_len + 8);
+			msn_push8(tmp, header->data_tf);
+			msn_push16be(tmp, header->package_number);
+			msn_push32be(tmp, header->session_id);
+
+			if (data_header_wire != NULL) {
+				memcpy(tmp, data_header_wire, header->data_header_len);
+				tmp += header->data_header_len;
+			}
+
+			if (len)
+				*len = header->header_len + header->data_header_len + 16;
+
+			break;
+		}
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 
 	return wire;
-
 }
 
-MsnP2PFooter *
-msn_p2p_footer_from_wire(const char *wire)
+size_t
+msn_p2p_footer_from_wire(MsnP2PInfo *info, const char *wire)
 {
 	MsnP2PFooter *footer;
 
-	footer = g_new(MsnP2PFooter, 1);
+	footer = &info->footer;
 
 	footer->value = msn_pop32be(wire);
 
-	return footer;
+	return P2P_PACKET_FOOTER_SIZE;
 }
 
 char *
-msn_p2p_footer_to_wire(MsnP2PFooter *footer)
+msn_p2p_footer_to_wire(MsnP2PInfo *info, size_t *len)
 {
+	MsnP2PFooter *footer;
 	char *wire;
 	char *tmp;
 
+	footer = &info->footer;
 	tmp = wire = g_new(char, P2P_PACKET_FOOTER_SIZE);
 
 	msn_push32be(tmp, footer->value);
 
+	if (len)
+		*len = P2P_PACKET_FOOTER_SIZE;
+
 	return wire;
 }
 
+void
+msn_p2p_info_to_string(MsnP2PInfo *info, GString *str)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE: {
+			MsnP2PHeader *header = &info->header.v1;
+			g_string_append_printf(str, "Session ID: %u\r\n", header->session_id);
+			g_string_append_printf(str, "ID:         %u\r\n", header->id);
+			g_string_append_printf(str, "Offset:     %" G_GUINT64_FORMAT "\r\n", header->offset);
+			g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", header->total_size);
+			g_string_append_printf(str, "Length:     %u\r\n", header->length);
+			g_string_append_printf(str, "Flags:      0x%x\r\n", header->flags);
+			g_string_append_printf(str, "ACK ID:     %u\r\n", header->ack_id);
+			g_string_append_printf(str, "SUB ID:     %u\r\n", header->ack_sub_id);
+			g_string_append_printf(str, "ACK Size:   %" G_GUINT64_FORMAT "\r\n", header->ack_size);
+
+			break;
+		}
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	g_string_append_printf(str, "Footer:     0x%08X\r\n", info->footer.value);
+}
+
 gboolean
 msn_p2p_msg_is_data(const MsnP2PHeaderFlag flags)
 {
@@ -102,3 +320,401 @@
 	        flags == P2P_FILE_DATA);
 }
 
+gboolean
+msn_p2p_info_is_valid(MsnP2PInfo *info)
+{
+	gboolean valid = FALSE;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			valid = info->header.v1.total_size >= info->header.v1.length;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return valid;
+}
+
+gboolean
+msn_p2p_info_is_final(MsnP2PInfo *info)
+{
+	gboolean final = FALSE;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			final = info->header.v1.offset + info->header.v1.length >= info->header.v1.total_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return final;
+}
+
+guint32
+msn_p2p_info_get_session_id(MsnP2PInfo *info)
+{
+	guint32 session_id = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			session_id = info->header.v1.session_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			session_id = info->header.v2.session_id;
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return session_id;
+}
+
+guint32
+msn_p2p_info_get_id(MsnP2PInfo *info)
+{
+	guint32 id = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			id = info->header.v1.id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			id = info->header.v2.base_id;
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return id;
+}
+
+guint64
+msn_p2p_info_get_offset(MsnP2PInfo *info)
+{
+	guint64 offset = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			offset = info->header.v1.offset;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return offset;
+}
+
+guint64
+msn_p2p_info_get_total_size(MsnP2PInfo *info)
+{
+	guint64 total_size = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			total_size = info->header.v1.total_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return total_size;
+}
+
+guint32
+msn_p2p_info_get_length(MsnP2PInfo *info)
+{
+	guint32 length = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			length = info->header.v1.length;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return length;
+}
+
+guint32
+msn_p2p_info_get_flags(MsnP2PInfo *info)
+{
+	guint32 flags = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			flags = info->header.v1.flags;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return flags;
+}
+
+guint32
+msn_p2p_info_get_ack_id(MsnP2PInfo *info)
+{
+	guint32 ack_id = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			ack_id = info->header.v1.ack_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return ack_id;
+}
+
+guint32
+msn_p2p_info_get_ack_sub_id(MsnP2PInfo *info)
+{
+	guint32 ack_sub_id = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			ack_sub_id = info->header.v1.ack_sub_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return ack_sub_id;
+}
+
+guint64
+msn_p2p_info_get_ack_size(MsnP2PInfo *info)
+{
+	guint64 ack_size = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			ack_size = info->header.v1.ack_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return ack_size;
+}
+
+guint32
+msn_p2p_info_get_app_id(MsnP2PInfo *info)
+{
+	return info->footer.value;
+}
+
+void
+msn_p2p_info_set_session_id(MsnP2PInfo *info, guint32 session_id)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.session_id = session_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			info->header.v2.session_id = session_id;
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+}
+
+void
+msn_p2p_info_set_id(MsnP2PInfo *info, guint32 id)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.id = id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			info->header.v2.base_id = id;
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+}
+
+void
+msn_p2p_info_set_offset(MsnP2PInfo *info, guint64 offset)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.offset = offset;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+}
+
+void
+msn_p2p_info_set_total_size(MsnP2PInfo *info, guint64 total_size)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.total_size = total_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+}
+
+void
+msn_p2p_info_set_length(MsnP2PInfo *info, guint32 length)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.length = length;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+}
+
+void
+msn_p2p_info_set_flags(MsnP2PInfo *info, guint32 flags)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.flags = flags;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+}
+
+void
+msn_p2p_info_set_ack_id(MsnP2PInfo *info, guint32 ack_id)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.ack_id = ack_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+}
+
+void
+msn_p2p_info_set_ack_sub_id(MsnP2PInfo *info, guint32 ack_sub_id)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.ack_sub_id = ack_sub_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+}
+
+void
+msn_p2p_info_set_ack_size(MsnP2PInfo *info, guint64 ack_size)
+{
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.ack_size = ack_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+}
+
+void
+msn_p2p_info_set_app_id(MsnP2PInfo *info, guint32 app_id)
+{
+	info->footer.value = app_id;
+}
+