From b105277b9726903f1eea33d6b0046e832406d539 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Jun 2011 23:32:53 +0300 Subject: [PATCH] gobex: Split gobex.c into multiple modules --- gobex/gobex-defs.h | 31 +++ gobex/gobex-header.c | 376 ++++++++++++++++++++++++++++ gobex/gobex-header.h | 78 ++++++ gobex/gobex-packet.c | 261 +++++++++++++++++++ gobex/gobex-packet.h | 59 +++++ gobex/gobex.c | 577 +------------------------------------------ gobex/gobex.h | 75 +----- unit/test-gobex.c | 1 + 8 files changed, 808 insertions(+), 650 deletions(-) create mode 100644 gobex/gobex-defs.h create mode 100644 gobex/gobex-header.c create mode 100644 gobex/gobex-header.h create mode 100644 gobex/gobex-packet.c create mode 100644 gobex/gobex-packet.h diff --git a/gobex/gobex-defs.h b/gobex/gobex-defs.h new file mode 100644 index 000000000..ad8b046fd --- /dev/null +++ b/gobex/gobex-defs.h @@ -0,0 +1,31 @@ +/* + * + * OBEX library with GLib integration + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GOBEX_DEFS_H +#define __GOBEX_DEFS_H + +typedef enum { + G_OBEX_DATA_INHERIT, + G_OBEX_DATA_COPY, + G_OBEX_DATA_REF, +} GObexDataPolicy; + +#endif /* __GOBEX_DEFS_H */ diff --git a/gobex/gobex-header.c b/gobex/gobex-header.c new file mode 100644 index 000000000..5b0a50268 --- /dev/null +++ b/gobex/gobex-header.c @@ -0,0 +1,376 @@ +/* + * + * OBEX library with GLib integration + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include "gobex-header.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 { + guint8 id; + gboolean extdata; + size_t vlen; /* Length of value */ + size_t hlen; /* Length of full encoded header */ + union { + char *string; /* UTF-8 converted from UTF-16 */ + guint8 *data; /* Own buffer */ + const guint8 *extdata; /* Reference to external buffer */ + guint8 u8; + guint32 u32; + } v; +}; + +static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) { + glong utf16_len; + int i; + + if (*utf8 == '\0') { + *utf16 = NULL; + return 0; + } + + *utf16 = g_utf8_to_utf16(utf8, -1, NULL, &utf16_len, NULL); + if (*utf16 == NULL) + return -1; + + /* g_utf8_to_utf16 produces host-byteorder UTF-16, + * but OBEX requires network byteorder (big endian) */ + for (i = 0; i < utf16_len; i++) + (*utf16)[i] = g_htons((*utf16)[i]); + + utf16_len = (utf16_len + 1) << 1; + + return utf16_len; +} + +static guint8 *put_bytes(guint8 *to, const void *from, size_t count) +{ + memcpy(to, from, count); + return (to + count); +} + +static const guint8 *get_bytes(void *to, const guint8 *from, size_t count) +{ + memcpy(to, from, count); + return (from + count); +} + +size_t g_obex_header_encode(GObexHeader *header, void *buf, size_t buf_len) +{ + guint8 *ptr = buf; + guint16 u16; + guint32 u32; + gunichar2 *utf16; + glong utf16_len; + + if (buf_len < header->hlen) + return 0; + + ptr = put_bytes(ptr, &header->id, sizeof(header->id)); + + switch (G_OBEX_HDR_TYPE(header->id)) { + case G_OBEX_HDR_TYPE_UNICODE: + utf16_len = utf8_to_utf16(&utf16, header->v.string); + if (utf16_len < 0 || (guint16) utf16_len > buf_len) + return 0; + g_assert_cmpuint(utf16_len + 3, ==, header->hlen); + u16 = g_htons(utf16_len + 3); + ptr = put_bytes(ptr, &u16, sizeof(u16)); + ptr = put_bytes(ptr, utf16, utf16_len); + g_free(utf16); + break; + case G_OBEX_HDR_TYPE_BYTES: + u16 = g_htons(header->hlen); + ptr = put_bytes(ptr, &u16, sizeof(u16)); + if (header->extdata) + ptr = put_bytes(ptr, header->v.extdata, header->vlen); + else + ptr = put_bytes(ptr, header->v.data, header->vlen); + break; + case G_OBEX_HDR_TYPE_UINT8: + *ptr = header->v.u8; + break; + case G_OBEX_HDR_TYPE_UINT32: + u32 = g_htonl(header->v.u32); + ptr = put_bytes(ptr, &u32, sizeof(u32)); + break; + default: + g_assert_not_reached(); + } + + return header->hlen; +} + +GObexHeader *g_obex_header_decode(const void *data, size_t len, + GObexDataPolicy data_policy, size_t *parsed) +{ + GObexHeader *header; + const guint8 *ptr = data; + guint16 hdr_len; + size_t str_len; + + if (len < 2) + return NULL; + + header = g_new0(GObexHeader, 1); + + ptr = get_bytes(&header->id, ptr, sizeof(header->id)); + + switch (G_OBEX_HDR_TYPE(header->id)) { + case G_OBEX_HDR_TYPE_UNICODE: + if (len < 3) + goto failed; + ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len)); + hdr_len = g_ntohs(hdr_len); + if (hdr_len > len || hdr_len < 5) + goto failed; + + header->v.string = g_convert((const char *) ptr, hdr_len - 5, + "UTF8", "UTF16BE", + NULL, &str_len, NULL); + if (header->v.string == NULL) + goto failed; + + header->vlen = (size_t) str_len; + header->hlen = hdr_len; + + *parsed = hdr_len; + + break; + case G_OBEX_HDR_TYPE_BYTES: + if (len < 3) + goto failed; + ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len)); + hdr_len = g_ntohs(hdr_len); + if (hdr_len > len) + goto failed; + + header->vlen = hdr_len - 3; + header->hlen = hdr_len; + + switch (data_policy) { + case G_OBEX_DATA_COPY: + header->v.data = g_memdup(ptr, header->vlen); + break; + case G_OBEX_DATA_REF: + header->extdata = TRUE; + header->v.extdata = ptr; + break; + default: + goto failed; + } + + *parsed = hdr_len; + + break; + case G_OBEX_HDR_TYPE_UINT8: + header->vlen = 1; + header->hlen = 2; + header->v.u8 = *ptr; + *parsed = 2; + break; + case G_OBEX_HDR_TYPE_UINT32: + if (len < 5) + goto failed; + header->vlen = 4; + header->hlen = 5; + ptr = get_bytes(&header->v.u32, ptr, sizeof(header->v.u32)); + header->v.u32 = g_ntohl(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); +} + +gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str) +{ + if (G_OBEX_HDR_TYPE(header->id) != G_OBEX_HDR_TYPE_UNICODE) + return FALSE; + + *str = header->v.string; + + return TRUE; +} + +gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val, + size_t *len) +{ + if (G_OBEX_HDR_TYPE(header->id) != G_OBEX_HDR_TYPE_BYTES) + return FALSE; + + *len = header->vlen; + + if (header->extdata) + *val = header->v.extdata; + else + *val = header->v.data; + + return TRUE; +} + +gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val) +{ + if (G_OBEX_HDR_TYPE(header->id) != G_OBEX_HDR_TYPE_UINT8) + return FALSE; + + *val = header->v.u8; + + return TRUE; +} + +gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val) +{ + if (G_OBEX_HDR_TYPE(header->id) != G_OBEX_HDR_TYPE_UINT32) + return FALSE; + + *val = header->v.u32; + + return TRUE; +} + +GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str) +{ + GObexHeader *header; + size_t len; + + if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_UNICODE) + return NULL; + + header = g_new0(GObexHeader, 1); + + header->id = id; + + len = g_utf8_strlen(str, -1); + + header->vlen = len; + header->hlen = 3 + ((len + 1) * 2); + header->v.string = g_strdup(str); + + return header; +} + +GObexHeader *g_obex_header_new_bytes(guint8 id, void *data, size_t len, + GObexDataPolicy data_policy) +{ + GObexHeader *header; + + if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_BYTES) + return NULL; + + header = g_new0(GObexHeader, 1); + + header->id = id; + header->vlen = len; + header->hlen = len + 3; + + switch (data_policy) { + case G_OBEX_DATA_INHERIT: + header->v.data = data; + break; + case G_OBEX_DATA_COPY: + header->v.data = g_memdup(data, len); + break; + case G_OBEX_DATA_REF: + header->extdata = TRUE; + header->v.extdata = data; + break; + } + + return header; +} + +GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val) +{ + GObexHeader *header; + + if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_UINT8) + return NULL; + + header = g_new0(GObexHeader, 1); + + header->id = id; + header->vlen = 1; + header->hlen = 2; + header->v.u8 = val; + + return header; +} + +GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val) +{ + GObexHeader *header; + + if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_UINT32) + return NULL; + + header = g_new0(GObexHeader, 1); + + header->id = id; + header->vlen = 4; + header->hlen = 5; + header->v.u32 = val; + + return header; +} + +guint8 g_obex_header_get_id(GObexHeader *header) +{ + return header->id; +} + +guint16 g_obex_header_get_length(GObexHeader *header) +{ + return header->hlen; +} diff --git a/gobex/gobex-header.h b/gobex/gobex-header.h new file mode 100644 index 000000000..3abe5c697 --- /dev/null +++ b/gobex/gobex-header.h @@ -0,0 +1,78 @@ +/* + * + * OBEX library with GLib integration + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GOBEX_HEADER_H +#define __GOBEX_HEADER_H + +#include + +#include + +/* Header ID's */ +#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 + +typedef struct _GObexHeader GObexHeader; + +gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str); +gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val, + size_t *len); +gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val); +gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val); + +GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str); +GObexHeader *g_obex_header_new_bytes(guint8 id, void *data, size_t len, + GObexDataPolicy data_policy); +GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val); +GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val); + +guint8 g_obex_header_get_id(GObexHeader *header); +guint16 g_obex_header_get_length(GObexHeader *header); + +size_t g_obex_header_encode(GObexHeader *header, void *hdr_ptr, size_t buf_len); +GObexHeader *g_obex_header_decode(const void *data, size_t len, + GObexDataPolicy data_policy, size_t *parsed); +void g_obex_header_free(GObexHeader *header); + +#endif /* __GOBEX_HEADER_H */ diff --git a/gobex/gobex-packet.c b/gobex/gobex-packet.c new file mode 100644 index 000000000..9b03ce134 --- /dev/null +++ b/gobex/gobex-packet.c @@ -0,0 +1,261 @@ +/* + * + * OBEX library with GLib integration + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#include "gobex-packet.h" + +struct _GObexPacket { + guint8 opcode; + gboolean final; + + GObexDataPolicy data_policy; + + union { + void *buf; /* Non-header data */ + const void *buf_ref; /* Reference to non-header data */ + } data; + size_t data_len; + + size_t hlen; /* Length of all encoded headers */ + GSList *headers; +}; + +GObexHeader *g_obex_packet_get_header(GObexPacket *pkt, guint8 id) +{ + GSList *l; + + for (l = pkt->headers; l != NULL; l = g_slist_next(l)) { + GObexHeader *hdr = l->data; + + if (g_obex_header_get_id(hdr) == id) + return hdr; + } + + return NULL; +} + +guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final) +{ + if (final) + *final = pkt->final; + + return pkt->opcode; +} + +gboolean g_obex_packet_add_header(GObexPacket *pkt, GObexHeader *header) +{ + pkt->headers = g_slist_append(pkt->headers, header); + pkt->hlen += g_obex_header_get_length(header); + + return TRUE; +} + +const void *g_obex_packet_get_data(GObexPacket *pkt, size_t *len) +{ + if (pkt->data_len == 0) { + *len = 0; + return NULL; + } + + *len = pkt->data_len; + + switch (pkt->data_policy) { + case G_OBEX_DATA_INHERIT: + case G_OBEX_DATA_COPY: + return pkt->data.buf; + case G_OBEX_DATA_REF: + return pkt->data.buf_ref; + } + + g_assert_not_reached(); +} + +gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, size_t len, + GObexDataPolicy data_policy) +{ + if (pkt->data.buf || pkt->data.buf_ref) + return FALSE; + + pkt->data_policy = data_policy; + pkt->data_len = len; + + switch (data_policy) { + case G_OBEX_DATA_COPY: + pkt->data.buf = g_memdup(data, len); + break; + case G_OBEX_DATA_REF: + pkt->data.buf_ref = data; + break; + case G_OBEX_DATA_INHERIT: + pkt->data.buf = (void *) data; + break; + } + + return TRUE; +} + +GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final) +{ + GObexPacket *pkt; + + pkt = g_new0(GObexPacket, 1); + + pkt->opcode = opcode; + pkt->final = final; + + pkt->data_policy = G_OBEX_DATA_COPY; + + return pkt; +} + +void g_obex_packet_free(GObexPacket *pkt) +{ + switch (pkt->data_policy) { + case G_OBEX_DATA_INHERIT: + case G_OBEX_DATA_COPY: + g_free(pkt->data.buf); + break; + case G_OBEX_DATA_REF: + break; + } + + g_slist_foreach(pkt->headers, (GFunc) g_obex_header_free, NULL); + g_slist_free(pkt->headers); + g_free(pkt); +} + +static gboolean parse_headers(GObexPacket *pkt, 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; + + pkt->headers = g_slist_append(pkt->headers, header); + + len -= parsed; + buf += parsed; + } + + return TRUE; +} + +static const guint8 *get_bytes(void *to, const guint8 *from, size_t count) +{ + memcpy(to, from, count); + return (from + count); +} + +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; + GObexPacket *pkt; + gboolean final; + + 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; + + final = (opcode & G_OBEX_PACKET_FINAL) ? TRUE : FALSE; + opcode &= ~G_OBEX_PACKET_FINAL; + + pkt = g_obex_packet_new(opcode, final); + + if (header_offset == 0) + goto headers; + + if (3 + header_offset < len) + goto failed; + + if (data_policy == G_OBEX_DATA_INHERIT) + goto failed; + + if (!g_obex_packet_set_data(pkt, buf, header_offset, data_policy)) + goto failed; + + buf += header_offset; + +headers: + if (!parse_headers(pkt, buf, len - (buf - (guint8 *) data), + data_policy)) + goto failed; + + return pkt; + +failed: + g_obex_packet_free(pkt); + return NULL; +} + +ssize_t g_obex_packet_encode(GObexPacket *pkt, guint8 *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_PACKET_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; +} diff --git a/gobex/gobex-packet.h b/gobex/gobex-packet.h new file mode 100644 index 000000000..ddce68607 --- /dev/null +++ b/gobex/gobex-packet.h @@ -0,0 +1,59 @@ +/* + * + * OBEX library with GLib integration + * + * Copyright (C) 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GOBEX_PACKET_H +#define __GOBEX_PACKET_H + +#include + +#include + +#include +#include + +/* Opcodes */ +#define G_OBEX_OP_CONNECT 0x00 +#define G_OBEX_OP_DISCONNECT 0x01 +#define G_OBEX_OP_PUT 0x02 +#define G_OBEX_OP_GET 0x03 +#define G_OBEX_OP_SETPATH 0x05 +#define G_OBEX_OP_SESSION 0x07 +#define G_OBEX_OP_ABORT 0x7f + +#define G_OBEX_PACKET_FINAL 0x80 + +typedef struct _GObexPacket GObexPacket; + +GObexHeader *g_obex_packet_get_header(GObexPacket *pkt, guint8 id); +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, + GObexDataPolicy data_policy); +const void *g_obex_packet_get_data(GObexPacket *pkt, size_t *len); +GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final); +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); +ssize_t g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, size_t len); + +#endif /* __GOBEX_PACKET_H */ diff --git a/gobex/gobex.c b/gobex/gobex.c index 75675cba8..7c6e5863f 100644 --- a/gobex/gobex.c +++ b/gobex/gobex.c @@ -21,7 +21,6 @@ #include #include -#include #include "gobex.h" @@ -29,46 +28,6 @@ #define G_OBEX_MINIMUM_MTU 255 #define G_OBEX_MAXIMUM_MTU 65535 -/* 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) - -#define G_OBEX_FINAL 0x80 - -struct _GObexHeader { - guint8 id; - gboolean extdata; - size_t vlen; /* Length of value */ - size_t hlen; /* Length of full encoded header */ - union { - char *string; /* UTF-8 converted from UTF-16 */ - guint8 *data; /* Own buffer */ - const guint8 *extdata; /* Reference to external buffer */ - guint8 u8; - guint32 u32; - } v; -}; - -struct _GObexPacket { - guint8 opcode; - gboolean final; - - GObexDataPolicy data_policy; - - union { - void *buf; /* Non-header data */ - const void *buf_ref; /* Reference to non-header data */ - } data; - size_t data_len; - - size_t hlen; /* Length of all encoded headers */ - GSList *headers; -}; - struct _GObex { gint ref_count; GIOChannel *io; @@ -116,430 +75,6 @@ struct setpath_data { guint8 constants; } __attribute__ ((packed)); -static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) { - glong utf16_len; - int i; - - if (*utf8 == '\0') { - *utf16 = NULL; - return 0; - } - - *utf16 = g_utf8_to_utf16(utf8, -1, NULL, &utf16_len, NULL); - if (*utf16 == NULL) - return -1; - - /* g_utf8_to_utf16 produces host-byteorder UTF-16, - * but OBEX requires network byteorder (big endian) */ - for (i = 0; i < utf16_len; i++) - (*utf16)[i] = g_htons((*utf16)[i]); - - utf16_len = (utf16_len + 1) << 1; - - return utf16_len; -} - -static guint8 *put_bytes(guint8 *to, const void *from, size_t count) -{ - memcpy(to, from, count); - return (to + count); -} - -static const guint8 *get_bytes(void *to, const guint8 *from, size_t count) -{ - memcpy(to, from, count); - return (from + count); -} - -size_t g_obex_header_encode(GObexHeader *header, void *buf, size_t buf_len) -{ - guint8 *ptr = buf; - guint16 u16; - guint32 u32; - gunichar2 *utf16; - glong utf16_len; - - if (buf_len < header->hlen) - return 0; - - ptr = put_bytes(ptr, &header->id, sizeof(header->id)); - - switch (G_OBEX_HDR_TYPE(header->id)) { - case G_OBEX_HDR_TYPE_UNICODE: - utf16_len = utf8_to_utf16(&utf16, header->v.string); - if (utf16_len < 0 || (guint16) utf16_len > buf_len) - return 0; - g_assert_cmpuint(utf16_len + 3, ==, header->hlen); - u16 = g_htons(utf16_len + 3); - ptr = put_bytes(ptr, &u16, sizeof(u16)); - ptr = put_bytes(ptr, utf16, utf16_len); - g_free(utf16); - break; - case G_OBEX_HDR_TYPE_BYTES: - u16 = g_htons(header->hlen); - ptr = put_bytes(ptr, &u16, sizeof(u16)); - if (header->extdata) - ptr = put_bytes(ptr, header->v.extdata, header->vlen); - else - ptr = put_bytes(ptr, header->v.data, header->vlen); - break; - case G_OBEX_HDR_TYPE_UINT8: - *ptr = header->v.u8; - break; - case G_OBEX_HDR_TYPE_UINT32: - u32 = g_htonl(header->v.u32); - ptr = put_bytes(ptr, &u32, sizeof(u32)); - break; - default: - g_assert_not_reached(); - } - - return header->hlen; -} - -GObexHeader *g_obex_header_decode(const void *data, size_t len, - GObexDataPolicy data_policy, size_t *parsed) -{ - GObexHeader *header; - const guint8 *ptr = data; - guint16 hdr_len; - size_t str_len; - - if (len < 2) - return NULL; - - header = g_new0(GObexHeader, 1); - - ptr = get_bytes(&header->id, ptr, sizeof(header->id)); - - switch (G_OBEX_HDR_TYPE(header->id)) { - case G_OBEX_HDR_TYPE_UNICODE: - if (len < 3) - goto failed; - ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len)); - hdr_len = g_ntohs(hdr_len); - if (hdr_len > len || hdr_len < 5) - goto failed; - - header->v.string = g_convert((const char *) ptr, hdr_len - 5, - "UTF8", "UTF16BE", - NULL, &str_len, NULL); - if (header->v.string == NULL) - goto failed; - - header->vlen = (size_t) str_len; - header->hlen = hdr_len; - - *parsed = hdr_len; - - break; - case G_OBEX_HDR_TYPE_BYTES: - if (len < 3) - goto failed; - ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len)); - hdr_len = g_ntohs(hdr_len); - if (hdr_len > len) - goto failed; - - header->vlen = hdr_len - 3; - header->hlen = hdr_len; - - switch (data_policy) { - case G_OBEX_DATA_COPY: - header->v.data = g_memdup(ptr, header->vlen); - break; - case G_OBEX_DATA_REF: - header->extdata = TRUE; - header->v.extdata = ptr; - break; - default: - goto failed; - } - - *parsed = hdr_len; - - break; - case G_OBEX_HDR_TYPE_UINT8: - header->vlen = 1; - header->hlen = 2; - header->v.u8 = *ptr; - *parsed = 2; - break; - case G_OBEX_HDR_TYPE_UINT32: - if (len < 5) - goto failed; - header->vlen = 4; - header->hlen = 5; - ptr = get_bytes(&header->v.u32, ptr, sizeof(header->v.u32)); - header->v.u32 = g_ntohl(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); -} - -gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str) -{ - if (G_OBEX_HDR_TYPE(header->id) != G_OBEX_HDR_TYPE_UNICODE) - return FALSE; - - *str = header->v.string; - - return TRUE; -} - -gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val, - size_t *len) -{ - if (G_OBEX_HDR_TYPE(header->id) != G_OBEX_HDR_TYPE_BYTES) - return FALSE; - - *len = header->vlen; - - if (header->extdata) - *val = header->v.extdata; - else - *val = header->v.data; - - return TRUE; -} - -gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val) -{ - if (G_OBEX_HDR_TYPE(header->id) != G_OBEX_HDR_TYPE_UINT8) - return FALSE; - - *val = header->v.u8; - - return TRUE; -} - -gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val) -{ - if (G_OBEX_HDR_TYPE(header->id) != G_OBEX_HDR_TYPE_UINT32) - return FALSE; - - *val = header->v.u32; - - return TRUE; -} - -GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str) -{ - GObexHeader *header; - size_t len; - - if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_UNICODE) - return NULL; - - header = g_new0(GObexHeader, 1); - - header->id = id; - - len = g_utf8_strlen(str, -1); - - header->vlen = len; - header->hlen = 3 + ((len + 1) * 2); - header->v.string = g_strdup(str); - - return header; -} - -GObexHeader *g_obex_header_new_bytes(guint8 id, void *data, size_t len, - GObexDataPolicy data_policy) -{ - GObexHeader *header; - - if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_BYTES) - return NULL; - - header = g_new0(GObexHeader, 1); - - header->id = id; - header->vlen = len; - header->hlen = len + 3; - - switch (data_policy) { - case G_OBEX_DATA_INHERIT: - header->v.data = data; - break; - case G_OBEX_DATA_COPY: - header->v.data = g_memdup(data, len); - break; - case G_OBEX_DATA_REF: - header->extdata = TRUE; - header->v.extdata = data; - break; - } - - return header; -} - -GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val) -{ - GObexHeader *header; - - if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_UINT8) - return NULL; - - header = g_new0(GObexHeader, 1); - - header->id = id; - header->vlen = 1; - header->hlen = 2; - header->v.u8 = val; - - return header; -} - -GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val) -{ - GObexHeader *header; - - if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_UINT32) - return NULL; - - header = g_new0(GObexHeader, 1); - - header->id = id; - header->vlen = 4; - header->hlen = 5; - header->v.u32 = val; - - return header; -} - -GObexHeader *g_obex_packet_get_header(GObexPacket *pkt, guint8 id) -{ - GSList *l; - - for (l = pkt->headers; l != NULL; l = g_slist_next(l)) { - GObexHeader *hdr = l->data; - - if (hdr->id == id) - return hdr; - } - - return NULL; -} - -guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final) -{ - if (final) - *final = pkt->final; - - return pkt->opcode; -} - -gboolean g_obex_packet_add_header(GObexPacket *pkt, GObexHeader *header) -{ - pkt->headers = g_slist_append(pkt->headers, header); - pkt->hlen += header->hlen; - - return TRUE; -} - -const void *g_obex_packet_get_data(GObexPacket *pkt, size_t *len) -{ - if (pkt->data_len == 0) { - *len = 0; - return NULL; - } - - *len = pkt->data_len; - - switch (pkt->data_policy) { - case G_OBEX_DATA_INHERIT: - case G_OBEX_DATA_COPY: - return pkt->data.buf; - case G_OBEX_DATA_REF: - return pkt->data.buf_ref; - } - - g_assert_not_reached(); -} - -gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, size_t len, - GObexDataPolicy data_policy) -{ - if (pkt->data.buf || pkt->data.buf_ref) - return FALSE; - - pkt->data_policy = data_policy; - pkt->data_len = len; - - switch (data_policy) { - case G_OBEX_DATA_COPY: - pkt->data.buf = g_memdup(data, len); - break; - case G_OBEX_DATA_REF: - pkt->data.buf_ref = data; - break; - case G_OBEX_DATA_INHERIT: - pkt->data.buf = (void *) data; - break; - } - - return TRUE; -} - -GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final) -{ - GObexPacket *pkt; - - pkt = g_new0(GObexPacket, 1); - - pkt->opcode = opcode; - pkt->final = final; - - pkt->data_policy = G_OBEX_DATA_COPY; - - return pkt; -} - -void g_obex_packet_free(GObexPacket *pkt) -{ - switch (pkt->data_policy) { - case G_OBEX_DATA_INHERIT: - case G_OBEX_DATA_COPY: - g_free(pkt->data.buf); - break; - case G_OBEX_DATA_REF: - break; - } - - g_slist_foreach(pkt->headers, (GFunc) g_obex_header_free, NULL); - g_slist_free(pkt->headers); - g_free(pkt); -} - static ssize_t req_header_offset(guint8 opcode) { switch (opcode) { @@ -575,116 +110,6 @@ static ssize_t rsp_header_offset(guint8 opcode) } } -static gboolean parse_headers(GObexPacket *pkt, 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; - - pkt->headers = g_slist_append(pkt->headers, header); - - len -= parsed; - buf += parsed; - } - - return TRUE; -} - -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; - GObexPacket *pkt; - gboolean final; - - 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; - - final = (opcode & G_OBEX_FINAL) ? TRUE : FALSE; - opcode &= ~G_OBEX_FINAL; - - pkt = g_obex_packet_new(opcode, final); - - if (header_offset == 0) - goto headers; - - if (3 + header_offset < len) - goto failed; - - if (data_policy == G_OBEX_DATA_INHERIT) - goto failed; - - if (!g_obex_packet_set_data(pkt, buf, header_offset, data_policy)) - goto failed; - - buf += header_offset; - -headers: - if (!parse_headers(pkt, buf, len - (buf - (guint8 *) data), - data_policy)) - goto failed; - - return pkt; - -failed: - g_obex_packet_free(pkt); - 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 void pending_pkt_free(struct pending_pkt *p) { g_obex_packet_free(p->pkt); @@ -944,7 +369,7 @@ static gboolean incoming_data(GIOChannel *io, GIOCondition cond, guint8 opcode = g_obex_packet_get_operation(p->pkt, NULL); header_offset = req_header_offset(opcode); } else { - guint8 opcode = obex->rx_buf[0] & ~G_OBEX_FINAL; + guint8 opcode = obex->rx_buf[0] & ~G_OBEX_PACKET_FINAL; header_offset = rsp_header_offset(opcode); } diff --git a/gobex/gobex.h b/gobex/gobex.h index 1d91fe77d..5823eacd0 100644 --- a/gobex/gobex.h +++ b/gobex/gobex.h @@ -22,50 +22,9 @@ #ifndef __GOBEX_H #define __GOBEX_H -#include #include -/* Opcodes */ -#define G_OBEX_OP_CONNECT 0x00 -#define G_OBEX_OP_DISCONNECT 0x01 -#define G_OBEX_OP_PUT 0x02 -#define G_OBEX_OP_GET 0x03 -#define G_OBEX_OP_SETPATH 0x05 -#define G_OBEX_OP_SESSION 0x07 -#define G_OBEX_OP_ABORT 0x7f - -/* Header ID's */ -#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 - -typedef enum { - G_OBEX_DATA_INHERIT, - G_OBEX_DATA_COPY, - G_OBEX_DATA_REF, -} GObexDataPolicy; +#include typedef enum { G_OBEX_TRANSPORT_STREAM, @@ -73,44 +32,12 @@ typedef enum { } GObexTransportType; typedef struct _GObex GObex; -typedef struct _GObexPacket GObexPacket; -typedef struct _GObexHeader GObexHeader; typedef void (*GObexRequestFunc) (GObex *obex, GObexPacket *req, gpointer user_data); typedef void (*GObexResponseFunc) (GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data); -gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str); -gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val, - size_t *len); -gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val); -gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val); - -GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str); -GObexHeader *g_obex_header_new_bytes(guint8 id, void *data, size_t len, - GObexDataPolicy data_policy); -GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val); -GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val); - -size_t g_obex_header_encode(GObexHeader *header, void *hdr_ptr, size_t buf_len); -GObexHeader *g_obex_header_decode(const void *data, size_t len, - GObexDataPolicy data_policy, size_t *parsed); -void g_obex_header_free(GObexHeader *header); - -GObexHeader *g_obex_packet_get_header(GObexPacket *pkt, guint8 id); -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, - GObexDataPolicy data_policy); -const void *g_obex_packet_get_data(GObexPacket *pkt, size_t *len); -GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final); -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, diff --git a/unit/test-gobex.c b/unit/test-gobex.c index 3d99c56bb..2d7734ada 100644 --- a/unit/test-gobex.c +++ b/unit/test-gobex.c @@ -25,6 +25,7 @@ #include #include #include +#include #include -- 2.47.3