diff --git a/gobex/gobex.c b/gobex/gobex.c
index ef0feef..5546d82 100644
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
*
*/
+#include <string.h>
+
#include "gobex.h"
+/* Header types */
+#define G_OBEX_HDR_TYPE_UNICODE (0 << 6)
+#define G_OBEX_HDR_TYPE_BYTES (1 << 6)
+#define G_OBEX_HDR_TYPE_UINT8 (2 << 6)
+#define G_OBEX_HDR_TYPE_UINT32 (3 << 6)
+
+#define G_OBEX_HDR_TYPE(id) ((id) & 0xc0)
+
+struct _GObexHeader {
+ uint8_t id;
+ size_t len;
+ gboolean extdata;
+ union {
+ char *string; /* UTF-8 converted from UTF-16 */
+ uint8_t *data; /* Own buffer */
+ const uint8_t *extdata; /* Reference to external buffer */
+ uint8_t u8;
+ uint32_t u32;
+ } v;
+};
+
struct _GObexRequest {
uint8_t opcode;
GSList *headers;
GQueue *req_queue;
};
+GObexHeader *g_obex_header_parse(const void *data, size_t len,
+ gboolean copy, size_t *parsed)
+{
+ GObexHeader *header;
+ const char *buf = data;
+ uint16_t hdr_len;
+ size_t str_len;
+
+ if (len < 2)
+ return NULL;
+
+ header = g_new0(GObexHeader, 1);
+
+ header->id = buf[0];
+
+ switch (G_OBEX_HDR_TYPE(header->id)) {
+ case G_OBEX_HDR_TYPE_UNICODE:
+ if (len < 3)
+ goto failed;
+ memcpy(&hdr_len, &buf[1], 2);
+ hdr_len = be16toh(hdr_len);
+ if (hdr_len > len || hdr_len < 5)
+ goto failed;
+
+ header->v.string = g_convert(&buf[3], hdr_len - 5,
+ "UTF8", "UTF16BE",
+ NULL, &str_len, NULL);
+ if (header->v.string == NULL)
+ goto failed;
+
+ header->len = (size_t) str_len;
+
+ *parsed = hdr_len;
+
+ break;
+ case G_OBEX_HDR_TYPE_BYTES:
+ if (len < 3)
+ goto failed;
+ memcpy(&hdr_len, &buf[1], 2);
+ hdr_len = be16toh(hdr_len);
+ if (hdr_len > len)
+ goto failed;
+
+ header->len = hdr_len - 3;
+
+ if (copy) {
+ header->v.data = g_malloc(hdr_len);
+ memcpy(header->v.data, &buf[3], header->len);
+ } else {
+ header->extdata = TRUE;
+ header->v.extdata = (const uint8_t *) &buf[3];
+ }
+
+ *parsed = hdr_len;
+
+ break;
+ case G_OBEX_HDR_TYPE_UINT8:
+ header->len = 1;
+ header->v.u8 = buf[1];
+ *parsed = 2;
+ break;
+ case G_OBEX_HDR_TYPE_UINT32:
+ if (len < 5)
+ goto failed;
+ header->len = 4;
+ memcpy(&header->v.u32, &buf[1], 4);
+ header->v.u32 = be32toh(header->v.u32);
+ *parsed = 5;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return header;
+
+failed:
+ g_obex_header_free(header);
+ return NULL;
+}
+
+void g_obex_header_free(GObexHeader *header)
+{
+ switch (G_OBEX_HDR_TYPE(header->id)) {
+ case G_OBEX_HDR_TYPE_UNICODE:
+ g_free(header->v.string);
+ break;
+ case G_OBEX_HDR_TYPE_BYTES:
+ if (!header->extdata)
+ g_free(header->v.data);
+ break;
+ case G_OBEX_HDR_TYPE_UINT8:
+ case G_OBEX_HDR_TYPE_UINT32:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ g_free(header);
+}
+
GObexRequest *g_obex_request_new(uint8_t opcode)
{
GObexRequest *req;
diff --git a/gobex/gobex.h b/gobex/gobex.h
index 3910367..e189616 100644
--- a/gobex/gobex.h
+++ b/gobex/gobex.h
typedef struct _GObex GObex;
typedef struct _GObexRequest GObexRequest;
+typedef struct _GObexHeader GObexHeader;
+
+GObexHeader *g_obex_header_parse(const void *data, size_t len,
+ gboolean copy, size_t *parsed);
+void g_obex_header_free(GObexHeader *header);
GObexRequest *g_obex_request_new(uint8_t opcode);
void g_obex_request_free(GObexRequest *req);
diff --git a/gobex/obex.h b/gobex/obex.h
index 45ef994..f5afe6a 100644
--- a/gobex/obex.h
+++ b/gobex/obex.h
#define G_OBEX_OP_ABORT 0x7f
#define G_OBEX_FINAL 0x80
+#define G_OBEX_HDR_ID_COUNT 0xc0
+#define G_OBEX_HDR_ID_NAME 0x01
+#define G_OBEX_HDR_ID_TYPE 0x42
+#define G_OBEX_HDR_ID_LENGTH 0xc3
+#define G_OBEX_HDR_ID_TIME 0x44
+#define G_OBEX_HDR_ID_DESCRIPTION 0x05
+#define G_OBEX_HDR_ID_TARGET 0x46
+#define G_OBEX_HDR_ID_HTTP 0x47
+#define G_OBEX_HDR_ID_BODY 0x48
+#define G_OBEX_HDR_ID_BODY_END 0x49
+#define G_OBEX_HDR_ID_WHO 0x4a
+#define G_OBEX_HDR_ID_CONNECTION 0xcb
+#define G_OBEX_HDR_ID_APPARAM 0x4c
+#define G_OBEX_HDR_ID_AUTHCHAL 0x4d
+#define G_OBEX_HDR_ID_AUTHRESP 0x4e
+#define G_OBEX_HDR_ID_CREATOR 0xcf
+#define G_OBEX_HDR_ID_WANUUID 0x50
+#define G_OBEX_HDR_ID_OBJECTCLASS 0x51
+#define G_OBEX_HDR_ID_SESSIONPARAM 0x52
+#define G_OBEX_HDR_ID_SESSIONSEQ 0x93
+#define G_OBEX_HDR_ID_ACTION 0x94
+#define G_OBEX_HDR_ID_DESTNAME 0x15
+#define G_OBEX_HDR_ID_PERMISSIONS 0xd6
+#define G_OBEX_HDR_ID_SRM 0x97
+#define G_OBEX_HDR_ID_SRM_FLAGS 0x98
+
#endif /* __GOBEX_H */
diff --git a/unit/test-gobex.c b/unit/test-gobex.c
index a1fabf2..480847d 100644
--- a/unit/test-gobex.c
+++ b/unit/test-gobex.c
#include <gobex/gobex.h>
+static uint8_t hdr_connid[] = { G_OBEX_HDR_ID_CONNECTION, 1, 2, 3, 4 };
+static uint8_t hdr_name[] = { G_OBEX_HDR_ID_NAME, 0x00, 0x0b,
+ 0x00, 'f', 0x00, 'o', 0x00, 'o', 0x00, 0x00 };
+static uint8_t hdr_body[] = { G_OBEX_HDR_ID_BODY, 0x00, 0x07, 1, 2, 3, 4 };
+static uint8_t hdr_actionid[] = { G_OBEX_HDR_ID_ACTION, 0x00 };
+
static GObex *create_gobex(int fd)
{
GIOChannel *io;
return g_obex_new(io);
}
+static void test_parse_header_connid(void)
+{
+ GObexHeader *header;
+ size_t parsed;
+
+ header = g_obex_header_parse(hdr_connid, sizeof(hdr_connid),
+ FALSE, &parsed);
+ g_assert(header != NULL);
+
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_connid));
+
+ g_obex_header_free(header);
+}
+
+static void test_parse_header_name(void)
+{
+ GObexHeader *header;
+ size_t parsed;
+
+ header = g_obex_header_parse(hdr_name, sizeof(hdr_name),
+ FALSE, &parsed);
+ g_assert(header != NULL);
+
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_name));
+
+ g_obex_header_free(header);
+}
+
+static void test_parse_header_body(void)
+{
+ GObexHeader *header;
+ size_t parsed;
+
+ header = g_obex_header_parse(hdr_body, sizeof(hdr_body),
+ FALSE, &parsed);
+ g_assert(header != NULL);
+
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+
+ g_obex_header_free(header);
+}
+
+static void test_parse_header_body_extdata(void)
+{
+ GObexHeader *header;
+ size_t parsed;
+
+ header = g_obex_header_parse(hdr_body, sizeof(hdr_body),
+ TRUE, &parsed);
+ g_assert(header != NULL);
+
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+
+ g_obex_header_free(header);
+}
+
+static void test_parse_header_actionid(void)
+{
+ GObexHeader *header;
+ size_t parsed;
+
+ header = g_obex_header_parse(hdr_actionid, sizeof(hdr_actionid),
+ FALSE, &parsed);
+ g_assert(header != NULL);
+
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_actionid));
+
+ g_obex_header_free(header);
+}
+
+static void test_parse_header_multi(void)
+{
+ GObexHeader *header;
+ GByteArray *buf;
+ size_t parsed;
+
+ buf = g_byte_array_sized_new(sizeof(hdr_connid) +
+ sizeof(hdr_name) +
+ sizeof(hdr_actionid) +
+ sizeof(hdr_body));
+
+ g_byte_array_append(buf, hdr_connid, sizeof(hdr_connid));
+ g_byte_array_append(buf, hdr_name, sizeof(hdr_name));
+ g_byte_array_append(buf, hdr_actionid, sizeof(hdr_actionid));
+ g_byte_array_append(buf, hdr_body, sizeof(hdr_body));
+
+ header = g_obex_header_parse(buf->data, buf->len, FALSE, &parsed);
+ g_assert(header != NULL);
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_connid));
+ g_byte_array_remove_range(buf, 0, parsed);
+ g_obex_header_free(header);
+
+ header = g_obex_header_parse(buf->data, buf->len, FALSE, &parsed);
+ g_assert(header != NULL);
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_name));
+ g_byte_array_remove_range(buf, 0, parsed);
+ g_obex_header_free(header);
+
+ header = g_obex_header_parse(buf->data, buf->len, FALSE, &parsed);
+ g_assert(header != NULL);
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_actionid));
+ g_byte_array_remove_range(buf, 0, parsed);
+ g_obex_header_free(header);
+
+ header = g_obex_header_parse(buf->data, buf->len, FALSE, &parsed);
+ g_assert(header != NULL);
+ g_assert_cmpuint(parsed, ==, sizeof(hdr_body));
+ g_byte_array_remove_range(buf, 0, parsed);
+ g_obex_header_free(header);
+
+ g_byte_array_unref(buf);
+}
+
static void test_req(void)
{
GObexRequest *req;
g_test_add_func("/gobex/test_req", test_req);
+ g_test_add_func("/gobex/test_parse_header_connid",
+ test_parse_header_connid);
+ g_test_add_func("/gobex/test_parse_header_name",
+ test_parse_header_name);
+ g_test_add_func("/gobex/test_parse_header_body",
+ test_parse_header_body);
+ g_test_add_func("/gobex/test_parse_header_body_extdata",
+ test_parse_header_body_extdata);
+ g_test_add_func("/gobex/test_parse_header_actionid",
+ test_parse_header_actionid);
+ g_test_add_func("/gobex/test_parse_header_multi",
+ test_parse_header_multi);
+
g_test_run();
return 0;