/*
* dLeyna
*
* Copyright (C) 2012-2017 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Ludovic Ferrandis <ludovic.ferrandis@intel.com>
*
*/
#include <string.h>
#include <gio/gio.h>
#include "log.h"
#include "settings.h"
#include "white-list.h"
struct dleyna_settings_t_ {
GKeyFile *keyfile;
gchar *file_name;
gchar *file_path;
/* Global section */
gboolean never_quit;
gchar *connector_name;
guint port;
guint push_host_port;
/* Log section */
dleyna_log_type_t log_type;
int log_level;
/* netf section */
gboolean netf_enabled;
GVariant *netf_entries;
};
#define DLEYNA_SETTINGS_GROUP_GENERAL "general"
#define DLEYNA_SETTINGS_KEY_NEVER_QUIT "never-quit"
#define DLEYNA_SETTINGS_KEY_CONNECTOR_NAME "connector-name"
#define DLEYNA_SETTINGS_KEY_PORT "port"
#define DLEYNA_SETTINGS_KEY_PUSH_HOST_PORT "push-host-port"
#define DLEYNA_SETTINGS_GROUP_LOG "log"
#define DLEYNA_SETTINGS_KEY_LOG_TYPE "log-type"
#define DLEYNA_SETTINGS_KEY_LOG_LEVEL "log-level"
#define DLEYNA_SETTINGS_GROUP_NETF "netf"
#define DLEYNA_SETTINGS_KEY_NETF_ENABLED "netf-enabled"
#define DLEYNA_SETTINGS_KEY_NETF_LIST "netf-list"
#define DLEYNA_SETTINGS_DEFAULT_NEVER_QUIT DLEYNA_NEVER_QUIT
#define DLEYNA_SETTINGS_DEFAULT_CONNECTOR_NAME DLEYNA_CONNECTOR_NAME
#define DLEYNA_SETTINGS_DEFAULT_LOG_TYPE DLEYNA_LOG_TYPE
#define DLEYNA_SETTINGS_DEFAULT_LOG_LEVEL DLEYNA_LOG_LEVEL
#if DLEYNA_LOG_LEVEL & DLEYNA_LOG_LEVEL_INFO
#define DLEYNA_SETTINGS_LOG_KEYS(sys, loc, settings) \
do { \
gchar *str = NULL; \
\
DLEYNA_LOG_DEBUG_NL(); \
DLEYNA_LOG_INFO("Load file [%s]", (settings)->file_path); \
DLEYNA_LOG_DEBUG_NL(); \
\
DLEYNA_LOG_INFO("[General settings]"); \
DLEYNA_LOG_INFO("Never Quit: %s", (settings)->never_quit ? "T" : "F");\
DLEYNA_LOG_DEBUG_NL(); \
DLEYNA_LOG_INFO("Connector Name: %s", (settings)->connector_name);\
DLEYNA_LOG_DEBUG_NL(); \
DLEYNA_LOG_INFO("Port: %u", (settings)->port);\
DLEYNA_LOG_DEBUG_NL(); \
DLEYNA_LOG_INFO("Push host port: %u", (settings)->push_host_port);\
DLEYNA_LOG_DEBUG_NL(); \
\
DLEYNA_LOG_INFO("[Logging settings]"); \
DLEYNA_LOG_INFO("Log Type : %d", (settings)->log_type); \
DLEYNA_LOG_INFO("Log Level: 0x%02X", (settings)->log_level); \
DLEYNA_LOG_DEBUG_NL(); \
\
if ((settings)->netf_entries != NULL) \
str = g_variant_print((settings)->netf_entries, FALSE); \
DLEYNA_LOG_INFO("[Network filtering settings]"); \
DLEYNA_LOG_INFO("Enabled : %s", (settings)->netf_enabled ? "T" : "F");\
DLEYNA_LOG_INFO("Entries: %s", str); \
g_free(str); \
DLEYNA_LOG_DEBUG_NL(); \
} while (0)
#else
#define DLEYNA_SETTINGS_LOG_KEYS(sys, loc, settings)
#endif
static void prv_get_keyfile_path(const gchar *file, gchar **sys_path,
gchar **loc_path)
{
const gchar *loc_dir;
if (sys_path != NULL) {
*sys_path = NULL;
if (*SYS_CONFIG_DIR)
*sys_path = g_strdup_printf("%s/%s", SYS_CONFIG_DIR,
file);
}
if (loc_path != NULL) {
loc_dir = g_get_user_config_dir();
*loc_path = NULL;
if (loc_dir && *loc_dir)
*loc_path = g_strdup_printf("%s/%s", loc_dir,
file);
}
}
static void prv_check_local_keyfile(const gchar *sys_path,
const gchar *loc_path)
{
GFile *sys_file = NULL;
GFile *loc_file;
GFile *loc_dir;
loc_file = g_file_new_for_path(loc_path);
loc_dir = g_file_get_parent(loc_file);
if (g_file_query_exists(loc_file, NULL) || (sys_path == NULL))
goto exit;
if (!g_file_query_exists(loc_dir, NULL)) {
if (!g_file_make_directory(loc_dir, NULL, NULL))
goto exit;
}
sys_file = g_file_new_for_path(sys_path);
(void) g_file_copy(sys_file, loc_file, G_FILE_COPY_TARGET_DEFAULT_PERMS,
NULL, NULL, NULL, NULL);
exit:
if (loc_dir)
g_object_unref(loc_dir);
if (loc_file)
g_object_unref(loc_file);
if (sys_file)
g_object_unref(sys_file);
}
static GKeyFile *prv_load_keyfile(const gchar *filepath)
{
GKeyFile *keyfile = NULL;
if (filepath == NULL)
goto exit;
keyfile = g_key_file_new();
if (!g_key_file_load_from_file(keyfile, filepath,
G_KEY_FILE_KEEP_COMMENTS,
NULL)) {
g_key_file_free(keyfile);
keyfile = NULL;
}
exit:
return keyfile;
}
static GVariant *prv_to_netf_entries(char **list)
{
GVariantBuilder vb;
GVariant *result = NULL;
if ((list != NULL) && (*list != NULL)) {
g_variant_builder_init(&vb, G_VARIANT_TYPE("as"));
while (*list != NULL) {
g_variant_builder_add(&vb, "s", *list);
list++;
}
result = g_variant_ref_sink(g_variant_builder_end(&vb));
}
return result;
}
static int prv_to_log_level(gint *int_list, gsize length)
{
gsize i;
int log_level_value;
int level;
int log_level_array[] = {
DLEYNA_LOG_LEVEL_DISABLED, DLEYNA_LOG_LEVEL_ERROR,
DLEYNA_LOG_LEVEL_CRITICAL, DLEYNA_LOG_LEVEL_WARNING,
DLEYNA_LOG_LEVEL_MESSAGE, DLEYNA_LOG_LEVEL_INFO,
DLEYNA_LOG_LEVEL_DEBUG, DLEYNA_LOG_LEVEL_DEFAULT,
DLEYNA_LOG_LEVEL_ALL };
log_level_value = 0;
/* Take all valid values, even duplicated ones, and skip all others.
* Priority is single value (0,7,8) over multi value (1..6)
* Priority for single values is first found */
for (i = 0; i < length; ++i) {
level = int_list[i];
if (level > 0 && level < 7) {
log_level_value |= log_level_array[level];
} else if ((level == 0) || (level == 7) || (level == 8)) {
log_level_value = log_level_array[level];
break;
}
}
return log_level_value;
}
static dleyna_log_type_t prv_to_log_type(int type)
{
dleyna_log_type_t log_type = DLEYNA_LOG_TYPE_SYSLOG;
switch (type) {
case 0:
break;
case 1:
log_type = DLEYNA_LOG_TYPE_GLIB;
break;
default:
break;
}
return log_type;
}
static void prv_read_keys(dleyna_settings_t *settings)
{
GError *error = NULL;
GKeyFile *keyfile = settings->keyfile;
gboolean b_val;
gint int_val;
gchar *s_val;
guint64 u_val;
gint *int_star;
gsize length;
gchar **list;
b_val = g_key_file_get_boolean(keyfile,
DLEYNA_SETTINGS_GROUP_GENERAL,
DLEYNA_SETTINGS_KEY_NEVER_QUIT,
&error);
if (error == NULL) {
settings->never_quit = b_val;
} else {
g_error_free(error);
error = NULL;
}
s_val = g_key_file_get_string(keyfile,
DLEYNA_SETTINGS_GROUP_GENERAL,
DLEYNA_SETTINGS_KEY_CONNECTOR_NAME,
&error);
if (error == NULL) {
g_free(settings->connector_name);
settings->connector_name = s_val;
} else {
g_error_free(error);
error = NULL;
}
u_val = g_key_file_get_uint64(keyfile,
DLEYNA_SETTINGS_GROUP_GENERAL,
DLEYNA_SETTINGS_KEY_PORT,
&error);
if (error == NULL) {
settings->port = (guint)u_val;
} else {
g_error_free(error);
error = NULL;
}
u_val = g_key_file_get_uint64(keyfile,
DLEYNA_SETTINGS_GROUP_GENERAL,
DLEYNA_SETTINGS_KEY_PUSH_HOST_PORT,
&error);
if (error == NULL) {
settings->push_host_port = (guint)u_val;
} else {
g_error_free(error);
error = NULL;
}
int_val = g_key_file_get_integer(keyfile,
DLEYNA_SETTINGS_GROUP_LOG,
DLEYNA_SETTINGS_KEY_LOG_TYPE,
&error);
if (error == NULL) {
settings->log_type = prv_to_log_type(int_val);
} else {
g_error_free(error);
error = NULL;
}
g_key_file_set_list_separator(keyfile, ',');
int_star = g_key_file_get_integer_list(keyfile,
DLEYNA_SETTINGS_GROUP_LOG,
DLEYNA_SETTINGS_KEY_LOG_LEVEL,
&length,
&error);
if (error == NULL) {
settings->log_level = prv_to_log_level(int_star, length);
g_free(int_star);
} else {
g_error_free(error);
error = NULL;
}
b_val = g_key_file_get_boolean(keyfile,
DLEYNA_SETTINGS_GROUP_NETF,
DLEYNA_SETTINGS_KEY_NETF_ENABLED,
&error);
if (error == NULL) {
settings->netf_enabled = b_val;
} else {
g_error_free(error);
error = NULL;
}
list = g_key_file_get_string_list(keyfile,
DLEYNA_SETTINGS_GROUP_NETF,
DLEYNA_SETTINGS_KEY_NETF_LIST,
NULL,
&error);
if (error == NULL) {
settings->netf_entries = prv_to_netf_entries(list);
g_strfreev(list);
} else {
g_error_free(error);
error = NULL;
}
}
static void prv_init_default(dleyna_settings_t *settings)
{
settings->never_quit = DLEYNA_SETTINGS_DEFAULT_NEVER_QUIT;
settings->connector_name = g_strdup(
DLEYNA_SETTINGS_DEFAULT_CONNECTOR_NAME);
settings->port = 0;
settings->push_host_port = 0;
settings->log_type = DLEYNA_SETTINGS_DEFAULT_LOG_TYPE;
settings->log_level = DLEYNA_SETTINGS_DEFAULT_LOG_LEVEL;
settings->netf_enabled = FALSE;
settings->netf_entries = NULL;
}
static void prv_keyfile_init(dleyna_settings_t *settings,
const gchar *sys_path,
const gchar *loc_path)
{
const gchar *path = loc_path;
settings->keyfile = prv_load_keyfile(loc_path);
if (settings->keyfile == NULL) {
path = sys_path;
settings->keyfile = prv_load_keyfile(sys_path);
}
if (settings->keyfile != NULL) {
prv_read_keys(settings);
dleyna_log_update_type_level(settings->log_type,
settings->log_level);
settings->file_path = g_strdup(path);
}
}
static void prv_keyfile_finalize(dleyna_settings_t *settings)
{
if (settings->netf_entries != NULL) {
g_variant_unref(settings->netf_entries);
settings->netf_entries = NULL;
}
if (settings->keyfile != NULL) {
g_key_file_free(settings->keyfile);
settings->keyfile = NULL;
}
g_free(settings->file_path);
settings->file_path = NULL;
}
void dleyna_settings_new(const gchar *server, dleyna_settings_t **settings)
{
gchar *sys_path = NULL;
gchar *loc_path = NULL;
const gchar *server_name;
*settings = g_malloc0(sizeof(**settings));
server_name = strrchr(server, '/');
if (server_name)
server_name++;
else
server_name = server;
(*settings)->file_name = g_strdup_printf("%s%s", server_name, ".conf");
prv_init_default(*settings);
prv_get_keyfile_path((*settings)->file_name, &sys_path, &loc_path);
if (loc_path)
prv_check_local_keyfile(sys_path, loc_path);
if (sys_path || loc_path)
prv_keyfile_init(*settings, sys_path, loc_path);
DLEYNA_SETTINGS_LOG_KEYS(sys_path, loc_path, *settings);
g_free(sys_path);
g_free(loc_path);
}
void dleyna_settings_delete(dleyna_settings_t *settings)
{
g_free(settings->connector_name);
g_free(settings->file_name);
prv_keyfile_finalize(settings);
g_free(settings);
}
const gchar *dleyna_settings_connector_name(dleyna_settings_t *settings)
{
return settings->connector_name;
}
guint dleyna_settings_port(dleyna_settings_t *settings)
{
return settings->port;
}
guint dleyna_settings_push_host_port(dleyna_settings_t *settings)
{
return settings->push_host_port;
}
static void prv_save_settings_to_file(dleyna_settings_t *settings,
GError **error)
{
gchar *data;
gsize length;
DLEYNA_LOG_DEBUG("Enter");
data = g_key_file_to_data(settings->keyfile, &length , NULL);
DLEYNA_LOG_DEBUG_NL();
DLEYNA_LOG_DEBUG("\n%s", data);
DLEYNA_LOG_DEBUG_NL();
(void) g_file_set_contents(settings->file_path, data, length, error);
g_free(data);
}
gboolean dleyna_settings_is_never_quit(dleyna_settings_t *settings)
{
return settings->never_quit;
}
void dleyna_settings_set_never_quit(dleyna_settings_t *settings,
gboolean never_quit,
GError **error)
{
DLEYNA_LOG_DEBUG("Enter");
g_key_file_set_boolean(settings->keyfile,
DLEYNA_SETTINGS_GROUP_GENERAL,
DLEYNA_SETTINGS_KEY_NEVER_QUIT,
never_quit);
prv_save_settings_to_file(settings, error);
if (*error == NULL)
settings->never_quit = never_quit;
DLEYNA_LOG_DEBUG("Exit");
}
gboolean dleyna_settings_is_white_list_enabled(dleyna_settings_t *settings)
{
return settings->netf_enabled;
}
void dleyna_settings_set_white_list_enabled(dleyna_settings_t *settings,
gboolean enabled,
GError **error)
{
DLEYNA_LOG_DEBUG("Enter");
g_key_file_set_boolean(settings->keyfile,
DLEYNA_SETTINGS_GROUP_NETF,
DLEYNA_SETTINGS_KEY_NETF_ENABLED,
enabled);
prv_save_settings_to_file(settings, error);
if (*error == NULL)
settings->netf_enabled = enabled;
DLEYNA_LOG_DEBUG("Exit");
}
GVariant *dleyna_settings_white_list_entries(dleyna_settings_t *settings)
{
return settings->netf_entries;
}
static const gchar **prv_filter_null_str(const gchar **orig_strv,
gsize orig_len,
gsize *new_len)
{
const gchar **strv = NULL;
gsize i;
gsize n = 0;
if (orig_strv && orig_len) {
strv = g_new(const gchar *, orig_len + 1);
for (i = 0; i < orig_len; i++) {
if (*orig_strv[i])
strv[n++] = orig_strv[i];
}
strv[n] = NULL;
}
*new_len = n;
return strv;
}
void dleyna_settings_set_white_list_entries(dleyna_settings_t *settings,
GVariant *entries,
GError **error)
{
const gchar **list;
gsize length;
const gchar **list2;
gsize length2;
DLEYNA_LOG_DEBUG("Enter");
list = g_variant_get_strv(entries, &length);
list2 = prv_filter_null_str(list, length, &length2);
g_key_file_set_string_list(settings->keyfile,
DLEYNA_SETTINGS_GROUP_NETF,
DLEYNA_SETTINGS_KEY_NETF_LIST,
list2, length2);
prv_save_settings_to_file(settings, error);
if (*error == NULL) {
g_variant_unref(settings->netf_entries);
settings->netf_entries = g_variant_ref(entries);
}
g_free(list);
g_free(list2);
DLEYNA_LOG_DEBUG("Exit");
}