Diff between a14e52b4712a0457fe79e41608f876f77b04f3c1 and ad811dd51eaee735446d7b53a37cdc1c3d029717

Changed Files

File Additions Deletions Status
gobex/gobex-header.c +38 -0 modified
gobex/gobex-header.h +6 -0 modified
gobex/gobex-packet.c +6 -8 modified
unit/test-gobex-header.c +29 -0 modified
unit/test-gobex-packet.c +40 -0 modified
unit/test-gobex.c +147 -59 modified

Full Patch

diff --git a/gobex/gobex-header.c b/gobex/gobex-header.c
index ff24589..0ef5e24 100644
--- a/gobex/gobex-header.c
+++ b/gobex/gobex-header.c
@@ -43,6 +43,9 @@ struct _GObexHeader {
 		guint8 u8;
 		guint32 u32;
 	} v;
+
+	GObexHeaderDataFunc get_data;
+	gpointer get_data_data;
 };
 
 static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) {
@@ -80,6 +83,20 @@ static const guint8 *get_bytes(void *to, const guint8 *from, gsize count)
 	return (from + count);
 }
 
+static gsize get_data(GObexHeader *header, guint8 *buf, gsize len)
+{
+	guint16 u16;
+
+	header->vlen = header->get_data(header, buf + 2, len - 2,
+						header->get_data_data);
+
+	header->hlen = header->vlen + 3;
+	u16 = g_htons(header->hlen);
+	memcpy(buf, &u16, sizeof(u16));
+
+	return header->hlen;
+}
+
 gsize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len)
 {
 	guint8 *ptr = buf;
@@ -105,6 +122,9 @@ gsize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len)
 		g_free(utf16);
 		break;
 	case G_OBEX_HDR_TYPE_BYTES:
+		if (header->get_data)
+			return get_data(header, ptr, buf_len - 1);
+
 		u16 = g_htons(header->hlen);
 		ptr = put_bytes(ptr, &u16, sizeof(u16));
 		if (header->extdata)
@@ -362,6 +382,24 @@ GObexHeader *g_obex_header_new_bytes(guint8 id, void *data, gsize len,
 	return header;
 }
 
+GObexHeader *g_obex_header_new_on_demand(guint8 id, GObexHeaderDataFunc func,
+							gpointer user_data)
+{
+	GObexHeader *header;
+
+	if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_BYTES)
+		return NULL;
+
+	header = g_new0(GObexHeader, 1);
+
+	header->id = id;
+	header->hlen = 3;
+	header->get_data = func;
+	header->get_data_data = user_data;
+
+	return header;
+}
+
 GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val)
 {
 	GObexHeader *header;
diff --git a/gobex/gobex-header.h b/gobex/gobex-header.h
index 1df7f14..e7f16e5 100644
--- a/gobex/gobex-header.h
+++ b/gobex/gobex-header.h
@@ -55,6 +55,9 @@
 
 typedef struct _GObexHeader GObexHeader;
 
+typedef guint16 (*GObexHeaderDataFunc) (GObexHeader *header, void *buf,
+						gsize len, gpointer user_data);
+
 gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str);
 gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val,
 								gsize *len);
@@ -64,6 +67,9 @@ gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val);
 GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str);
 GObexHeader *g_obex_header_new_bytes(guint8 id, void *data, gsize len,
 						GObexDataPolicy data_policy);
+GObexHeader *g_obex_header_new_on_demand(guint8 id,
+						GObexHeaderDataFunc func,
+						gpointer user_data);
 GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val);
 GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val);
 
diff --git a/gobex/gobex-packet.c b/gobex/gobex-packet.c
index a74b129..89f14e4 100644
--- a/gobex/gobex-packet.c
+++ b/gobex/gobex-packet.c
@@ -237,21 +237,16 @@ failed:
 gssize g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, gsize len)
 {
 	gsize count;
-	guint16 pkt_len, u16;
+	guint16 u16;
 	GSList *l;
 
-	pkt_len = 3 + pkt->data_len + pkt->hlen;
-
-	if (pkt_len > len)
+	if (3 + pkt->data_len + pkt->hlen > len)
 		return -ENOBUFS;
 
 	buf[0] = pkt->opcode;
 	if (pkt->final)
 		buf[0] |= FINAL_BIT;
 
-	u16 = g_htons(pkt_len);
-	memcpy(&buf[1], &u16, sizeof(u16));
-
 	if (pkt->data_len > 0) {
 		if (pkt->data_policy == G_OBEX_DATA_REF)
 			memcpy(&buf[3], pkt->data.buf_ref, pkt->data_len);
@@ -263,10 +258,13 @@ gssize g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, gsize len)
 
 	for (l = pkt->headers; l != NULL; l = g_slist_next(l)) {
 		GObexHeader *hdr = l->data;
+		if (count >= len)
+			return -ENOBUFS;
 		count += g_obex_header_encode(hdr, buf + count, len - count);
 	}
 
-	g_assert_cmpuint(count, ==, pkt_len);
+	u16 = g_htons(count);
+	memcpy(&buf[1], &u16, sizeof(u16));
 
 	return count;
 }
