/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "str.h"
#include "strescape.h"
#include "dsync-serializer.h"
#include "dsync-deserializer.h"
struct dsync_deserializer {
pool_t pool;
const char *name;
const char *const *required_fields;
const char *const *keys;
unsigned int *required_field_indexes;
unsigned int required_field_count;
};
struct dsync_deserializer_decoder {
pool_t pool;
struct dsync_deserializer *deserializer;
const char *const *values;
unsigned int values_count;
};
static bool field_find(const char *const *names, const char *name,
unsigned int *idx_r)
{
unsigned int i;
for (i = 0; names[i] != NULL; i++) {
if (strcmp(names[i], name) == 0) {
*idx_r = i;
return TRUE;
}
}
return FALSE;
}
int dsync_deserializer_init(const char *name, const char *const *required_fields,
const char *header_line,
struct dsync_deserializer **deserializer_r,
const char **error_r)
{
struct dsync_deserializer *deserializer;
const char **dup_required_fields;
unsigned int i, required_count;
pool_t pool;
*deserializer_r = NULL;
pool = pool_alloconly_create("dsync deserializer", 1024);
deserializer = p_new(pool, struct dsync_deserializer, 1);
deserializer->pool = pool;
deserializer->name = p_strdup(pool, name);
deserializer->keys = (void *)p_strsplit_tabescaped(pool, header_line);
deserializer->required_field_count = required_count =
required_fields == NULL ? 0 :
str_array_length(required_fields);
dup_required_fields = p_new(pool, const char *, required_count + 1);
deserializer->required_field_indexes =
p_new(pool, unsigned int, required_count + 1);
for (i = 0; i < required_count; i++) {
dup_required_fields[i] =
p_strdup(pool, required_fields[i]);
if (!field_find(deserializer->keys, required_fields[i],
&deserializer->required_field_indexes[i])) {
*error_r = t_strdup_printf(
"Header missing required field %s",
required_fields[i]);
pool_unref(&pool);
return -1;
}
}
deserializer->required_fields = dup_required_fields;
*deserializer_r = deserializer;
return 0;
}
void dsync_deserializer_deinit(struct dsync_deserializer **_deserializer)
{
struct dsync_deserializer *deserializer = *_deserializer;
*_deserializer = NULL;
pool_unref(&deserializer->pool);
}
int dsync_deserializer_decode_begin(struct dsync_deserializer *deserializer,
const char *input,
struct dsync_deserializer_decoder **decoder_r,
const char **error_r)
{
struct dsync_deserializer_decoder *decoder;
unsigned int i;
char **values;
pool_t pool;
*decoder_r = NULL;
pool = pool_alloconly_create("dsync deserializer decode", 1024);
decoder = p_new(pool, struct dsync_deserializer_decoder, 1);
decoder->pool = pool;
decoder->deserializer = deserializer;
values = p_strsplit_tabescaped(pool, input);
/* fix NULLs */
for (i = 0; values[i] != NULL; i++) {
if (values[i][0] == NULL_CHR) {
/* NULL? */
if (values[i][1] == '\0')
values[i] = NULL;
else
values[i] += 1;
}
}
decoder->values_count = i;
/* see if all required fields exist */
for (i = 0; i < deserializer->required_field_count; i++) {
unsigned int ridx = deserializer->required_field_indexes[i];
if (ridx >= decoder->values_count || values[ridx] == NULL) {
*error_r = t_strdup_printf("Missing required field %s",
deserializer->required_fields[i]);
pool_unref(&pool);
return -1;
}
}
decoder->values = (void *)values;
*decoder_r = decoder;
return 0;
}
static bool
dsync_deserializer_find_field(struct dsync_deserializer *deserializer,
const char *key, unsigned int *idx_r)
{
unsigned int i;
for (i = 0; deserializer->keys[i] != NULL; i++) {
if (strcmp(deserializer->keys[i], key) == 0) {
*idx_r = i;
return TRUE;
}
}
return FALSE;
}
bool dsync_deserializer_decode_try(struct dsync_deserializer_decoder *decoder,
const char *key, const char **value_r)
{
unsigned int idx;
if (!dsync_deserializer_find_field(decoder->deserializer, key, &idx) ||
idx >= decoder->values_count) {
*value_r = NULL;
return FALSE;
} else {
*value_r = decoder->values[idx];
return *value_r != NULL;
}
}
const char *
dsync_deserializer_decode_get(struct dsync_deserializer_decoder *decoder,
const char *key)
{
const char *value;
if (!dsync_deserializer_decode_try(decoder, key, &value)) {
i_panic("dsync_deserializer_decode_get() "
"used for non-required key %s", key);
}
return value;
}
const char *
dsync_deserializer_decoder_get_name(struct dsync_deserializer_decoder *decoder)
{
return decoder->deserializer->name;
}
void dsync_deserializer_decode_finish(struct dsync_deserializer_decoder **_decoder)
{
struct dsync_deserializer_decoder *decoder = *_decoder;
*_decoder = NULL;
pool_unref(&decoder->pool);
}