From 8005be3b8a0146f5231f6c34824b344545504931 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Jun 2011 14:08:33 +0300 Subject: [PATCH] gobex: Add basic packet sending support --- gobex/gobex.c | 105 +++++++++++++++++++++++++++++++++++++++++ unit/test-gobex.c | 117 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 217 insertions(+), 5 deletions(-) diff --git a/gobex/gobex.c b/gobex/gobex.c index 80a8bc473..485b91d5d 100644 --- a/gobex/gobex.c +++ b/gobex/gobex.c @@ -21,6 +21,7 @@ #include #include +#include #include "gobex.h" @@ -77,6 +78,12 @@ struct _GObex { size_t rx_data; guint16 rx_pkt_len; + guint8 *tx_buf; + size_t tx_data; + size_t tx_sent; + + guint write_source; + guint16 rx_mtu; guint16 tx_mtu; @@ -536,13 +543,108 @@ failed: return NULL; } +static ssize_t g_obex_packet_encode(GObexPacket *pkt, uint8_t *buf, size_t len) +{ + size_t count; + guint16 pkt_len, u16; + GSList *l; + + pkt_len = 3 + pkt->data_len + pkt->hlen; + + if (pkt_len > len) + return -ENOBUFS; + + buf[0] = pkt->opcode; + if (pkt->final) + buf[0] |= G_OBEX_FINAL; + + 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); + else + memcpy(&buf[3], pkt->data.buf, pkt->data_len); + } + + count = 3 + pkt->data_len; + + for (l = pkt->headers; l != NULL; l = g_slist_next(l)) { + GObexHeader *hdr = l->data; + count += g_obex_header_encode(hdr, buf + count, len - count); + } + + g_assert_cmpuint(count, ==, pkt_len); + + return count; +} + +static gboolean write_data(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + GObex *obex = user_data; + GIOStatus status; + gsize bytes_written; + gchar *buf; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) + goto done; + + if (obex->tx_data == 0) { + GObexPacket *pkt = g_queue_pop_head(obex->req_queue); + ssize_t len; + + if (pkt == NULL) + goto done; + + len = g_obex_packet_encode(pkt, obex->tx_buf, obex->tx_mtu); + + g_obex_packet_free(pkt); + + if (len < 0) + goto done; + + obex->tx_data = len; + obex->tx_sent = 0; + } + + buf = (gchar *) &obex->tx_buf[obex->tx_sent]; + status = g_io_channel_write_chars(io, buf, obex->tx_data, + &bytes_written, NULL); + if (status != G_IO_STATUS_NORMAL) + goto done; + + obex->tx_sent += bytes_written; + obex->tx_data -= bytes_written; + + if (obex->tx_data > 0 || g_queue_get_length(obex->req_queue) > 0) + return TRUE; + +done: + obex->tx_data = 0; + obex->write_source = 0; + return FALSE; +} + gboolean g_obex_send(GObex *obex, GObexPacket *pkt) { + GIOCondition cond; + if (obex == NULL || pkt == NULL) return FALSE; g_queue_push_tail(obex->req_queue, pkt); + if (g_queue_get_length(obex->req_queue) > 1) + return TRUE; + + cond = G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + obex->write_source = g_io_add_watch(obex->io, cond, write_data, obex); + return TRUE; } @@ -658,8 +760,10 @@ GObex *g_obex_new(GIOChannel *io) obex->tx_mtu = G_OBEX_MINIMUM_MTU; obex->req_queue = g_queue_new(); obex->rx_buf = g_malloc(obex->rx_mtu); + obex->tx_buf = g_malloc(obex->tx_mtu); g_io_channel_set_encoding(io, NULL, NULL); + g_io_channel_set_buffered(io, FALSE); cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; obex->io_source = g_io_add_watch(io, cond, incoming_data, obex); @@ -694,6 +798,7 @@ void g_obex_unref(GObex *obex) g_source_remove(obex->io_source); g_free(obex->rx_buf); + g_free(obex->tx_buf); g_free(obex); } diff --git a/unit/test-gobex.c b/unit/test-gobex.c index 99170afd3..368a602f7 100644 --- a/unit/test-gobex.c +++ b/unit/test-gobex.c @@ -76,15 +76,20 @@ static void dump_bytes(uint8_t *buf, size_t buf_len) g_printerr("\n"); } -static void assert_memequal(void *mem1, size_t len1, void *mem2, size_t len2) +static void dump_bufs(void *mem1, size_t len1, void *mem2, size_t len2) { - if (len1 == len2 && memcmp(mem1, mem2, len1) == 0) - return; - g_printerr("\nExpected: "); dump_bytes(mem1, len1); g_printerr("Got: "); dump_bytes(mem2, len2); +} + +static void assert_memequal(void *mem1, size_t len1, void *mem2, size_t len2) +{ + if (len1 == len2 && memcmp(mem1, mem2, len1) == 0) + return; + + dump_bufs(mem1, len1, mem2, len2); g_assert(0); } @@ -103,6 +108,98 @@ 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 void test_send_connect_stream(void) +{ + guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 }; + GError *gerr = NULL; + GIOChannel *io; + GIOCondition cond; + GObexPacket *req; + guint io_id, timer_id; + GObex *obex; + int sv[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sv) < 0) { + g_printerr("socketpair: %s", strerror(errno)); + abort(); + } + + obex = create_gobex(sv[0]); + g_assert(obex != NULL); + + 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); + + io = g_io_channel_unix_new(sv[1]); + g_io_channel_set_encoding(io, NULL, NULL); + g_io_channel_set_buffered(io, FALSE); + 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); + + mainloop = g_main_loop_new(NULL, FALSE); + + timer_id = g_timeout_add_seconds(1, test_timeout, &gerr); + + 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(gerr); +} + static void handle_connect_request(GObex *obex, GObexPacket *pkt, gpointer user_data) { @@ -123,6 +220,7 @@ static void handle_connect_request(GObex *obex, GObexPacket *pkt, static void test_recv_connect_stream(void) { GError *gerr = NULL; + guint timer_id; GObex *obex; ssize_t err; int sv[2]; @@ -142,10 +240,17 @@ static void test_recv_connect_stream(void) mainloop = g_main_loop_new(NULL, FALSE); - g_timeout_add_seconds(1, test_timeout, &gerr); + timer_id = g_timeout_add_seconds(1, test_timeout, &gerr); g_main_loop_run(mainloop); + g_source_remove(timer_id); + g_obex_unref(obex); + + g_main_loop_unref(mainloop); + mainloop = NULL; + + g_assert_no_error(gerr); } @@ -510,6 +615,8 @@ int main(int argc, char *argv[]) g_test_add_func("/gobex/test_recv_connect_stream", test_recv_connect_stream); + g_test_add_func("/gobex/test_send_connect_stream", + test_send_connect_stream); g_test_run(); -- 2.47.3