size_t hlen; /* Length of all encoded headers */
GSList *headers;
+
+ guint id;
+ GObexResponseFunc rsp_func;
+ gpointer rsp_data;
};
struct _GObex {
GObexRequestFunc req_func;
gpointer req_func_data;
+
+ struct pending_req *pending_req;
+};
+
+struct pending_req {
+ guint id;
+ guint8 opcode;
+ GObexResponseFunc rsp_func;
+ gpointer rsp_data;
};
struct connect_data {
return header;
}
+guint g_obex_packet_set_response_function(GObexPacket *pkt,
+ GObexResponseFunc func,
+ gpointer user_data)
+{
+ static guint next_id = 1;
+
+ pkt->rsp_func = func;
+ pkt->rsp_data = user_data;
+ pkt->id = next_id++;
+
+ return pkt->id;
+}
+
guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final)
{
if (final)
g_free(pkt);
}
-static ssize_t get_header_offset(guint8 opcode)
+static ssize_t req_header_offset(guint8 opcode)
{
switch (opcode) {
case G_OBEX_OP_CONNECT:
}
}
+static ssize_t rsp_header_offset(guint8 opcode)
+{
+ switch (opcode) {
+ case G_OBEX_OP_CONNECT:
+ return sizeof(struct connect_data);
+ case G_OBEX_OP_SETPATH:
+ 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(GObexPacket *pkt, const void *data, size_t len,
GObexDataPolicy data_policy)
{
}
GObexPacket *g_obex_packet_decode(const void *data, size_t len,
+ size_t header_offset,
GObexDataPolicy data_policy)
{
const guint8 *buf = data;
guint16 packet_len;
guint8 opcode;
- ssize_t header_offset;
GObexPacket *pkt;
gboolean final;
final = (opcode & G_OBEX_FINAL) ? TRUE : FALSE;
opcode &= ~G_OBEX_FINAL;
- header_offset = get_header_offset(opcode);
- if (header_offset < 0)
- return NULL;
-
pkt = g_obex_packet_new(opcode, final);
if (header_offset == 0)
goto headers;
- if (3 + header_offset < (ssize_t) len)
+ if (3 + header_offset < len)
goto failed;
if (data_policy == G_OBEX_DATA_INHERIT)
return count;
}
+static void pending_req_free(struct pending_req *req)
+{
+ g_free(req);
+}
+
+static struct pending_req *pending_req_new(GObexPacket *pkt)
+{
+ struct pending_req *req;
+
+ req = g_new0(struct pending_req, 1);
+
+ req->id = pkt->id;
+ req->rsp_func = pkt->rsp_func;
+ req->rsp_data = pkt->rsp_data;
+ req->opcode = pkt->opcode;
+
+ return req;
+}
+
static gboolean write_data(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
if (pkt == NULL)
goto done;
+ /* Can't send a request while there's a pending one */
+ if (obex->pending_req && pkt->id > 0)
+ goto done;
+
len = g_obex_packet_encode(pkt, obex->tx_buf, obex->tx_mtu);
+ if (len < 0) {
+ g_obex_packet_free(pkt);
+ goto done;
+ }
- g_obex_packet_free(pkt);
+ if (pkt->id > 0)
+ obex->pending_req = pending_req_new(pkt);
- if (len < 0)
- goto done;
+ g_obex_packet_free(pkt);
obex->tx_data = len;
obex->tx_sent = 0;
return TRUE;
}
+guint g_obex_send_req(GObex *obex, GObexPacket *req, GObexResponseFunc func,
+ gpointer user_data)
+{
+ guint id;
+
+ id = g_obex_packet_set_response_function(req, func, user_data);
+
+ if (!g_obex_send(obex, req))
+ return 0;
+
+ return id;
+}
+
+gboolean g_obex_cancel_req(GObex *obex, guint req_id)
+{
+ return TRUE;
+}
+
void g_obex_set_request_function(GObex *obex, GObexRequestFunc func,
gpointer user_data)
{
obex->req_func_data = user_data;
}
-static gboolean g_obex_handle_packet(GObex *obex, GObexPacket *pkt)
+static void handle_response(GObex *obex, GObexPacket *rsp)
+{
+ struct pending_req *req = obex->pending_req;
+
+ if (req->rsp_func)
+ req->rsp_func(obex, NULL, rsp, req->rsp_data);
+
+ pending_req_free(req);
+ obex->pending_req = NULL;
+}
+
+static void handle_request(GObex *obex, GObexPacket *req)
{
if (obex->req_func)
- obex->req_func(obex, pkt, obex->req_func_data);
+ obex->req_func(obex, req, obex->req_func_data);
+}
+
+static gboolean g_obex_handle_packet(GObex *obex, GObexPacket *pkt)
+{
+ if (obex->pending_req)
+ handle_response(obex, pkt);
+ else
+ handle_request(obex, pkt);
return TRUE;
}
{
GObex *obex = user_data;
GObexPacket *pkt;
+ ssize_t header_offset;
if (cond & G_IO_NVAL)
return FALSE;
if (obex->rx_data < 3 || obex->rx_data < obex->rx_pkt_len)
return TRUE;
- pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data,
+ if (obex->pending_req)
+ header_offset = rsp_header_offset(obex->pending_req->opcode);
+ else {
+ guint8 opcode = obex->rx_buf[0] & ~G_OBEX_FINAL;
+ header_offset = req_header_offset(opcode);
+ }
+
+ if (header_offset < 0)
+ goto failed;
+
+ pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data, header_offset,
G_OBEX_DATA_REF);
if (pkt == NULL) {
/* FIXME: Handle decoding error */
g_free(obex->rx_buf);
g_free(obex->tx_buf);
+ if (obex->pending_req)
+ pending_req_free(obex->pending_req);
+
g_free(obex);
}
typedef void (*GObexRequestFunc) (GObex *obex, GObexPacket *req,
gpointer user_data);
+typedef void (*GObexResponseFunc) (GObex *obex, GError *err, GObexPacket *rsp,
+ gpointer user_data);
GObexHeader *g_obex_header_unicode(guint8 id, const char *str);
GObexHeader *g_obex_header_bytes(guint8 id, void *data, size_t len,
GObexDataPolicy data_policy, size_t *parsed);
void g_obex_header_free(GObexHeader *header);
+guint g_obex_packet_set_response_function(GObexPacket *pkt,
+ GObexResponseFunc func,
+ gpointer user_data);
guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final);
gboolean g_obex_packet_add_header(GObexPacket *pkt, GObexHeader *header);
gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, size_t len,
void g_obex_packet_free(GObexPacket *pkt);
GObexPacket *g_obex_packet_decode(const void *data, size_t len,
+ size_t header_offset,
GObexDataPolicy data_policy);
gboolean g_obex_send(GObex *obex, GObexPacket *pkt);
+guint g_obex_send_req(GObex *obex, GObexPacket *req, GObexResponseFunc func,
+ gpointer user_data);
+gboolean g_obex_cancel_req(GObex *obex, guint req_id);
+
void g_obex_set_request_function(GObex *obex, GObexRequestFunc func,
gpointer user_data);
static uint8_t pkt_connect_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT,
0x00, 0x07, 0x10, 0x00, 0x10, 0x00 };
+static uint8_t pkt_connect_rsp[] = { 0x10 | FINAL_BIT, 0x00, 0x07,
+ 0x10, 0x00, 0x10, 0x00 };
static uint8_t hdr_connid[] = { G_OBEX_HDR_ID_CONNECTION, 1, 2, 3, 4 };
static uint8_t hdr_name_ascii[] = { G_OBEX_HDR_ID_NAME, 0x00, 0x0b,
g_io_channel_set_close_on_unref(*io, TRUE);
}
+static void connect_rsp(GObex *obex, GError *err, GObexPacket *rsp,
+ gpointer user_data)
+{
+ guint8 rsp_code;
+ gboolean final;
+ GError **test_err = user_data;
+
+ if (err != NULL) {
+ g_assert(*test_err == NULL);
+ *test_err = g_error_copy(err);
+ goto done;
+ }
+
+ rsp_code = g_obex_packet_get_operation(rsp, &final);
+ if (rsp_code != 0x10) {
+ g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+ "Unexpected response 0x%02x", rsp_code);
+ goto done;
+ }
+
+ if (!final) {
+ g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+ "Connect response didn't have final bit");
+ goto done;
+ }
+
+done:
+ g_main_loop_quit(mainloop);
+}
+
+static gboolean send_connect_rsp(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ GError **err = user_data;
+ gsize bytes_written, rbytes;
+ char buf[255];
+ GIOStatus status;
+
+ 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,
+ "read failed with status %d", status);
+ goto failed;
+ }
+
+ g_io_channel_write_chars(io, (gchar *) pkt_connect_rsp,
+ sizeof(pkt_connect_rsp),
+ &bytes_written, NULL);
+ if (bytes_written != sizeof(pkt_connect_rsp)) {
+ g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+ "Unable to write to socket");
+ goto failed;
+ }
+
+ return FALSE;
+
+failed:
+ g_main_loop_quit(mainloop);
+ return FALSE;
+}
+
+static void test_send_req_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;
+
+ create_endpoints(&obex, &io, SOCK_STREAM);
+
+ 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, connect_rsp, &gerr);
+
+ cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+ io_id = g_io_add_watch(io, cond, send_connect_rsp, &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 test_send_connect_stream(void)
{
guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
GObexPacket *pkt;
uint8_t buf[] = { G_OBEX_OP_PUT, 0x00, 0x03 };
- pkt = g_obex_packet_decode(buf, sizeof(buf), G_OBEX_DATA_REF);
+ pkt = g_obex_packet_decode(buf, sizeof(buf), 0, G_OBEX_DATA_REF);
g_assert(pkt != NULL);
g_obex_packet_free(pkt);
test_recv_connect_stream);
g_test_add_func("/gobex/test_send_connect_stream",
test_send_connect_stream);
+ g_test_add_func("/gobex/test_send_req_stream",
+ test_send_req_stream);
g_test_run();