diff --git a/unit/test-gobex-header.c b/unit/test-gobex-header.c
index d2dd99a..61536e2 100644
--- a/unit/test-gobex-header.c
+++ b/unit/test-gobex-header.c
@@ -20,6 +20,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <gobex/gobex-header.h>
 
@@ -129,6 +130,32 @@ static void test_header_uint32(void)
 	g_obex_header_free(header);
 }
 
+static guint16 get_body_data(GObexHeader *header, void *buf, gsize len,
+							gpointer user_data)
+{
+	uint8_t body_data[] = { 1, 2, 3, 4 };
+
+	memcpy(buf, body_data, sizeof(body_data));
+
+	return sizeof(body_data);
+}
+
+static void test_header_on_demand(void)
+{
+	GObexHeader *header;
+	uint8_t buf[1024];
+	size_t len;
+
+	header = g_obex_header_new_on_demand(G_OBEX_HDR_ID_BODY,
+						get_body_data, NULL);
+
+	len = g_obex_header_encode(header, buf, sizeof(buf));
+
+	assert_memequal(hdr_body, sizeof(hdr_body), buf, len);
+
+	g_obex_header_free(header);
+}
+
 static GObexHeader *parse_and_encode(uint8_t *buf, size_t buf_len)
 {
 	GObexHeader *header;
@@ -457,6 +484,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_header_on_demand", test_header_on_demand);
+
 	g_test_run();
 
 	return 0;
diff --git a/unit/test-gobex-packet.c b/unit/test-gobex-packet.c
index afc8adf..464382a 100644
--- a/unit/test-gobex-packet.c
+++ b/unit/test-gobex-packet.c
@@ -20,6 +20,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <gobex/gobex-packet.h>
 
@@ -31,6 +32,9 @@ static uint8_t pkt_connect[] = { G_OBEX_OP_CONNECT, 0x00, 0x0c,
 						0x00, 0x05, 0xab, 0xcd };
 static uint8_t pkt_put_action[] = { G_OBEX_OP_PUT, 0x00, 0x05,
 					G_OBEX_HDR_ID_ACTION, 0xab };
+static uint8_t pkt_put_body[] = { G_OBEX_OP_PUT, 0x00, 0x0a,
+					G_OBEX_HDR_ID_BODY, 0x00, 0x07,
+					1, 2, 3, 4 };
 static uint8_t pkt_put[] = { G_OBEX_OP_PUT, 0x00, 0x03 };
 
 static uint8_t pkt_nval_len[] = { G_OBEX_OP_PUT, 0xab, 0xcd, 0x12 };
@@ -140,6 +144,40 @@ static void test_decode_encode(void)
 	g_obex_packet_free(pkt);
 }
 
