| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * |
| * OBEX library with GLib integration |
| * |
| * Copyright (C) 2012 Intel Corporation. |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| |
| #include "gobex-apparam.h" |
| #include "gobex-debug.h" |
| |
| struct _GObexApparam { |
| GHashTable *tags; |
| }; |
| |
| struct apparam_tag { |
| guint8 id; |
| guint8 len; |
| union { |
| /* Data is stored in network order */ |
| char string[0]; |
| guint8 data[0]; |
| guint8 u8; |
| guint16 u16; |
| guint32 u32; |
| guint64 u64; |
| } value; |
| } __attribute__ ((packed)); |
| |
| static struct apparam_tag *tag_new(guint8 id, guint8 len, const void *data) |
| { |
| struct apparam_tag *tag; |
| |
| tag = g_malloc0(2 + len); |
| tag->id = id; |
| tag->len = len; |
| memcpy(tag->value.data, data, len); |
| |
| return tag; |
| } |
| |
| static GObexApparam *g_obex_apparam_new(void) |
| { |
| GObexApparam *apparam; |
| |
| apparam = g_new0(GObexApparam, 1); |
| apparam->tags = g_hash_table_new_full(g_direct_hash, g_direct_equal, |
| NULL, g_free); |
| |
| return apparam; |
| } |
| |
| static struct apparam_tag *apparam_tag_decode(const void *data, gsize size, |
| gsize *parsed) |
| { |
| struct apparam_tag *tag; |
| const guint8 *ptr = data; |
| guint8 id; |
| guint8 len; |
| |
| if (size < 2) |
| return NULL; |
| |
| id = ptr[0]; |
| len = ptr[1]; |
| |
| if (len > size - 2) |
| return NULL; |
| |
| tag = tag_new(id, len, ptr + 2); |
| if (tag == NULL) |
| return NULL; |
| |
| *parsed = 2 + tag->len; |
| |
| return tag; |
| } |
| |
| GObexApparam *g_obex_apparam_decode(const void *data, gsize size) |
| { |
| GObexApparam *apparam; |
| GHashTable *tags; |
| gsize count = 0; |
| |
| if (size < 2) |
| return NULL; |
| |
| apparam = g_obex_apparam_new(); |
| |
| tags = apparam->tags; |
| while (count < size) { |
| struct apparam_tag *tag; |
| gsize parsed; |
| guint id; |
| |
| tag = apparam_tag_decode(data + count, size - count, &parsed); |
| if (tag == NULL) |
| break; |
| |
| id = tag->id; |
| g_hash_table_insert(tags, GUINT_TO_POINTER(id), tag); |
| |
| count += parsed; |
| } |
| |
| if (count != size) { |
| g_obex_apparam_free(apparam); |
| return NULL; |
| } |
| |
| return apparam; |
| } |
| |
| static gssize tag_encode(struct apparam_tag *tag, void *buf, gsize len) |
| { |
| gsize count = 2 + tag->len; |
| |
| if (len < count) |
| return -ENOBUFS; |
| |
| memcpy(buf, tag, count); |
| |
| return count; |
| } |
| |
| gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize len) |
| { |
| gsize count = 0; |
| gssize ret; |
| GHashTableIter iter; |
| gpointer key, value; |
| |
| if (!apparam) |
| return 0; |
| |
| g_hash_table_iter_init(&iter, apparam->tags); |
| while (g_hash_table_iter_next(&iter, &key, &value)) { |
| struct apparam_tag *tag = value; |
| |
| ret = tag_encode(tag, buf + count, len - count); |
| if (ret < 0) |
| return ret; |
| |
| count += ret; |
| } |
| |
| return count; |
| } |
| |
| GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id, |
| const void *value, gsize len) |
| { |
| struct apparam_tag *tag; |
| guint uid = id; |
| |
| if (apparam == NULL) |
| apparam = g_obex_apparam_new(); |
| |
| tag = tag_new(id, len, value); |
| g_hash_table_replace(apparam->tags, GUINT_TO_POINTER(uid), tag); |
| |
| return apparam; |
| } |
| |
| GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id, |
| guint8 value) |
| { |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); |
| |
| return g_obex_apparam_set_bytes(apparam, id, &value, 1); |
| } |
| |
| GObexApparam *g_obex_apparam_set_uint16(GObexApparam *apparam, guint8 id, |
| guint16 value) |
| { |
| guint16 num = g_htons(value); |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); |
| |
| return g_obex_apparam_set_bytes(apparam, id, &num, 2); |
| } |
| |
| GObexApparam *g_obex_apparam_set_uint32(GObexApparam *apparam, guint8 id, |
| guint32 value) |
| { |
| guint32 num = g_htonl(value); |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %u", id, value); |
| |
| return g_obex_apparam_set_bytes(apparam, id, &num, 4); |
| } |
| |
| GObexApparam *g_obex_apparam_set_uint64(GObexApparam *apparam, guint8 id, |
| guint64 value) |
| { |
| guint64 num = GUINT64_TO_BE(value); |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %" |
| G_GUINT64_FORMAT, id, value); |
| |
| return g_obex_apparam_set_bytes(apparam, id, &num, 8); |
| } |
| |
| GObexApparam *g_obex_apparam_set_string(GObexApparam *apparam, guint8 id, |
| const char *value) |
| { |
| gsize len; |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x value %s", id, value); |
| |
| len = strlen(value) + 1; |
| if (len > G_MAXUINT8) { |
| ((char *) value)[G_MAXUINT8 - 1] = '\0'; |
| len = G_MAXUINT8; |
| } |
| |
| return g_obex_apparam_set_bytes(apparam, id, value, len); |
| } |
| |
| static struct apparam_tag *g_obex_apparam_find_tag(GObexApparam *apparam, |
| guint id) |
| { |
| return g_hash_table_lookup(apparam->tags, GUINT_TO_POINTER(id)); |
| } |
| |
| gboolean g_obex_apparam_get_uint8(GObexApparam *apparam, guint8 id, |
| guint8 *dest) |
| { |
| struct apparam_tag *tag; |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); |
| |
| tag = g_obex_apparam_find_tag(apparam, id); |
| if (tag == NULL) |
| return FALSE; |
| |
| *dest = tag->value.u8; |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); |
| |
| return TRUE; |
| } |
| |
| gboolean g_obex_apparam_get_uint16(GObexApparam *apparam, guint8 id, |
| guint16 *dest) |
| { |
| struct apparam_tag *tag; |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); |
| |
| tag = g_obex_apparam_find_tag(apparam, id); |
| if (tag == NULL) |
| return FALSE; |
| |
| if (tag->len < sizeof(*dest)) |
| return FALSE; |
| |
| *dest = g_ntohs(tag->value.u16); |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); |
| |
| return TRUE; |
| } |
| |
| gboolean g_obex_apparam_get_uint32(GObexApparam *apparam, guint8 id, |
| guint32 *dest) |
| { |
| struct apparam_tag *tag; |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); |
| |
| tag = g_obex_apparam_find_tag(apparam, id); |
| if (tag == NULL) |
| return FALSE; |
| |
| if (tag->len < sizeof(*dest)) |
| return FALSE; |
| |
| *dest = g_ntohl(tag->value.u32); |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "%u", *dest); |
| |
| return TRUE; |
| } |
| |
| gboolean g_obex_apparam_get_uint64(GObexApparam *apparam, guint8 id, |
| guint64 *dest) |
| { |
| struct apparam_tag *tag; |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); |
| |
| tag = g_obex_apparam_find_tag(apparam, id); |
| if (tag == NULL) |
| return FALSE; |
| |
| if (tag->len < sizeof(*dest)) |
| return FALSE; |
| |
| *dest = GUINT64_FROM_BE(tag->value.u64); |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "%" G_GUINT64_FORMAT, *dest); |
| |
| return TRUE; |
| } |
| |
| char *g_obex_apparam_get_string(GObexApparam *apparam, guint8 id) |
| { |
| struct apparam_tag *tag; |
| char *string; |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); |
| |
| tag = g_obex_apparam_find_tag(apparam, id); |
| if (tag == NULL) |
| return NULL; |
| |
| string = g_strndup(tag->value.string, tag->len); |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "%s", string); |
| |
| return string; |
| } |
| |
| gboolean g_obex_apparam_get_bytes(GObexApparam *apparam, guint8 id, |
| const guint8 **val, gsize *len) |
| { |
| struct apparam_tag *tag; |
| |
| g_obex_debug(G_OBEX_DEBUG_APPARAM, "tag 0x%02x", id); |
| |
| tag = g_obex_apparam_find_tag(apparam, id); |
| if (tag == NULL) |
| return FALSE; |
| |
| *len = tag->len; |
| *val = tag->value.data; |
| |
| return TRUE; |
| } |
| |
| void g_obex_apparam_free(GObexApparam *apparam) |
| { |
| g_hash_table_unref(apparam->tags); |
| g_free(apparam); |
| } |