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
guint8 u8;
guint32 u32;
} v;
+
+ GObexHeaderDataFunc get_data;
+ gpointer get_data_data;
};
static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) {
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;
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)
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
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);
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
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);
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
*/
#include <stdint.h>
+#include <string.h>
#include <gobex/gobex-header.h>
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;
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
*/
#include <stdint.h>
+#include <string.h>
#include <gobex/gobex-packet.h>
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 };
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);
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
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)
{
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)
{
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);
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);
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;
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);
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)
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)
{
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",
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",