OSDN Git Service

gobex: Add basic request decoding support
authorJohan Hedberg <johan.hedberg@intel.com>
Sun, 26 Jun 2011 20:06:16 +0000 (23:06 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 4 Dec 2012 21:21:56 +0000 (22:21 +0100)
gobex/gobex.c
gobex/gobex.h
unit/test-gobex.c

index 428fbda..3c70337 100644 (file)
@@ -19,6 +19,7 @@
  *
  */
 
+#include <unistd.h>
 #include <string.h>
 
 #include "gobex.h"
@@ -47,6 +48,15 @@ struct _GObexHeader {
 
 struct _GObexRequest {
        guint8 opcode;
+
+       GObexDataPolicy data_policy;
+
+       union {
+               void *data;             /* Non-header data */
+               const void *data_ref;   /* Reference to non-header data */
+       } req;
+       size_t req_data_len;
+
        size_t hlen;            /* Length of all encoded headers */
        GSList *headers;
 };
@@ -58,6 +68,17 @@ struct _GObex {
        GQueue *req_queue;
 };
 
+struct connect_data {
+       guint8 version;
+       guint8 flags;
+       guint16 mtu;
+} __attribute__ ((packed));
+
+struct setpath_data {
+       guint8 flags;
+       guint8 constants;
+} __attribute__ ((packed));
+
 static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) {
        glong utf16_len;
        int i;
@@ -348,16 +369,126 @@ GObexRequest *g_obex_request_new(guint8 opcode)
 
        req->opcode = opcode;
 
+       req->data_policy = G_OBEX_DATA_COPY;
+
        return req;
 }
 
 void g_obex_request_free(GObexRequest *req)
 {
+       switch (req->data_policy) {
+       case G_OBEX_DATA_INHERIT:
+       case G_OBEX_DATA_COPY:
+               g_free(req->req.data);
+               break;
+       case G_OBEX_DATA_REF:
+               break;
+       }
+
        g_slist_foreach(req->headers, (GFunc) g_obex_header_free, NULL);
        g_slist_free(req->headers);
        g_free(req);
 }
 
+static ssize_t get_header_offset(guint8 opcode)
+{
+       switch (opcode) {
+       case G_OBEX_OP_CONNECT:
+               return sizeof(struct connect_data);
+       case G_OBEX_OP_SETPATH:
+               return sizeof(struct setpath_data);
+       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(GObexRequest *req, const void *data, size_t len,
+                                               GObexDataPolicy data_policy)
+{
+       const guint8 *buf = data;
+
+       while (len > 0) {
+               GObexHeader *header;
+               size_t parsed;
+
+               header = g_obex_header_decode(buf, len, data_policy, &parsed);
+               if (header == NULL)
+                       return FALSE;
+
+               req->headers = g_slist_append(req->headers, header);
+
+               len -= parsed;
+               buf += parsed;
+       }
+
+       return TRUE;
+}
+
+GObexRequest *g_obex_request_decode(const void *data, size_t len,
+                                               GObexDataPolicy data_policy)
+{
+       const guint8 *buf = data;
+       guint16 packet_len;
+       guint8 opcode;
+       ssize_t header_offset;
+       GObexRequest *req;
+
+       if (len < 3)
+               return NULL;
+
+       buf = get_bytes(&opcode, buf, sizeof(opcode));
+       buf = get_bytes(&packet_len, buf, sizeof(packet_len));
+
+       packet_len = g_ntohs(packet_len);
+       if (packet_len < len)
+               return NULL;
+
+       header_offset = get_header_offset(opcode);
+       if (header_offset < 0)
+               return NULL;
+
+       req = g_obex_request_new(opcode);
+
+       req->data_policy = data_policy;
+
+       if (header_offset == 0)
+               goto headers;
+
+       if (3 + header_offset < (ssize_t) len)
+               goto failed;
+
+       req->req_data_len = header_offset;
+       switch (data_policy) {
+       case G_OBEX_DATA_COPY:
+               req->req.data = g_malloc(header_offset);
+               buf = get_bytes(req->req.data, buf, header_offset);
+               break;
+       case G_OBEX_DATA_REF:
+               req->req.data_ref = buf;
+               buf += header_offset;
+               break;
+       default:
+               goto failed;
+       }
+
+headers:
+       if (!parse_headers(req, buf, len - (buf - (guint8 *) data),
+                                                               data_policy))
+               goto failed;
+
+       return req;
+
+failed:
+       g_obex_request_free(req);
+       return NULL;
+}
+
 gboolean g_obex_send(GObex *obex, GObexRequest *req)
 {
        if (obex == NULL || req == NULL)
index ae34d48..3fa2439 100644 (file)
@@ -88,6 +88,9 @@ gboolean g_obex_request_add_header(GObexRequest *req, GObexHeader *header);
 GObexRequest *g_obex_request_new(guint8 opcode);
 void g_obex_request_free(GObexRequest *req);
 
+GObexRequest *g_obex_request_decode(const void *data, size_t len,
+                                               GObexDataPolicy data_policy);
+
 gboolean g_obex_send(GObex *obex, GObexRequest *req);
 
 GObex *g_obex_new(GIOChannel *io);
index 8504a7e..ddbf9ac 100644 (file)
@@ -149,6 +149,17 @@ static void test_header_uint32(void)
        g_obex_header_free(header);
 }
 
+static void test_decode_req(void)
+{
+       GObexRequest *req;
+       uint8_t buf[] = { G_OBEX_OP_PUT, 0x00, 0x03 };
+
+       req = g_obex_request_decode(buf, sizeof(buf), G_OBEX_DATA_REF);
+       g_assert(req != NULL);
+
+       g_obex_request_free(req);
+}
+
 static void parse_and_encode(uint8_t *buf, size_t buf_len)
 {
        GObexHeader *header;
@@ -411,6 +422,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_decode_req", test_decode_req);
+
        g_test_run();
 
        return 0;