+static guint16 get_body_data(GObexHeader *header, void *buf, gsize len,
+							gpointer user_data)
+{
+	uint8_t data[] = { 1, 2, 3, 4 };
+
+	memcpy(buf, data, sizeof(data));
+
+	return sizeof(data);
+}
+
+static void test_encode_on_demand(void)
+{
+	GObexPacket *pkt;
+	GObexHeader *hdr;
+	uint8_t buf[255];
+	gssize len;
+
+	pkt = g_obex_packet_new(G_OBEX_OP_PUT, FALSE);
+
+	hdr = g_obex_header_new_on_demand(G_OBEX_HDR_ID_BODY,
+						get_body_data, NULL);
+	g_obex_packet_add_header(pkt, hdr);
+
+	len = g_obex_packet_encode(pkt, buf, sizeof(buf));
+	if (len < 0) {
+		g_printerr("Encoding failed: %s\n", g_strerror(-len));
+		g_assert_not_reached();
+	}
+
+	assert_memequal(pkt_put_body, sizeof(pkt_put_body), buf, len);
+
+	g_obex_packet_free(pkt);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -155,6 +193,8 @@ int main(int argc, char *argv[])
 
 	g_test_add_func("/gobex/test_encode_pkt", test_decode_encode);
 
+	g_test_add_func("/gobex/test_encode_on_demand", test_encode_on_demand);
+
 	g_test_run();
 
 	return 0;
diff --git a/unit/test-gobex.c b/unit/test-gobex.c
index 13a0668..2d8630e 100644
--- a/unit/test-gobex.c
+++ b/unit/test-gobex.c
@@ -43,6 +43,9 @@ static uint8_t pkt_nval_connect_rsp[] = { 0x10 | FINAL_BIT, 0x00, 0x05,
 					0x10, 0x00, };
 static uint8_t pkt_abort_rsp[] = { 0x90, 0x00, 0x03 };
 static uint8_t pkt_nval_short_rsp[] = { 0x10 | FINAL_BIT, 0x12 };
+static uint8_t pkt_put_body[] = { G_OBEX_OP_PUT, 0x00, 0x0a,
+					G_OBEX_HDR_ID_BODY, 0x00, 0x07,
+					1, 2, 3, 4 };
 
 static gboolean test_timeout(gpointer user_data)
 {
@@ -58,49 +61,6 @@ static gboolean test_timeout(gpointer user_data)
 	return FALSE;
 }
 
-static gboolean handle_connect_data(GIOChannel *io, GIOCondition cond,
-							gpointer user_data)
-{
-	GError **err = user_data;
-	GIOStatus status;
-	gsize rbytes;
-	char buf[255];
-
-	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
-		g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
-				"Unexpected condition %d on socket", cond);
-		goto done;
-	}
-
-	status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
-	if (status != G_IO_STATUS_NORMAL) {
-		g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
-				"Reading data failed with status %d", status);
-		goto done;
-	}
-
-	if (rbytes != sizeof(pkt_connect_req)) {
-		g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
-				"Got %zu bytes instead of %zu",
-				rbytes, sizeof(pkt_connect_req));
-		dump_bufs(pkt_connect_req, sizeof(pkt_connect_req),
-								buf, rbytes);
-		goto done;
-	}
-
-	if (memcmp(buf, pkt_connect_req, rbytes) != 0) {
-		g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
-				"Mismatch with received data");
-		dump_bufs(pkt_connect_req, sizeof(pkt_connect_req),
-								buf, rbytes);
-		goto done;
-	}
-
-done:
-	g_main_loop_quit(mainloop);
-	return FALSE;
-}
-
 static GObex *create_gobex(int fd, GObexTransportType transport_type,
 						gboolean close_on_unref)
 {
@@ -272,25 +232,18 @@ static gboolean send_nothing(GIOChannel *io, GIOCondition cond,
 	return FALSE;
 }
 
-static void send_connect(GObexResponseFunc rsp_func, GIOFunc send_rsp_func,
-					gint req_timeout, int transport_type)
+static void send_req(GObexPacket *req, GObexResponseFunc rsp_func,
+				GIOFunc send_rsp_func, gint req_timeout,
+				int transport_type)
 {
-	guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
 	GError *gerr = NULL;
 	GIOChannel *io;
 	GIOCondition cond;
-	GObexPacket *req;
 	guint io_id, timer_id, test_time;
 	GObex *obex;
 
 	create_endpoints(&obex, &io, transport_type);
 
-	req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE);
-	g_assert(req != NULL);
-
-	g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
-							G_OBEX_DATA_REF);
-
 	g_obex_send_req(obex, req, req_timeout, rsp_func, &gerr, &gerr);
 	g_assert_no_error(gerr);
 
@@ -319,6 +272,21 @@ static void send_connect(GObexResponseFunc rsp_func, GIOFunc send_rsp_func,
 	g_assert_no_error(gerr);
 }
 
+static void send_connect(GObexResponseFunc rsp_func, GIOFunc send_rsp_func,
+					gint req_timeout, int transport_type)
+{
+	GObexPacket *req;
+	guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
+
+	req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE);
+	g_assert(req != NULL);
+
+	g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
+							G_OBEX_DATA_REF);
+
+	send_req(req, rsp_func, send_rsp_func, req_timeout, transport_type);
+}
+
 static void test_send_connect_req_stream(void)
 {
 	send_connect(connect_rsp, send_connect_rsp, -1, SOCK_STREAM);
@@ -498,10 +466,56 @@ static void test_cancel_req_delay_pkt(void)
 	test_cancel_req_delay(SOCK_SEQPACKET);
 }
 
+struct rcv_buf_info {
+	GError *err;
+	const guint8 *buf;
+	gsize len;
+};
+
+static gboolean rcv_data(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+	struct rcv_buf_info *r = user_data;
+	GIOStatus status;
+	gsize rbytes;
+	char buf[255];
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Unexpected condition %d on socket", cond);
+		goto done;
+	}
+
+	status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
+	if (status != G_IO_STATUS_NORMAL) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Reading data failed with status %d", status);
+		goto done;
+	}
+
+	if (rbytes != r->len) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Got %zu bytes instead of %zu",
+				rbytes, sizeof(pkt_connect_req));
+		dump_bufs(r->buf, r->len, buf, rbytes);
+		goto done;
+	}
+
+	if (memcmp(buf, r->buf, rbytes) != 0) {
+		g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+				"Mismatch with received data");
+		dump_bufs(r->buf, r->len, buf, rbytes);
+		goto done;
+	}
+
+done:
+	g_main_loop_quit(mainloop);
+	return FALSE;
+}
+
 static void test_send_connect(int transport_type)
 {
 	guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
-	GError *gerr = NULL;
+	struct rcv_buf_info r;
 	GIOChannel *io;
 	GIOCondition cond;
 	GObexPacket *req;
@@ -510,20 +524,24 @@ static void test_send_connect(int transport_type)
 
 	create_endpoints(&obex, &io, transport_type);
 
+	r.err = NULL;
+	r.buf = pkt_connect_req;
+	r.len = sizeof(pkt_connect_req);
+
 	req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE);
 	g_assert(req != NULL);
 
 	g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
 							G_OBEX_DATA_REF);
