Diff between 352a739a897f4575c21fd520288574302a54cb8d and 8d451283e0d48f93620d93fad5d66d3e0edaeebf

Changed Files

File Additions Deletions Status
gobex/gobex.c +131 -0 modified
gobex/gobex.h +3 -0 modified
unit/test-gobex.c +13 -0 modified

Full Patch

diff --git a/gobex/gobex.c b/gobex/gobex.c
index 428fbda..3c70337 100644
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
@@ -19,6 +19,7 @@
  *
  */
 
+#include <unistd.h>
 #include <string.h>
 
 #include "gobex.h"
@@ -47,6 +48,15 @@ struct _GObexHeader {
 
 struct _GObexRequest {
 	guint8 opcode;
+
+	GObexDataPolicy data_policy;
+
+	union {
+		void *data;		/* Non-header data */
+		const void *data_ref;	/* Reference to non-header data */
+	} req;
+	size_t req_data_len;
+
 	size_t hlen;		/* Length of all encoded headers */
 	GSList *headers;
 };
@@ -58,6 +68,17 @@ struct _GObex {
 	GQueue *req_queue;
 };
 
+struct connect_data {
+	guint8 version;
+	guint8 flags;
+	guint16 mtu;
+} __attribute__ ((packed));
+
+struct setpath_data {
+	guint8 flags;
+	guint8 constants;
+} __attribute__ ((packed));
+
 static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) {
 	glong utf16_len;
 	int i;
@@ -348,16 +369,126 @@ GObexRequest *g_obex_request_new(guint8 opcode)
 
 	req->opcode = opcode;
 
+	req->data_policy = G_OBEX_DATA_COPY;
+
 	return req;
 }
 
 void g_obex_request_free(GObexRequest *req)
 {
+	switch (req->data_policy) {
+	case G_OBEX_DATA_INHERIT:
+	case G_OBEX_DATA_COPY:
+		g_free(req->req.data);
+		break;
+	case G_OBEX_DATA_REF:
+		break;
+	}
+
 	g_slist_foreach(req->headers, (GFunc) g_obex_header_free, NULL);
 	g_slist_free(req->headers);
 	g_free(req);
 }
 
+static ssize_t get_header_offset(guint8 opcode)
+{
+	switch (opcode) {
+	case G_OBEX_OP_CONNECT:
+		return sizeof(struct connect_data);
+	case G_OBEX_OP_SETPATH:
+		return sizeof(struct setpath_data);
+	case G_OBEX_OP_DISCONNECT:
+	case G_OBEX_OP_PUT:
+	case G_OBEX_OP_GET:
+	case G_OBEX_OP_SESSION:
+	case G_OBEX_OP_ABORT:
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+static gboolean parse_headers(GObexRequest *req, const void *data, size_t len,
+						GObexDataPolicy data_policy)
+{
+	const guint8 *buf = data;
+
+	while (len > 0) {
+		GObexHeader *header;
+		size_t parsed;
+
+		header = g_obex_header_decode(buf, len, data_policy, &parsed);
+		if (header == NULL)
+			return FALSE;
+
+		req->headers = g_slist_append(req->headers, header);
+
+		len -= parsed;
+		buf += parsed;
+	}
+
+	return TRUE;
+}
+
+GObexRequest *g_obex_request_decode(const void *data, size_t len,
+						GObexDataPolicy data_policy)
+{
+	const guint8 *buf = data;
+	guint16 packet_len;
+	guint8 opcode;
+	ssize_t header_offset;
+	GObexRequest *req;
+
+	if (len < 3)
+		return NULL;
+
+	buf = get_bytes(&opcode, buf, sizeof(opcode));
+	buf = get_bytes(&packet_len, buf, sizeof(packet_len));
+
+	packet_len = g_ntohs(packet_len);
+	if (packet_len < len)
+		return NULL;
+
+	header_offset = get_header_offset(opcode);
+	if (header_offset < 0)
+		return NULL;
+
+	req = g_obex_request_new(opcode);
+
+	req->data_policy = data_policy;
+
+	if (header_offset == 0)
+		goto headers;
+
+	if (3 + header_offset < (ssize_t) len)
+		goto failed;
+
+	req->req_data_len = header_offset;
+	switch (data_policy) {
+	case G_OBEX_DATA_COPY:
+		req->req.data = g_malloc(header_offset);
+		buf = get_bytes(req->req.data, buf, header_offset);
+		break;
+	case G_OBEX_DATA_REF:
+		req->req.data_ref = buf;
+		buf += header_offset;
+		break;
+	default:
+		goto failed;
+	}
+
+headers:
+	if (!parse_headers(req, buf, len - (buf - (guint8 *) data),
+								data_policy))
+		goto failed;
+
+	return req;
+
+failed:
+	g_obex_request_free(req);
+	return NULL;
+}
+
 gboolean g_obex_send(GObex *obex, GObexRequest *req)
 {
 	if (obex == NULL || req == NULL)
diff --git a/gobex/gobex.h b/gobex/gobex.h
index ae34d48..3fa2439 100644
--- a/gobex/gobex.h
+++ b/gobex/gobex.h
@@ -88,6 +88,9 @@ gboolean g_obex_request_add_header(GObexRequest *req, GObexHeader *header);
 GObexRequest *g_obex_request_new(guint8 opcode);
 void g_obex_request_free(GObexRequest *req);
 
+GObexRequest *g_obex_request_decode(const void *data, size_t len,
+						GObexDataPolicy data_policy);
+
 gboolean g_obex_send(GObex *obex, GObexRequest *req);
 
 GObex *g_obex_new(GIOChannel *io);
diff --git a/unit/test-gobex.c b/unit/test-gobex.c
index 8504a7e..ddbf9ac 100644
--- a/unit/test-gobex.c
+++ b/unit/test-gobex.c
@@ -149,6 +149,17 @@ static void test_header_uint32(void)
 	g_obex_header_free(header);
 }
 
+static void test_decode_req(void)
+{
+	GObexRequest *req;
+	uint8_t buf[] = { G_OBEX_OP_PUT, 0x00, 0x03 };
+
+	req = g_obex_request_decode(buf, sizeof(buf), G_OBEX_DATA_REF);
+	g_assert(req != NULL);
+
+	g_obex_request_free(req);
+}
+
 static void parse_and_encode(uint8_t *buf, size_t buf_len)
 {
 	GObexHeader *header;
@@ -411,6 +422,8 @@ int main(int argc, char *argv[])
 	g_test_add_func("/gobex/test_header_uint8", test_header_uint8);
 	g_test_add_func("/gobex/test_header_uint32", test_header_uint32);
 
+	g_test_add_func("/gobex/test_decode_req", test_decode_req);
+
 	g_test_run();
 
 	return 0;