// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX library with GLib integration * * Copyright (C) 2012 Intel Corporation. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #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); }