-	g_obex_send(obex, req, &gerr);
-	g_assert_no_error(gerr);
+	g_obex_send(obex, req, &r.err);
+	g_assert_no_error(r.err);
 
 	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
-	io_id = g_io_add_watch(io, cond, handle_connect_data, &gerr);
+	io_id = g_io_add_watch(io, cond, rcv_data, &r);
 
 	mainloop = g_main_loop_new(NULL, FALSE);
 
-	timer_id = g_timeout_add_seconds(1, test_timeout, &gerr);
+	timer_id = g_timeout_add_seconds(1, test_timeout, &r.err);
 
 	g_main_loop_run(mainloop);
 
@@ -535,7 +553,7 @@ static void test_send_connect(int transport_type)
 	g_source_remove(io_id);
 	g_obex_unref(obex);
 
-	g_assert_no_error(gerr);
+	g_assert_no_error(r.err);
 }
 
 static void test_send_connect_stream(void)
@@ -548,6 +566,71 @@ static void test_send_connect_pkt(void)
 	test_send_connect(SOCK_SEQPACKET);
 }
 
+static guint16 get_body_data(GObexHeader *header, void *buf, gsize len,
+							gpointer user_data)
+{
+	uint8_t data[] = { 1, 2, 3, 4 };
+
+	memcpy(buf, data, sizeof(data));
+
+	return sizeof(data);
+}
+
+static void test_send_on_demand(int transport_type)
+{
+	struct rcv_buf_info r;
+	GIOChannel *io;
+	GIOCondition cond;
+	GObexPacket *req;
+	GObexHeader *hdr;
+	guint io_id, timer_id;
+	GObex *obex;
+
+	create_endpoints(&obex, &io, transport_type);
+
+	r.err = NULL;
+	r.buf = pkt_put_body;
+	r.len = sizeof(pkt_put_body);
+
+	req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE);
+
+	hdr = g_obex_header_new_on_demand(G_OBEX_HDR_ID_BODY,
+						get_body_data, NULL);
+	g_obex_packet_add_header(req, hdr);
+
+	g_obex_send(obex, req, &r.err);
+	g_assert_no_error(r.err);
+
+	cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+	io_id = g_io_add_watch(io, cond, rcv_data, &r);
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	timer_id = g_timeout_add_seconds(1, test_timeout, &r.err);
+
+	g_main_loop_run(mainloop);
+
+	g_main_loop_unref(mainloop);
+	mainloop = NULL;
+
+	g_source_remove(timer_id);
+	g_io_channel_unref(io);
+	g_source_remove(io_id);
+	g_obex_unref(obex);
+
+	g_assert_no_error(r.err);
+}
+
+static void test_send_on_demand_stream(void)
+{
+	test_send_on_demand(SOCK_STREAM);
+}
+
+static void test_send_on_demand_pkt(void)
+{
+	test_send_on_demand(SOCK_SEQPACKET);
+}
+
 static void handle_connect_event(GObex *obex, GError *err, GObexPacket *pkt,
 							gpointer user_data)
 {
@@ -703,6 +786,10 @@ int main(int argc, char *argv[])
 						test_send_connect_stream);
 	g_test_add_func("/gobex/test_send_connect_pkt",
 						test_send_connect_pkt);
+	g_test_add_func("/gobex/test_send_on_demand_stream",
+						test_send_on_demand_stream);
+	g_test_add_func("/gobex/test_send_on_demand_pkt",
+						test_send_on_demand_pkt);
 	g_test_add_func("/gobex/test_send_connect_req_stream",
 					test_send_connect_req_stream);
 	g_test_add_func("/gobex/test_send_connect_req_pkt",
@@ -718,6 +805,7 @@ int main(int argc, char *argv[])
 	g_test_add_func("/gobex/test_send_connect_req_timeout_pkt",
 					test_send_connect_req_timeout_pkt);
 
+
 	g_test_add_func("/gobex/test_cancel_req_immediate",
 					test_cancel_req_immediate);
 	g_test_add_func("/gobex/test_cancel_req_delay_stream",