blob: acf428c2ba3eda331e557f17b136b3c865643db3 [file] [log] [blame]
/*
*
* Embedded Linux library
*
* Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "dbus.h"
#include "private.h"
#include "dbus-private.h"
#include "string.h"
#include "queue.h"
#define DBUS_MAX_INTERFACE_LEN 255
#define DBUS_MAX_METHOD_LEN 255
static const char *simple_types = "sogybnqiuxtdh";
static int get_alignment(const char type)
{
switch (type) {
case 'b':
return 4;
case 'y':
return 1;
case 'n':
case 'q':
return 2;
case 'u':
case 'i':
return 4;
case 'x':
case 't':
case 'd':
return 8;
case 's':
case 'o':
return 4;
case 'g':
return 1;
case 'a':
return 4;
case '(':
case '{':
return 8;
case 'v':
return 1;
case 'h':
return 4;
default:
return 0;
}
}
static int get_basic_size(const char type)
{
switch (type) {
case 'b':
return 4;
case 'y':
return 1;
case 'n':
case 'q':
return 2;
case 'i':
case 'u':
return 4;
case 'x':
case 't':
return 8;
case 'd':
return 8;
case 'h':
return 4;
default:
return 0;
}
}
static inline bool is_valid_character(const char c, bool bus_name)
{
if (c >= 'a' && c <= 'z')
return true;
if (c >= 'A' && c <= 'Z')
return true;
if (c >= '0' && c <= '9')
return true;
if (c == '_')
return true;
if (c == '-' && bus_name)
return true;
return false;
}
bool _dbus_valid_object_path(const char *path)
{
unsigned int i;
char c = '\0';
if (path == NULL)
return false;
if (path[0] == '\0')
return false;
if (path[0] && !path[1] && path[0] == '/')
return true;
if (path[0] != '/')
return false;
for (i = 0; path[i]; i++) {
if (path[i] == '/' && c == '/')
return false;
c = path[i];
if (is_valid_character(path[i], false) || path[i] == '/')
continue;
return false;
}
if (path[i-1] == '/')
return false;
return true;
}
static const char *validate_next_type(const char *sig)
{
char s = *sig;
if (s == '\0')
return NULL;
if (strchr(simple_types, s) || s == 'v')
return sig + 1;
switch (s) {
case 'a':
s = *++sig;
if (s == '{') {
s = *++sig;
/* Dictionary keys can only be simple types */
if (!strchr(simple_types, s))
return NULL;
sig = validate_next_type(sig + 1);
if (!sig)
return NULL;
if (*sig != '}')
return NULL;
return sig + 1;
}
return validate_next_type(sig);
case '(':
sig++;
do
sig = validate_next_type(sig);
while (sig && *sig != ')');
if (!sig)
return NULL;
return sig + 1;
}
return NULL;
}
static bool valid_dict_signature(const char *sig)
{
char s = *sig;
if (s != '{')
return false;
s = *++sig;
if (!strchr(simple_types, s))
return false;
sig = validate_next_type(sig + 1);
if (!sig)
return false;
if (sig[0] != '}')
return false;
if (sig[1] != '\0')
return false;
return true;
}
bool _dbus_valid_signature(const char *sig)
{
const char *s = sig;
do {
s = validate_next_type(s);
if (!s)
return false;
} while (*s);
return true;
}
int _dbus_num_children(const char *sig)
{
const char *s = sig;
int num_children = 0;
do {
s = validate_next_type(s);
if (!s)
return -1;
num_children += 1;
} while (*s);
return num_children;
}
static bool valid_member_name(const char *start, const char *end,
bool bus_name)
{
const char *p;
if ((end - start) < 1)
return false;
if (*start >= '0' && *start <= '9')
return false;
for (p = start; p < end; p++)
if (!is_valid_character(*p, bus_name))
return false;
return true;
}
bool _dbus_valid_method(const char *method)
{
unsigned int i;
if (!method)
return false;
if (method[0] == '\0' || strlen(method) > DBUS_MAX_METHOD_LEN)
return false;
if (method[0] >= '0' && method[0] <= '9')
return false;
for (i = 0; method[i]; i++)
if (!is_valid_character(method[i], false))
return false;
return true;
}
bool _dbus_valid_interface(const char *interface)
{
const char *sep;
if (!interface)
return false;
if (interface[0] == '\0' || strlen(interface) > DBUS_MAX_INTERFACE_LEN)
return false;
sep = strchrnul(interface, '.');
if (*sep == '\0')
return false;
while (true) {
if (!valid_member_name(interface, sep, false))
return false;
if (*sep == '\0')
break;
interface = sep + 1;
sep = strchrnul(interface, '.');
}
return true;
}
bool _dbus_parse_unique_name(const char *name, uint64_t *out_id)
{
char *endp = NULL;
uint64_t r;
if (!l_str_has_prefix(name, ":1."))
return false;
errno = 0;
r = strtoull(name + 3, &endp, 10);
if (!endp || endp == name || *endp || errno)
return false;
if (out_id)
*out_id = r;
return true;
}
bool _dbus_valid_bus_name(const char *bus_name)
{
const char *sep;
if (!bus_name)
return false;
if (bus_name[0] == '\0' || strlen(bus_name) > DBUS_MAX_INTERFACE_LEN)
return false;
if (_dbus_parse_unique_name(bus_name, NULL))
return true;
sep = strchrnul(bus_name, '.');
if (*sep == '\0')
return false;
while (true) {
if (!valid_member_name(bus_name, sep, true))
return false;
if (*sep == '\0')
break;
bus_name = sep + 1;
sep = strchrnul(bus_name, '.');
}
return true;
}
const char *_dbus_signature_end(const char *signature)
{
const char *ptr = signature;
unsigned int indent = 0;
char expect;
switch (*signature) {
case '(':
expect = ')';
break;
case '{':
expect = '}';
break;
case 'a':
return _dbus_signature_end(signature + 1);
default:
return signature;
}
for (ptr = signature; *ptr != '\0'; ptr++) {
if (*ptr == *signature)
indent++;
else if (*ptr == expect)
if (!--indent)
return ptr;
}
return NULL;
}
bool _dbus1_header_is_valid(void *data, size_t size)
{
struct dbus_header *hdr;
size_t header_len;
if (size < sizeof(struct dbus_header))
return false;
hdr = data;
if (hdr->endian != DBUS_NATIVE_ENDIAN)
header_len = bswap_32(hdr->dbus1.field_length);
else
header_len = hdr->dbus1.field_length;
header_len += sizeof(struct dbus_header);
return size >= header_len;
}
static inline void dbus1_iter_init_internal(struct l_dbus_message_iter *iter,
struct l_dbus_message *message,
enum dbus_container_type type,
const char *sig_start,
const char *sig_end,
const void *data, size_t len,
size_t pos)
{
size_t sig_len;
iter->message = message;
if (sig_end)
sig_len = sig_end - sig_start;
else
sig_len = strlen(sig_start);
iter->sig_start = sig_start;
iter->sig_len = sig_len;
iter->sig_pos = 0;
iter->data = data;
iter->len = pos + len;
iter->pos = pos;
iter->container_type = type;
}
void _dbus1_iter_init(struct l_dbus_message_iter *iter,
struct l_dbus_message *message,
const char *sig_start, const char *sig_end,
const void *data, size_t len)
{
dbus1_iter_init_internal(iter, message, DBUS_CONTAINER_TYPE_STRUCT,
sig_start, sig_end, data, len, 0);
}
static const char *calc_len_next_item(const char *signature, const void *data,
size_t data_pos, size_t data_len,
size_t *out_len)
{
unsigned int alignment;
size_t pos;
size_t len;
const char *sig_end;
const char *var_sig;
alignment = get_alignment(*signature);
if (alignment == 0)
return NULL;
pos = align_len(data_pos, alignment);
if (pos > data_len)
return NULL;
switch (*signature) {
case 'o':
case 's':
if (pos + 5 > data_len)
return NULL;
pos += l_get_u32(data + pos) + 5;
break;
case 'g':
if (pos + 2 > data_len)
return NULL;
pos += l_get_u8(data + pos) + 2;
break;
case 'y':
pos += 1;
break;
case 'n':
case 'q':
pos += 2;
break;
case 'b':
case 'i':
case 'u':
case 'h':
pos += 4;
break;
case 'x':
case 't':
case 'd':
pos += 8;
break;
case 'a':
if (pos + 4 > data_len)
return NULL;
len = l_get_u32(data + pos);
pos += 4;
alignment = get_alignment(signature[1]);
pos = align_len(pos, alignment);
pos += len;
sig_end = _dbus_signature_end(signature) + 1;
goto done;
case '(':
sig_end = signature + 1;
while (*sig_end != ')') {
sig_end = calc_len_next_item(sig_end, data, pos,
data_len, &len);
if (!sig_end)
return NULL;
pos += len;
}
sig_end += 1;
goto done;
case '{':
sig_end = calc_len_next_item(signature + 1, data, pos,
data_len, &len);
if (!sig_end)
return NULL;
pos += len;
sig_end = calc_len_next_item(sig_end, data, pos,
data_len, &len);
if (!sig_end)
return NULL;
pos += len;
sig_end += 1;
goto done;
case 'v':
if (!calc_len_next_item("g", data, pos, data_len, &len))
return NULL;
var_sig = data + pos + 1;
pos += len;
if (!calc_len_next_item(var_sig, data, pos, data_len, &len))
return NULL;
pos += len;
break;
default:
return NULL;
}
sig_end = signature + 1;
done:
if (pos > data_len)
return NULL;
*out_len = pos - data_pos;
return sig_end;
}
bool _dbus1_iter_next_entry_basic(struct l_dbus_message_iter *iter,
char type, void *out)
{
const char *str_val;
uint8_t uint8_val;
uint16_t uint16_val;
uint32_t uint32_val;
uint64_t uint64_val;
int16_t int16_val;
int32_t int32_val;
int64_t int64_val;
size_t pos;
if (iter->pos >= iter->len)
return false;
pos = align_len(iter->pos, get_alignment(type));
switch (type) {
case 'o':
case 's':
if (pos + 5 > iter->len)
return false;
uint32_val = l_get_u32(iter->data + pos);
str_val = iter->data + pos + 4;
*(const void **) out = str_val;
iter->pos = pos + uint32_val + 5;
break;
case 'g':
if (pos + 2 > iter->len)
return false;
uint8_val = l_get_u8(iter->data + pos);
str_val = iter->data + pos + 1;
*(const void **) out = str_val;
iter->pos = pos + uint8_val + 2;
break;
case 'b':
if (pos + 4 > iter->len)
return false;
uint32_val = l_get_u32(iter->data + pos);
*(bool *) out = !!uint32_val;
iter->pos = pos + 4;
break;
case 'y':
if (pos + 1 > iter->len)
return false;
uint8_val = l_get_u8(iter->data + pos);
*(uint8_t *) out = uint8_val;
iter->pos = pos + 1;
break;
case 'n':
if (pos + 2 > iter->len)
return false;
int16_val = l_get_s16(iter->data + pos);
*(int16_t *) out = int16_val;
iter->pos = pos + 2;
break;
case 'q':
if (pos + 2 > iter->len)
return false;
uint16_val = l_get_u16(iter->data + pos);
*(uint16_t *) out = uint16_val;
iter->pos = pos + 2;
break;
case 'i':
if (pos + 4 > iter->len)
return false;
int32_val = l_get_s32(iter->data + pos);
*(int32_t *) out = int32_val;
iter->pos = pos + 4;
break;
case 'u':
case 'h':
if (pos + 4 > iter->len)
return false;
uint32_val = l_get_u32(iter->data + pos);
*(uint32_t *) out = uint32_val;
iter->pos = pos + 4;
break;
case 'x':
if (pos + 8 > iter->len)
return false;
int64_val = l_get_s64(iter->data + pos);
*(int64_t *) out= int64_val;
iter->pos = pos + 8;
break;
case 't':
case 'd':
if (pos + 8 > iter->len)
return false;
uint64_val = l_get_u64(iter->data + pos);
*(uint64_t *) out = uint64_val;
iter->pos = pos + 8;
break;
default:
return false;
}
if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY)
iter->sig_pos += 1;
return true;
}
bool _dbus1_iter_get_fixed_array(struct l_dbus_message_iter *iter,
void *out, uint32_t *n_elem)
{
char type;
uint32_t size;
if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY)
return false;
type = iter->sig_start[iter->sig_pos];
size = get_basic_size(type);
/* Fail if the array is not a fixed size or contains file descriptors */
if (!size || type == 'n')
return false;
/*
* enter_array should already align us to our container type, so
* there is no need to align pos here
*/
*(const void **) out = iter->data + iter->pos;
*n_elem = (iter->len - iter->pos) / size;
return true;
}
bool _dbus1_iter_enter_struct(struct l_dbus_message_iter *iter,
struct l_dbus_message_iter *structure)
{
size_t len;
size_t pos;
const char *sig_start;
const char *sig_end;
bool is_dict = iter->sig_start[iter->sig_pos] == '{';
bool is_struct = iter->sig_start[iter->sig_pos] == '(';
if (!is_dict && !is_struct)
return false;
pos = align_len(iter->pos, 8);
if (pos >= iter->len)
return false;
sig_start = iter->sig_start + iter->sig_pos + 1;
sig_end = _dbus_signature_end(iter->sig_start + iter->sig_pos);
if (!calc_len_next_item(iter->sig_start + iter->sig_pos,
iter->data, pos, iter->len, &len))
return false;
dbus1_iter_init_internal(structure, iter->message,
DBUS_CONTAINER_TYPE_STRUCT,
sig_start, sig_end, iter->data,
len, pos);
if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY)
iter->sig_pos += sig_end - sig_start + 2;
iter->pos = pos + len;
return true;
}
bool _dbus1_iter_enter_variant(struct l_dbus_message_iter *iter,
struct l_dbus_message_iter *variant)
{
size_t pos;
uint8_t sig_len;
size_t len;
const char *sig_start;
if (iter->sig_start[iter->sig_pos] != 'v')
return false;
pos = align_len(iter->pos, 1);
if (pos + 2 > iter->len)
return false;
sig_len = l_get_u8(iter->data + pos);
sig_start = iter->data + pos + 1;
if (!calc_len_next_item(sig_start, iter->data, pos + sig_len + 2,
iter->len, &len))
return false;
dbus1_iter_init_internal(variant, iter->message,
DBUS_CONTAINER_TYPE_VARIANT,
sig_start, NULL, iter->data,
len, pos + sig_len + 2);
if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY)
iter->sig_pos += 1;
iter->pos = pos + sig_len + 2 + len;
return true;
}
bool _dbus1_iter_enter_array(struct l_dbus_message_iter *iter,
struct l_dbus_message_iter *array)
{
size_t pos;
size_t len;
const char *sig_start;
const char *sig_end;
if (iter->sig_start[iter->sig_pos] != 'a')
return false;
sig_start = iter->sig_start + iter->sig_pos + 1;
sig_end = _dbus_signature_end(sig_start) + 1;
pos = align_len(iter->pos, 4);
if (pos + 4 > iter->len)
return false;
len = l_get_u32(iter->data + pos);
pos += 4;
pos = align_len(pos, get_alignment(*sig_start));
dbus1_iter_init_internal(array, iter->message,
DBUS_CONTAINER_TYPE_ARRAY,
sig_start, sig_end,
iter->data, len, pos);
if (iter->container_type != DBUS_CONTAINER_TYPE_ARRAY)
iter->sig_pos += sig_end - sig_start + 1;
iter->pos = pos + len;
return true;
}
bool _dbus1_iter_skip_entry(struct l_dbus_message_iter *iter)
{
size_t len;
const char *sig_end;
sig_end = calc_len_next_item(iter->sig_start + iter->sig_pos,
iter->data, iter->pos, iter->len, &len);
if (!sig_end)
return false;
iter->pos += len;
iter->sig_pos = sig_end - iter->sig_start;
return true;
}
struct dbus_builder {
struct l_string *signature;
void *body;
size_t body_size;
size_t body_pos;
struct l_queue *containers;
struct {
struct container *container;
int sig_end;
size_t body_pos;
} mark;
};
struct container {
size_t start;
enum dbus_container_type type;
char signature[256];
uint8_t sigindex;
};
static struct container *container_new(enum dbus_container_type type,
const char *signature, size_t start)
{
struct container *ret;
ret = l_new(struct container, 1);
ret->type = type;
strcpy(ret->signature, signature);
ret->start = start;
return ret;
}
static void container_free(struct container *container)
{
l_free(container);
}
static inline size_t grow_body(struct dbus_builder *builder,
size_t len, unsigned int alignment)
{
size_t size = align_len(builder->body_pos, alignment);
if (size + len > builder->body_size) {
builder->body = l_realloc(builder->body, size + len);
builder->body_size = size + len;
}
if (size - builder->body_pos > 0)
memset(builder->body + builder->body_pos, 0,
size - builder->body_pos);
builder->body_pos = size + len;
return size;
}
struct dbus_builder *_dbus1_builder_new(void *body, size_t body_size)
{
struct dbus_builder *builder;
struct container *root;
builder = l_new(struct dbus_builder, 1);
builder->signature = l_string_new(63);
builder->containers = l_queue_new();
root = container_new(DBUS_CONTAINER_TYPE_STRUCT, "", 0);
l_queue_push_head(builder->containers, root);
builder->body = body;
builder->body_size = body_size;
builder->body_pos = body_size;
builder->mark.container = root;
builder->mark.sig_end = 0;
builder->mark.body_pos = 0;
return builder;
}
void _dbus1_builder_free(struct dbus_builder *builder)
{
if (unlikely(!builder))
return;
l_string_free(builder->signature);
l_queue_destroy(builder->containers,
(l_queue_destroy_func_t) container_free);
l_free(builder->body);
l_free(builder);
}
bool _dbus1_builder_append_basic(struct dbus_builder *builder,
char type, const void *value)
{
struct container *container = l_queue_peek_head(builder->containers);
size_t start;
unsigned int alignment;
size_t len;
if (unlikely(!builder))
return false;
if (unlikely(!strchr(simple_types, type)))
return false;
alignment = get_alignment(type);
if (!alignment)
return false;
if (l_queue_length(builder->containers) == 1)
l_string_append_c(builder->signature, type);
else if (container->signature[container->sigindex] != type)
return false;
len = get_basic_size(type);
if (len) {
uint32_t b;
start = grow_body(builder, len, alignment);
if (type == 'b') {
b = *(bool *)value;
memcpy(builder->body + start, &b, len);
} else
memcpy(builder->body + start, value, len);
if (container->type != DBUS_CONTAINER_TYPE_ARRAY)
container->sigindex += 1;
return true;
}
len = strlen(value);
if (type == 'g') {
start = grow_body(builder, len + 2, 1);
l_put_u8(len, builder->body + start);
strcpy(builder->body + start + 1, value);
} else {
start = grow_body(builder, len + 5, 4);
l_put_u32(len, builder->body + start);
strcpy(builder->body + start + 4, value);
}
if (container->type != DBUS_CONTAINER_TYPE_ARRAY)
container->sigindex += 1;
return true;
}
static bool enter_struct_dict_common(struct dbus_builder *builder,
const char *signature,
enum dbus_container_type type,
const char open,
const char close)
{
size_t qlen = l_queue_length(builder->containers);
struct container *container = l_queue_peek_head(builder->containers);
size_t start;
if (qlen == 1) {
if (l_string_length(builder->signature) +
strlen(signature) + 2 > 255)
return false;
} else {
/* Verify Signatures Match */
char expect[256];
const char *start;
const char *end;
start = container->signature + container->sigindex;
end = _dbus_signature_end(start);
if (*start != open || *end != close)
return false;
memcpy(expect, start + 1, end - start - 1);
expect[end - start - 1] = '\0';
if (strcmp(expect, signature))
return false;
}
start = grow_body(builder, 0, 8);
container = container_new(type, signature, start);
l_queue_push_head(builder->containers, container);
return true;
}
bool _dbus1_builder_enter_struct(struct dbus_builder *builder,
const char *signature)
{
if (!_dbus_valid_signature(signature))
return false;
return enter_struct_dict_common(builder, signature,
DBUS_CONTAINER_TYPE_STRUCT, '(', ')');
}
bool _dbus1_builder_enter_dict(struct dbus_builder *builder,
const char *signature)
{
if (_dbus_num_children(signature) != 2)
return false;
if (!strchr(simple_types, signature[0]))
return false;
return enter_struct_dict_common(builder, signature,
DBUS_CONTAINER_TYPE_DICT_ENTRY,
'{', '}');
}
static bool leave_struct_dict_common(struct dbus_builder *builder,
enum dbus_container_type type,
const char open,
const char close)
{
struct container *container = l_queue_peek_head(builder->containers);
size_t qlen = l_queue_length(builder->containers);
struct container *parent;
if (unlikely(qlen <= 1))
return false;
if (unlikely(container->type != type))
return false;
l_queue_pop_head(builder->containers);
qlen -= 1;
parent = l_queue_peek_head(builder->containers);
if (qlen == 1)
l_string_append_printf(builder->signature, "%c%s%c",
open,
container->signature,
close);
else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY)
parent->sigindex += strlen(container->signature) + 2;
container_free(container);
return true;
}
bool _dbus1_builder_leave_struct(struct dbus_builder *builder)
{
return leave_struct_dict_common(builder, DBUS_CONTAINER_TYPE_STRUCT,
'(', ')');
}
bool _dbus1_builder_leave_dict(struct dbus_builder *builder)
{
return leave_struct_dict_common(builder,
DBUS_CONTAINER_TYPE_DICT_ENTRY,
'{', '}');
}
bool _dbus1_builder_enter_variant(struct dbus_builder *builder,
const char *signature)
{
size_t qlen = l_queue_length(builder->containers);
struct container *container = l_queue_peek_head(builder->containers);
size_t start;
size_t siglen;
if (_dbus_num_children(signature) != 1)
return false;
if (qlen == 1) {
if (l_string_length(builder->signature) + 1 > 255)
return false;
} else if (container->signature[container->sigindex] != 'v')
return false;
siglen = strlen(signature);
start = grow_body(builder, siglen + 2, 1);
l_put_u8(siglen, builder->body + start);
strcpy(builder->body + start + 1, signature);
container = container_new(DBUS_CONTAINER_TYPE_VARIANT,
signature, start);
l_queue_push_head(builder->containers, container);
return true;
}
bool _dbus1_builder_leave_variant(struct dbus_builder *builder)
{
struct container *container = l_queue_peek_head(builder->containers);
size_t qlen = l_queue_length(builder->containers);
struct container *parent;
if (unlikely(qlen <= 1))
return false;
if (unlikely(container->type != DBUS_CONTAINER_TYPE_VARIANT))
return false;
l_queue_pop_head(builder->containers);
qlen -= 1;
parent = l_queue_peek_head(builder->containers);
if (qlen == 1)
l_string_append_c(builder->signature, 'v');
else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY)
parent->sigindex += 1;
container_free(container);
return true;
}
bool _dbus1_builder_enter_array(struct dbus_builder *builder,
const char *signature)
{
size_t qlen = l_queue_length(builder->containers);
struct container *container = l_queue_peek_head(builder->containers);
size_t start;
int alignment;
if (_dbus_num_children(signature) != 1 &&
!valid_dict_signature(signature))
return false;
if (qlen == 1) {
if (l_string_length(builder->signature) +
strlen(signature) + 1 > 255)
return false;
} else {
/* Verify Signatures Match */
char expect[256];
const char *start;
const char *end;
start = container->signature + container->sigindex;
end = validate_next_type(start);
if (*start != 'a')
return false;
memcpy(expect, start + 1, end - start - 1);
expect[end - start - 1] = '\0';
if (strcmp(expect, signature))
return false;
}
/* First grow the body enough to cover preceding length */
start = grow_body(builder, 4, 4);
/* Now align to element alignment */
alignment = get_alignment(*signature);
grow_body(builder, 0, alignment);
container = container_new(DBUS_CONTAINER_TYPE_ARRAY, signature, start);
l_queue_push_head(builder->containers, container);
return true;
}
bool _dbus1_builder_leave_array(struct dbus_builder *builder)
{
struct container *container = l_queue_peek_head(builder->containers);
size_t qlen = l_queue_length(builder->containers);
struct container *parent;
size_t alignment;
size_t array_start;
if (unlikely(qlen <= 1))
return false;
if (unlikely(container->type != DBUS_CONTAINER_TYPE_ARRAY))
return false;
l_queue_pop_head(builder->containers);
qlen -= 1;
parent = l_queue_peek_head(builder->containers);
if (qlen == 1)
l_string_append_printf(builder->signature, "a%s",
container->signature);
else if (parent->type != DBUS_CONTAINER_TYPE_ARRAY)
parent->sigindex += strlen(container->signature) + 1;
/* Update array length */
alignment = get_alignment(container->signature[0]);
array_start = align_len(container->start + 4, alignment);
l_put_u32(builder->body_pos - array_start,
builder->body + container->start);
container_free(container);
return true;
}
bool _dbus1_builder_mark(struct dbus_builder *builder)
{
struct container *container = l_queue_peek_head(builder->containers);
builder->mark.container = container;
if (l_queue_length(builder->containers) == 1)
builder->mark.sig_end = l_string_length(builder->signature);
else
builder->mark.sig_end = container->sigindex;
builder->mark.body_pos = builder->body_pos;
return true;
}
bool _dbus1_builder_rewind(struct dbus_builder *builder)
{
struct container *container;
while ((container = l_queue_peek_head(builder->containers)) !=
builder->mark.container) {
container_free(container);
l_queue_pop_head(builder->containers);
}
builder->body_pos = builder->mark.body_pos;
if (l_queue_length(builder->containers) == 1)
l_string_truncate(builder->signature, builder->mark.sig_end);
else
container->sigindex = builder->mark.sig_end;
return true;
}
char *_dbus1_builder_finish(struct dbus_builder *builder,
void **body, size_t *body_size)
{
char *signature;
if (unlikely(!builder))
return NULL;
if (unlikely(l_queue_length(builder->containers) != 1))
return NULL;
signature = l_string_unwrap(builder->signature);
builder->signature = NULL;
*body = builder->body;
*body_size = builder->body_pos;
builder->body = NULL;
builder->body_size = 0;
return signature;
}