ervu-sign-module/src/utils/conf_file_context.c

700 lines
18 KiB
C

#include "conf_file_context.h"
#include "gconf_file.h"
#include "types.h"
#include <assert.h>
#define KEY_FILE_DEFAULT_LIST_SEPARATOR ' '
typedef GKeyFile conf_file_context_s;
#define is_allowed_tcp_port(port_num) (port_num >= 0 && port_num < 65536)
/** Checks if character @c is allowed in location.
* @return 1 - true, 0 - false
*/
static int
allowed_location_char(char c)
{
if (c >= '0' && c <= '9') return 1; /* true */
unsigned char ch = (unsigned char) (c | 0x20); /* lower */
if (ch >= 'a' && ch <= 'z') return 1; /* true */
switch (c) {
/* pct-encoding */
case '%':
/* unreserved */
case '-': case '.': case '_': case '~':
/* sub-delims */
case '!': case '$': case '&': case '\'':
case '(': case ')': case '*': case '+':
case ',': case ';': case '=':
/* gen-delims (allowed) */
case '/': case ':': case '@':
return 1; /* true */
/* gen-delims (not allowed) */
case '?': case '#': case '[': case ']':
default:
return 0; /* false */
}
}
/** Checks if character @c is allowed in uri.
* @return 1 - true, 0 - false
*/
static int
allowed_uri_char(char c)
{
if (c >= '0' && c <= '9') return 1; /* true */
unsigned char ch = (unsigned char) (c | 0x20); /* lower */
if (ch >= 'a' && ch <= 'z') return 1; /* true */
switch (c) {
/* pct-encoding */
case '%':
/* unreserved */
case '-': case '.': case '_': case '~':
/* sub-delims */
case '!': case '$': case '&': case '\'':
case '(': case ')': case '*': case '+':
case ',': case ';': case '=':
/* gen-delims */
case '/': case ':': case '@':
case '?': case '#': case '[': case ']':
return 1; /* true */
default:
return 0; /* false */
}
}
/** Checks if character @c is allowed in host name.
* @return 1 - true, 0 - false
*/
static int
allowed_hostname_char(char c)
{
if (c >= '0' && c <= '9') return 1; /* true */
unsigned char ch = (unsigned char) (c | 0x20); /* lower */
if (ch >= 'a' && ch <= 'z') return 1; /* true */
switch (c) {
/* pct-encoding */
case '%':
/* unreserved */
case '-': case '.': case '_': case '~':
/* sub-delims */
case '!': case '$': case '&': case '\'':
case '(': case ')': case '*': case '+':
case ',': case ';': case '=':
/* gen-delims (allowed) */
case ':': case '[': case ']':
return 1; /* true */
/* gen-delims (not allowed) */
case '/': case '@': case '?': case '#':
default:
return 0; /* false */
}
}
/** Checks the syntax of str_t @location: it is allowed to consist of only
* rfc-3986 defined characters
* @return 1 - true, 0 - false
*/
static int
allowed_location_strt(const str_t* location)
{
assert(location != NULL && !str_t_is_null(*location));
size_t i;
for (i = 0; i < location->len; i++) {
if (!allowed_location_char(location->data[i])) {
LOG_ERROR("location '%.*s' is not valid URI-path: '%c' character is not allowed",
(int)location->len, location->data, location->data[i]);
return 0; /* false */
}
}
return 1; /* true */
}
/** Checks the syntax of NULL-terminated string @location: it is allowed
* to consist of only rfc-3986 defined characters.
* @return 1 - true, 0 - false
*/
static int
allowed_location_string(const char* location)
{
assert(location != NULL);
const char* loc;
for (loc = location; *loc != '\0'; loc++) {
if (!allowed_location_char(*loc)) {
LOG_ERROR("location '%s' is not valid URI-path: '%c' character is not allowed",
location, *loc);
return 0; /* false */
}
}
return 1; /* true */
}
/** Checks the syntax of str_t @uri: it is allowed to consist of only rfc-3986
* defined characters
* @return 1 - true, 0 - false
*/
static int
allowed_uri_strt(const str_t* uri)
{
assert(uri != NULL && !str_t_is_null(*uri));
size_t i;
for (i = 0; i < uri->len; i++) {
if (!allowed_uri_char(uri->data[i])) {
LOG_ERROR("uri '%.*s' is not valid URI: '%c' character is not allowed",
(int)uri->len, uri->data, uri->data[i]);
return 0; /* false */
}
}
return 1; /* true */
}
/** Checks the syntax of NULL-terminated string @uri: it is allowed
* to consist of only rfc-3986 defined characters.
* @return 1 - true, 0 - false
*/
static int
allowed_uri_string(const char* uri)
{
assert(uri != NULL);
const char* u;
for (u = uri; *u != '\0'; u++) {
if (!allowed_uri_char(*u)) {
LOG_ERROR("uri '%s' is not valid URI-path: '%c' character is not allowed",
uri, *u);
return 0; /* false */
}
}
return 1; /* true */
}
/** Checks the syntax of NULL-terminated string @host: it is allowed
* to consist of only rfc-3986 defined characters. And limit hostname length is
* HOST_NAME_MAX
* @return 1 - true, 0 - false
*/
static int
allowed_hostname(const char* host)
{
assert(host != NULL);
size_t len = 0;
const char* h = host;
for (; *h != '\0' && len <= HOST_NAME_MAX ; h++, len++) {
if (!allowed_hostname_char(*h)) {
LOG_ERROR("Hostname '%s' is not valid: '%c' character is not allowed",
host, *h);
return 0;
}
}
if (len == 0 || len > HOST_NAME_MAX) {
LOG_ERROR("Hostname length (%zu) is not allowed: == 0 or > HOST_NAME_MAX(%d)",
len, HOST_NAME_MAX);
return 0; /*false*/
}
return 1; /*true*/
}
static inline void
conf_file_set_boolean_value(const conf_file_field_t *field, int value)
{
*((int*)field->value) = value;
LOG_DEBUG("%s::%s = %s", field->section, field->key, value == TRUE ? "true" : "false");
}
static inline void
conf_file_set_integer_value(const conf_file_field_t *field, int value)
{
*((int*)field->value) = value;
LOG_DEBUG("%s::%s = %d", field->section, field->key, value);
}
static inline void
conf_file_set_integer_list_value(const conf_file_field_t *field, const int_list_t *value)
{
*((int_list_t*)field->value) = *value;
LOG_DEBUG("%s::%s = {", field->section, field->key);;
int_list_print(value);
LOG_DEBUG("}");
}
static inline void
conf_file_set_string_value(const conf_file_field_t *field, char *value)
{
*(char**)field->value = value;
if (field->check != CONF_FILE_VALUE_PSWD) {
if (value != NULL) {
LOG_DEBUG("%s::%s = %s", field->section, field->key, value);
} else {
LOG_DEBUG("%s::%s = NULL", field->section, field->key);
}
} else {
LOG_DEBUG("%s::%s = ******", field->section, field->key);
}
}
static inline void
conf_file_set_string_list_value(const conf_file_field_t *field, const string_list_t *value)
{
*((string_list_t*)field->value) = *value;
LOG_DEBUG("%s::%s = {", field->section, field->key);
string_list_print(value);
LOG_DEBUG("}");
}
static inline void
conf_file_set_strt_value(const conf_file_field_t *field, const str_t *value)
{
*(str_t*)field->value = *value;
if (field->check != CONF_FILE_VALUE_PSWD) {
LOG_DEBUG("%s::%s = %.*s", field->section, field->key, (int)value->len, value->data);
} else {
LOG_DEBUG("%s::%s = ******", field->section, field->key);
}
}
static int
conf_file_set_default_value(const conf_file_field_t *field)
{
assert(field != NULL);
switch (field->type) {
case CONF_FILE_VALUE_BOOLEAN:
conf_file_set_boolean_value(field, *((int*)field->default_value));
break;
case CONF_FILE_VALUE_INTEGER:
conf_file_set_integer_value(field, *((int*)field->default_value));
break;
case CONF_FILE_VALUE_INTEGER_LIST:
{
int_list_t value = {0};
if (int_list_copy(&value, field->default_value)) {
goto error;
}
conf_file_set_integer_list_value(field, &value);
}
break;
case CONF_FILE_VALUE_STRING:
{
char *value = NULL;
char *default_value = *((char**)field->default_value);
if (default_value != NULL) {
value = strdup(default_value);
if (value == NULL) {
LOG_ERROR("Could not copy default value for '%s::%s'",
field->section, field->key);
goto error;
}
}
conf_file_set_string_value(field, value);
}
break;
case CONF_FILE_VALUE_STRING_LIST:
{
string_list_t value = {0};
if (string_list_copy(&value, field->default_value)) {
goto error;
}
conf_file_set_string_list_value(field, &value);
}
break;
case CONF_FILE_VALUE_STRT:
{
str_t value = {0};
if (str_t_copy(&value, field->default_value)) {
goto error;
}
conf_file_set_strt_value(field, &value);
}
break;
default:
goto error;
}
return 0;
error:
LOG_ERROR("conf_file_set_default_value exit with error");
return -1;
}
static int
conf_file_load_value(GKeyFile* ini_file, const conf_file_field_t *field, GError** gerror)
{
assert(ini_file != NULL);
assert(field != NULL && field->value != NULL);
assert(field->key != NULL && field->section != NULL);
assert(gerror != NULL && *gerror == NULL);
switch (field->type) {
case CONF_FILE_VALUE_BOOLEAN:
{
gboolean value = g_key_file_get_boolean(ini_file, field->section, field->key, gerror);
conf_file_set_boolean_value(field, (int)value);
}
break;
case CONF_FILE_VALUE_INTEGER:
{
gint value = g_key_file_get_integer(ini_file, field->section, field->key, gerror);
conf_file_set_integer_value(field, (int)value);
}
break;
case CONF_FILE_VALUE_INTEGER_LIST:
{
int_list_t value = {0};
value.list = g_key_file_get_integer_list(ini_file, field->section, field->key, &(value.size), gerror);
if (value.list == NULL) break;
conf_file_set_integer_list_value(field, &value);
}
break;
case CONF_FILE_VALUE_STRING:
{
char *value = gconf_file_get_string(ini_file, field->section, field->key, gerror);
if (value == NULL) break;
conf_file_set_string_value(field, value);
}
break;
case CONF_FILE_VALUE_STRING_LIST:
{
string_list_t value = {0};
value.list = gconf_file_get_string_list(ini_file, field->section, field->key, &(value.size), gerror);
if (value.list == NULL) break;
conf_file_set_string_list_value(field, &value);
}
break;
case CONF_FILE_VALUE_STRT:
{
str_t value = {0};
if (gconf_file_get_strt(ini_file, field->section, field->key, gerror, &value)) {
break;
}
if (str_t_is_null(value)) break;
conf_file_set_strt_value(field, &value);
}
break;
default:
goto error;
}
if (*gerror != NULL) {
if (field->default_value != NULL && (*gerror)->code != G_KEY_FILE_ERROR_INVALID_VALUE) {
LOG_DEBUG("Conf field '%s::%s' is optional. Skip error (%s)",
field->section, field->key, (*gerror)->message);
g_error_free(*gerror);
*gerror = NULL;
LOG_DEBUG("Set default value for field '%s::%s'", field->section, field->key);
if (conf_file_set_default_value(field)) {
goto error;
}
} else {
goto error;
}
}
return 0;
error:
LOG_ERROR("conf_file_load_value '%s':'%s' exit with error", field->section, field->key);
return -1;
}
static int
conf_file_check_value(const conf_file_field_t *field)
{
assert(field != NULL);
switch (field->check) {
case CONF_FILE_VALUE_NONE:
case CONF_FILE_VALUE_PSWD:
/* do nothing */
break;
case CONF_FILE_VALUE_HOSTNAME:
if (field->type == CONF_FILE_VALUE_STRING) {
char* host = *(char**)field->value;
if (!allowed_hostname(host)) {
LOG_ERROR("%s:%s '%s' is not allowed hostname",
field->section, field->key, host);
goto error;
}
}
break;
case CONF_FILE_VALUE_LOCATION:
if (field->type == CONF_FILE_VALUE_STRING) {
char *location = *(char**)field->value;
if (!allowed_location_string(location)) {
LOG_ERROR("%s:%s '%s' is not allowed location",
field->section, field->key, location);
goto error;
}
} else if (field->type == CONF_FILE_VALUE_STRT) {
str_t *location = (str_t*)field->value;
if (!allowed_location_strt(location)) {
LOG_ERROR("%s:%s '%.*s' is not allowed location", field->section,
field->key, (int)location->len, location->data);
goto error;
}
}
break;
case CONF_FILE_VALUE_TCP_PORT:
if (field->type == CONF_FILE_VALUE_INTEGER) {
int port = *(int*)field->value;
if (!is_allowed_tcp_port(port)) {
LOG_ERROR("%s:%s '%d' not allowed tcp port",
field->section, field->key, port);
goto error;
}
}
break;
case CONF_FILE_VALUE_POSITIVE_INT:
if (field->type == CONF_FILE_VALUE_INTEGER) {
int value = *(int*)field->value;
if (value < 0) {
LOG_ERROR("%s:%s is not valid: %d (expected value >= 0)",
field->section, field->key, value);
goto error;
}
}
break;
case CONF_FILE_VALUE_URI:
if (field->type == CONF_FILE_VALUE_STRING) {
char *uri = *(char**)field->value;
if (uri != NULL) {
if (!allowed_uri_string(uri)) {
LOG_ERROR("%s:%s '%s' is not allowed uri",
field->section, field->key, uri);
goto error;
}
} else if (field->default_value == NULL) {
LOG_ERROR("%s:%s 'NULL' is not allowed uri",
field->section, field->key);
goto error;
}
} else if (field->type == CONF_FILE_VALUE_STRT) {
str_t *uri = (str_t*)field->value;
if (!str_t_is_null(*uri)) {
if (!allowed_uri_strt(uri)) {
LOG_ERROR("%s:%s '%.*s' is not allowed uri", field->section,
field->key, (int)uri->len, uri->data);
goto error;
}
} else if (field->default_value == NULL) {
LOG_ERROR("%s:%s 'NULL' is not allowed uri",
field->section, field->key);
goto error;
}
}
break;
default:
break;
}
return 0;
error:
return -1;
}
int
conf_file_load_from_file(const char *filename, conf_file_field_t* fields, int fields_count)
{
LOG_TRACE("conf_file_load_from_file enter. Config filename: %s", filename);
conf_file_context_t ctx = conf_file_open_file(filename);
if (ctx == NULL) {
goto error;
}
if (conf_file_load_values(ctx, fields, fields_count)) {
goto error;
}
conf_file_close_file(ctx);
LOG_TRACE("conf_file_load_from_file exit");
return 0;
error:
LOG_ERROR("Load configuration from file '%s' FAILED", filename);
conf_file_close_file(ctx);
LOG_ERROR("conf_file_load_from_file exit with error");
return -1;
}
conf_file_context_t
conf_file_open_file(const char *filename)
{
GKeyFile* ini_file = NULL;
GError* gerror = NULL;
LOG_TRACE("conf_file_open_file enter. Config filename: %s", filename);
assert(filename != NULL);
ini_file = g_key_file_new();
if (ini_file == NULL) {
LOG_ERROR("conf_file_open_file failed. Desc: could not create key file instance");
goto error;
}
g_key_file_set_list_separator(ini_file, KEY_FILE_DEFAULT_LIST_SEPARATOR);
if (!g_key_file_load_from_file(ini_file, filename, G_KEY_FILE_KEEP_COMMENTS,
&gerror)) {
goto error;
}
return (conf_file_context_t)ini_file;
error:
LOG_ERROR("Open configuration file '%s' FAILED", filename);
if (gerror != NULL) {
LOG_ERROR("Desc: %s", gerror->message);
g_error_free(gerror);
}
if (ini_file != NULL) {
g_key_file_free(ini_file);
}
LOG_ERROR("conf_file_open_file exit with error");
return NULL;
}
void
conf_file_close_file(conf_file_context_t ctx)
{
if (ctx) {
GKeyFile* ini_file = (GKeyFile*) ctx;
g_key_file_free(ini_file);
}
}
int
conf_file_load_values(const conf_file_context_t ctx, const conf_file_field_t* fields, int fields_count)
{
GKeyFile* ini_file = (GKeyFile*) ctx;
GError* gerror = NULL;
int i = 0;
for (; i < fields_count; i++) {
if (conf_file_load_value(ini_file, &fields[i], &gerror)) {
goto error;
}
if (conf_file_check_value(&fields[i])) {
goto error;
}
}
return 0;
error:
LOG_ERROR("Load configuration FAILED");
if (gerror != NULL) {
LOG_ERROR("Desc: %s", gerror->message);
g_error_free(gerror);
}
LOG_ERROR("conf_file_load_values exit with error");
return -1;
}
int
conf_file_has_section(const conf_file_context_t ctx, const char *section)
{
GKeyFile* ini_file = (GKeyFile*) ctx;
return g_key_file_has_group(ini_file, section) ? 0 : 1;
}