Diff between b8cd1913e6261b166eab6a388c387487eda19f25 and e8c4fe5aa709e74170e50675b4fea2008168249a

Changed Files

File Additions Deletions Status
gobex/gobex.c +123 -0 modified
gobex/gobex.h +5 -0 modified
gobex/obex.h +26 -0 modified
unit/test-gobex.c +132 -0 modified

Full Patch

diff --git a/gobex/gobex.c b/gobex/gobex.c
index ef0feef..5546d82 100644
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
@@ -19,8 +19,31 @@
  *
  */
 
+#include <string.h>
+
 #include "gobex.h"
 
+/* Header types */
+#define G_OBEX_HDR_TYPE_UNICODE	(0 << 6)
+#define G_OBEX_HDR_TYPE_BYTES	(1 << 6)
+#define G_OBEX_HDR_TYPE_UINT8	(2 << 6)
+#define G_OBEX_HDR_TYPE_UINT32	(3 << 6)
+
+#define G_OBEX_HDR_TYPE(id)	((id) & 0xc0)
+
+struct _GObexHeader {
+	uint8_t id;
+	size_t len;
+	gboolean extdata;
+	union {
+		char *string;		/* UTF-8 converted from UTF-16 */
+		uint8_t *data;		/* Own buffer */
+		const uint8_t *extdata;	/* Reference to external buffer */
+		uint8_t u8;
+		uint32_t u32;
+	} v;
+};
+
 struct _GObexRequest {
 	uint8_t opcode;
 	GSList *headers;
@@ -33,6 +56,106 @@ struct _GObex {
 	GQueue *req_queue;
 };
 
+GObexHeader *g_obex_header_parse(const void *data, size_t len,
+						gboolean copy, size_t *parsed)
+{
+	GObexHeader *header;
+	const char *buf = data;
+	uint16_t hdr_len;
+	size_t str_len;
+
+	if (len < 2)
+		return NULL;
+
+	header = g_new0(GObexHeader, 1);
+
+	header->id = buf[0];
+
+	switch (G_OBEX_HDR_TYPE(header->id)) {
+	case G_OBEX_HDR_TYPE_UNICODE:
+		if (len < 3)
+			goto failed;
+		memcpy(&hdr_len, &buf[1], 2);
+		hdr_len = be16toh(hdr_len);
+		if (hdr_len > len || hdr_len < 5)
+			goto failed;
+
+		header->v.string = g_convert(&buf[3], hdr_len - 5,
+						"UTF8", "UTF16BE",
+						NULL, &str_len, NULL);
+		if (header->v.string == NULL)
+			goto failed;
+
+		header->len = (size_t) str_len;
+
+		*parsed = hdr_len;
+
+		break;
+	case G_OBEX_HDR_TYPE_BYTES:
+		if (len < 3)
+			goto failed;
+		memcpy(&hdr_len, &buf[1], 2);
+		hdr_len = be16toh(hdr_len);
+		if (hdr_len > len)
+			goto failed;
+
+		header->len = hdr_len - 3;
+
+		if (copy) {
+			header->v.data = g_malloc(hdr_len);
+			memcpy(header->v.data, &buf[3], header->len);
+		} else {
+			header->extdata = TRUE;
+			header->v.extdata = (const uint8_t *) &buf[3];
+		}
+
+		*parsed = hdr_len;
+
+		break;
+	case G_OBEX_HDR_TYPE_UINT8:
+		header->len = 1;
+		header->v.u8 = buf[1];
+		*parsed = 2;
+		break;
+	case G_OBEX_HDR_TYPE_UINT32:
+		if (len < 5)
+			goto failed;
+		header->len = 4;
+		memcpy(&header->v.u32, &buf[1], 4);
+		header->v.u32 = be32toh(header->v.u32);
+		*parsed = 5;
+		break;
+	default:
+		g_assert_not_reached();
+	}
+
+	return header;
+
+failed:
+	g_obex_header_free(header);
+	return NULL;
+}
+
+void g_obex_header_free(GObexHeader *header)
+{
+	switch (G_OBEX_HDR_TYPE(header->id)) {
+	case G_OBEX_HDR_TYPE_UNICODE:
+		g_free(header->v.string);
+		break;
+	case G_OBEX_HDR_TYPE_BYTES:
+		if (!header->extdata)
+			g_free(header->v.data);
+		break;
+	case G_OBEX_HDR_TYPE_UINT8:
+	case G_OBEX_HDR_TYPE_UINT32:
+		break;
+	default:
+		g_assert_not_reached();
+	}
+
+	g_free(header);
+}
+
 GObexRequest *g_obex_request_new(uint8_t opcode)
 {
 	GObexRequest *req;
diff --git a/gobex/gobex.h b/gobex/gobex.h
index 3910367..e189616 100644
--- a/gobex/gobex.h
+++ b/gobex/gobex.h
@@ -29,6 +29,11 @@
 
 typedef struct _GObex GObex;
 typedef struct _GObexRequest GObexRequest;
+typedef struct _GObexHeader GObexHeader;
+
+GObexHeader *g_obex_header_parse(const void *data, size_t len,
+						gboolean copy, size_t *parsed);
+void g_obex_header_free(GObexHeader *header);
 
 GObexRequest *g_obex_request_new(uint8_t opcode);
 void g_obex_request_free(GObexRequest *req);
diff --git a/gobex/obex.h b/gobex/obex.h
index 45ef994..f5afe6a 100644
--- a/gobex/obex.h
+++ b/gobex/obex.h
@@ -32,4 +32,30 @@
 #define G_OBEX_OP_ABORT		0x7f
 #define G_OBEX_FINAL		0x80
 
+#define G_OBEX_HDR_ID_COUNT		0xc0
+#define G_OBEX_HDR_ID_NAME		0x01
+#define G_OBEX_HDR_ID_TYPE		0x42
+#define G_OBEX_HDR_ID_LENGTH		0xc3
+#define G_OBEX_HDR_ID_TIME		0x44
+#define G_OBEX_HDR_ID_DESCRIPTION	0x05
+#define G_OBEX_HDR_ID_TARGET		0x46
+#define G_OBEX_HDR_ID_HTTP		0x47
+#define G_OBEX_HDR_ID_BODY		0x48
+#define G_OBEX_HDR_ID_BODY_END		0x49
+#define G_OBEX_HDR_ID_WHO		0x4a
+#define G_OBEX_HDR_ID_CONNECTION	0xcb
+#define G_OBEX_HDR_ID_APPARAM		0x4c
+#define G_OBEX_HDR_ID_AUTHCHAL		0x4d
+#define G_OBEX_HDR_ID_AUTHRESP		0x4e
+#define G_OBEX_HDR_ID_CREATOR		0xcf
+#define G_OBEX_HDR_ID_WANUUID		0x50
+#define G_OBEX_HDR_ID_OBJECTCLASS	0x51
+#define G_OBEX_HDR_ID_SESSIONPARAM	0x52
+#define G_OBEX_HDR_ID_SESSIONSEQ	0x93
+#define G_OBEX_HDR_ID_ACTION		0x94
+#define G_OBEX_HDR_ID_DESTNAME		0x15
+#define G_OBEX_HDR_ID_PERMISSIONS	0xd6
+#define G_OBEX_HDR_ID_SRM         	0x97
+#define G_OBEX_HDR_ID_SRM_FLAGS   	0x98
+
 #endif /* __GOBEX_H */
diff --git a/unit/test-gobex.c b/unit/test-gobex.c
index a1fabf2..480847d 100644
--- a/unit/test-gobex.c
+++ b/unit/test-gobex.c
@@ -23,6 +23,12 @@
 
 #include <gobex/gobex.h>
 
+static uint8_t hdr_connid[] = { G_OBEX_HDR_ID_CONNECTION, 1, 2, 3, 4 };
+static uint8_t hdr_name[] = { G_OBEX_HDR_ID_NAME, 0x00, 0x0b,
+				0x00, 'f', 0x00, 'o', 0x00, 'o', 0x00, 0x00 };
+static uint8_t hdr_body[] = { G_OBEX_HDR_ID_BODY, 0x00, 0x07, 1, 2, 3, 4 };
+static uint8_t hdr_actionid[] = { G_OBEX_HDR_ID_ACTION, 0x00 };
+
 static GObex *create_gobex(int fd)
 {
 	GIOChannel *io;
@@ -33,6 +39,119 @@ static GObex *create_gobex(int fd)
 	return g_obex_new(io);
 }
 
+static void test_parse_header_connid(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+
+	header = g_obex_header_parse(hdr_connid, sizeof(hdr_connid),
+							FALSE, &parsed);
+	g_assert(header != NULL);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_connid));
+
+	g_obex_header_free(header);
+}
+
+static void test_parse_header_name(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+
+	header = g_obex_header_parse(hdr_name, sizeof(hdr_name),
+							FALSE, &parsed);
+	g_assert(header != NULL);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_name));
+
+	g_obex_header_free(header);
+}
+
+static void test_parse_header_body(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+
+	header = g_obex_header_parse(hdr_body, sizeof(hdr_body),
+							FALSE, &parsed);
+	g_assert(header != NULL);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+
+	g_obex_header_free(header);
+}
+
+static void test_parse_header_body_extdata(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+
+	header = g_obex_header_parse(hdr_body, sizeof(hdr_body),
+							TRUE, &parsed);
+	g_assert(header != NULL);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+
+	g_obex_header_free(header);
+}
+
+static void test_parse_header_actionid(void)
+{
+	GObexHeader *header;
+	size_t parsed;
+
+	header = g_obex_header_parse(hdr_actionid, sizeof(hdr_actionid),
+							FALSE, &parsed);
+	g_assert(header != NULL);
+
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_actionid));
+
+	g_obex_header_free(header);
+}
+
+static void test_parse_header_multi(void)
+{
+	GObexHeader *header;
+	GByteArray *buf;
+	size_t parsed;
+
+	buf = g_byte_array_sized_new(sizeof(hdr_connid) +
+					sizeof(hdr_name) +
+					sizeof(hdr_actionid) +
+					sizeof(hdr_body));
+
+	g_byte_array_append(buf, hdr_connid, sizeof(hdr_connid));
+	g_byte_array_append(buf, hdr_name, sizeof(hdr_name));
+	g_byte_array_append(buf, hdr_actionid, sizeof(hdr_actionid));
+	g_byte_array_append(buf, hdr_body, sizeof(hdr_body));
+
+	header = g_obex_header_parse(buf->data, buf->len, FALSE, &parsed);
+	g_assert(header != NULL);
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_connid));
+	g_byte_array_remove_range(buf, 0, parsed);
+	g_obex_header_free(header);
+
+	header = g_obex_header_parse(buf->data, buf->len, FALSE, &parsed);
+	g_assert(header != NULL);
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_name));
+	g_byte_array_remove_range(buf, 0, parsed);
+	g_obex_header_free(header);
+
+	header = g_obex_header_parse(buf->data, buf->len, FALSE, &parsed);
+	g_assert(header != NULL);
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_actionid));
+	g_byte_array_remove_range(buf, 0, parsed);
+	g_obex_header_free(header);
+
+	header = g_obex_header_parse(buf->data, buf->len, FALSE, &parsed);
+	g_assert(header != NULL);
+	g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+	g_byte_array_remove_range(buf, 0, parsed);
+	g_obex_header_free(header);
+
+	g_byte_array_unref(buf);
+}
+
 static void test_req(void)
 {
 	GObexRequest *req;
@@ -88,6 +207,19 @@ int main(int argc, char *argv[])
 
 	g_test_add_func("/gobex/test_req", test_req);
 
+	g_test_add_func("/gobex/test_parse_header_connid",
+						test_parse_header_connid);
+	g_test_add_func("/gobex/test_parse_header_name",
+						test_parse_header_name);
+	g_test_add_func("/gobex/test_parse_header_body",
+						test_parse_header_body);
+	g_test_add_func("/gobex/test_parse_header_body_extdata",
+					test_parse_header_body_extdata);
+	g_test_add_func("/gobex/test_parse_header_actionid",
+						test_parse_header_actionid);
+	g_test_add_func("/gobex/test_parse_header_multi",
+						test_parse_header_multi);
+
 	g_test_run();
 
 	return 0;