From e8c4fe5aa709e74170e50675b4fea2008168249a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Jun 2011 14:48:54 +0300 Subject: [PATCH] gobex: Add basic header parsing support --- gobex/gobex.c | 123 ++++++++++++++++++++++++++++++++++++++++++ gobex/gobex.h | 5 ++ gobex/obex.h | 26 +++++++++ unit/test-gobex.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 286 insertions(+) diff --git a/gobex/gobex.c b/gobex/gobex.c index ef0feef70..5546d8297 100644 --- a/gobex/gobex.c +++ b/gobex/gobex.c @@ -19,8 +19,31 @@ * */ +#include + #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; @@ -33,6 +56,106 @@ struct _GObex { 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 3910367e4..e189616ae 100644 --- a/gobex/gobex.h +++ b/gobex/gobex.h @@ -29,6 +29,11 @@ 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 45ef994cd..f5afe6ae9 100644 --- a/gobex/obex.h +++ b/gobex/obex.h @@ -32,4 +32,30 @@ #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 a1fabf268..480847db7 100644 --- a/unit/test-gobex.c +++ b/unit/test-gobex.c @@ -23,6 +23,12 @@ #include +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; @@ -33,6 +39,119 @@ static GObex *create_gobex(int fd) 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; @@ -88,6 +207,19 @@ int main(int argc, char *argv[]) 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; -- 2.47.3