#include <unistd.h>
#include <string.h>
+#include <errno.h>
#include "gobex.h"
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;
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;
}
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);
g_source_remove(obex->io_source);
g_free(obex->rx_buf);
+ g_free(obex->tx_buf);
g_free(obex);
}
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);
}
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)
{
static void test_recv_connect_stream(void)
{
GError *gerr = NULL;
+ guint timer_id;
GObex *obex;
ssize_t err;
int sv[2];
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);
}
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();