/* * Application configuration * * Copyright (C) 2017 Patrick McDermott * * This file is part of Marquee. * * Marquee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Marquee 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Marquee. If not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include "config.h" #include #include #include struct MqConfig { gchar *profile; gchar *file_name; GKeyFile *key_file; gboolean types_and_cbs_set; GHashTable *types_and_cbs; }; enum type { TYPE_BOOLEAN, TYPE_INTEGER, TYPE_DOUBLE, TYPE_STRING, }; union callback { MqConfigBooleanCallback boolean_cb; MqConfigIntegerCallback integer_cb; MqConfigDoubleCallback double_cb; MqConfigStringCallback string_cb; }; struct callbacks { union callback cb; gpointer user_data; struct callbacks *next; }; struct item { enum type type; struct callbacks *callbacks; }; #define SET_BOOL(NAME, VALUE) mq_config_set_boolean(config, NAME, VALUE) #define SET_INT( NAME, VALUE) mq_config_set_integer(config, NAME, VALUE) #define SET_DBL( NAME, VALUE) mq_config_set_double (config, NAME, VALUE) #define SET_STR( NAME, VALUE) mq_config_set_string (config, NAME, VALUE) #define SET_STRL(NAME, VALUE) mq_config_set_string (config, NAME, VALUE) static void set_defaults(MqConfig *config, MqConfigProfile profile) { config->types_and_cbs_set = FALSE; /* General -> Web Browsing */ SET_STR ("tabs.new", "home"); SET_STR ("tabs.home", ""); /* TODO */ SET_BOOL("tabs.background", TRUE); SET_BOOL("tabs.warn-on-close", TRUE); /* General -> Navigation and Accessibility */ SET_BOOL("navigation.smooth-scrolling", TRUE); SET_BOOL("navigation.tabbing", TRUE); SET_BOOL("navigation.caret", FALSE); SET_BOOL("navigation.spatial", FALSE); /* General -> Spell Checking */ SET_BOOL("spell.enable", TRUE); SET_STRL("spell.langs", "en_US"); // TODO /* General -> Miscellaneous */ SET_BOOL("display.textarea.resize.enable", TRUE); SET_BOOL("devtools.enable", TRUE); /* General -> Compatibility */ SET_STR ("compatibility.user-agent", "default"); SET_BOOL("compatibility.quirks", TRUE); /* Display -> Fonts */ SET_STR ("font.family.default", "sans-serif"); SET_STR ("font.family.monospace", "monospace"); SET_STR ("font.family.serif", "serif"); SET_STR ("font.family.sans-serif", "sans-serif"); SET_STR ("font.family.cursive", "serif"); SET_STR ("font.family.fantasy", "serif"); SET_STR ("font.family.pictograph", "serif"); SET_INT ("font.size.default", 16); SET_INT ("font.size.monospace-default", 13); SET_INT ("font.size.minimum", 0); /* Display -> Languages */ SET_STRL("intl.accept-languages", "en-us"); // TODO SET_STR ("intl.default-charset", "iso-8859-1"); //^ /* Display -> Zoom */ SET_DBL ("zoom.default", 1.00); SET_BOOL("zoom.text-only", FALSE); /* Permissions -> General */ SET_BOOL("permissions.images.auto-load", TRUE); SET_BOOL("permissions.images.favicons.override", FALSE); SET_BOOL("permissions.java.enable", TRUE); SET_BOOL("permissions.javascript.enable", TRUE); SET_BOOL("permissions.plugins.enable", TRUE); /* Permissions -> JavaScript */ SET_BOOL("permissions.javascript.open-windows", FALSE); SET_BOOL("permissions.javascript.fullscreen", TRUE); SET_BOOL("permissions.javascript.modal-dialogs", FALSE); SET_BOOL("permissions.javascript.clipboard", FALSE); /* Permissions -> Data Storage */ SET_BOOL("permissions.javascript.database", TRUE); SET_BOOL("permissions.javascript.storage", TRUE); SET_BOOL("permissions.appcache", TRUE); /* Permissions -> Graphics and Multimedia */ SET_BOOL("canvas.acceleration.enable", FALSE); SET_BOOL("permissions.javascript.webgl", FALSE); SET_BOOL("permissions.javascript.audio", FALSE); SET_BOOL("media.autoplay", TRUE); SET_BOOL("media.force-fullscreen", FALSE); SET_BOOL("permissions.javascript.mediastream.enable", FALSE); SET_BOOL("permissions.javascript.mediasource.enable", FALSE); /* Security and Privacy -> History */ SET_BOOL("privacy.private-browsing.enabled", profile == MQ_CONFIG_PROFILE_PRIVATE); SET_BOOL("privacy.remember.history", profile != MQ_CONFIG_PROFILE_PRIVATE); SET_BOOL("privacy.remember.downloads", profile != MQ_CONFIG_PROFILE_PRIVATE); /* Security and Privacy -> Cookies */ SET_STR ("cookies.accept", "no-third-party"); /* Security and Privacy -> Security */ SET_BOOL("security.xss-auditor.enable", TRUE); /* Security and Privacy -> Network */ SET_BOOL("dns.prefetch.enable", FALSE); /* Hidden window state preferences */ SET_BOOL("window.maximized", FALSE); SET_INT ("window.width", 1024); SET_INT ("window.height", 768); /* Hidden directory preferences */ SET_STR ("directories.open-file", ""); SET_STR ("directories.downloads", g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD)); config->types_and_cbs_set = TRUE; } #undef SET_BOOL #undef SET_INT #undef SET_DBL #undef SET_STR #undef SET_STRL MqConfig * mq_config_new(const gchar *profile, MqConfigProfile profile_type) { MqConfig *config; config = g_new0(MqConfig, 1); config->profile = g_strdup(profile); config->file_name = g_build_filename(g_get_user_config_dir(), PACKAGE, profile, "config", NULL); config->key_file = g_key_file_new(); config->types_and_cbs = g_hash_table_new(g_str_hash, g_int_equal); set_defaults(config, profile_type); return config; } void mq_config_free(MqConfig *config) { g_free(config->profile); g_free(config->file_name); g_key_file_unref(config->key_file); g_hash_table_unref(config->types_and_cbs); g_free(config); } gboolean mq_config_load(MqConfig *config) { gchar *profile_dir; profile_dir = g_build_filename(g_get_user_config_dir(), PACKAGE, config->profile, NULL); g_mkdir_with_parents(profile_dir, 0700); g_free(profile_dir); /* TODO: Handle parsing and ENOENT errors differently? */ return g_key_file_load_from_file(config->key_file, config->file_name, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, NULL); } gboolean mq_config_save(MqConfig *config) { /* TODO: Handle GFileError? */ return g_key_file_save_to_file(config->key_file, config->file_name, NULL); } static void split_name(const gchar *name, gchar **group, gchar **key) { *group = g_strdup(name); *key = strchr(*group, '.'); *(key[0]) = '\0'; ++*key; } gboolean mq_config_get_boolean(MqConfig *config, const gchar *name) { gchar *group; gchar *key; gboolean value; split_name(name, &group, &key); /* TODO: Handle value parsing errors? */ value = g_key_file_get_boolean(config->key_file, group, key, NULL); g_free(group); return value; } gint mq_config_get_integer(MqConfig *config, const gchar *name) { gchar *group; gchar *key; gint value; split_name(name, &group, &key); /* TODO: Handle value parsing errors? */ value = g_key_file_get_integer(config->key_file, group, key, NULL); g_free(group); return value; } gdouble mq_config_get_double(MqConfig *config, const gchar *name) { gchar *group; gchar *key; gdouble value; split_name(name, &group, &key); /* TODO: Handle value parsing errors? */ value = g_key_file_get_double(config->key_file, group, key, NULL); g_free(group); return value; } gchar * mq_config_get_string(MqConfig *config, const gchar *name) { gchar *group; gchar *key; gchar *value; split_name(name, &group, &key); /* TODO: Handle value parsing errors? */ value = g_key_file_get_string(config->key_file, group, key, NULL); g_free(group); return value; } gchar ** mq_config_get_string_list(MqConfig *config, const gchar *name) { gchar *group; gchar *key; gchar **value; split_name(name, &group, &key); /* TODO: Handle value parsing errors? */ value = g_key_file_get_string_list(config->key_file, group, key, NULL, NULL); g_free(group); return value; } static void set_type_or_run_callbacks(MqConfig *config, const gchar *name, gconstpointer value, enum type type) { struct item *item; struct callbacks *cbs; if (!config->types_and_cbs_set) { item = g_malloc(sizeof(*item)); item->type = type; item->callbacks = NULL; g_hash_table_insert(config->types_and_cbs, g_strdup(name), item); } else { item = g_hash_table_lookup(config->types_and_cbs, name); for (cbs = item->callbacks; cbs; cbs = cbs->next) { switch (item->type) { case TYPE_BOOLEAN: cbs->cb.boolean_cb(config, name, *((const gboolean *) value), cbs->user_data); break; case TYPE_INTEGER: cbs->cb.integer_cb(config, name, *((const gint *) value), cbs->user_data); break; case TYPE_DOUBLE: cbs->cb.double_cb(config, name, *((const gdouble *) value), cbs->user_data); break; case TYPE_STRING: cbs->cb.string_cb(config, name, (const gchar *) value, cbs->user_data); break; default: g_assert_not_reached(); break; } } } } void mq_config_set_boolean(MqConfig *config, const gchar *name, gboolean value) { gchar *group; gchar *key; split_name(name, &group, &key); g_key_file_set_boolean(config->key_file, group, key, value); g_free(group); set_type_or_run_callbacks(config, name, &value, TYPE_BOOLEAN); } void mq_config_set_integer(MqConfig *config, const gchar *name, gint value) { gchar *group; gchar *key; split_name(name, &group, &key); g_key_file_set_integer(config->key_file, group, key, value); g_free(group); set_type_or_run_callbacks(config, name, &value, TYPE_INTEGER); } void mq_config_set_double(MqConfig *config, const gchar *name, gdouble value) { gchar *group; gchar *key; split_name(name, &group, &key); g_key_file_set_double(config->key_file, group, key, value); g_free(group); set_type_or_run_callbacks(config, name, &value, TYPE_DOUBLE); } void mq_config_set_string(MqConfig *config, const gchar *name, const gchar *value) { gchar *group; gchar *key; split_name(name, &group, &key); g_key_file_set_string(config->key_file, group, key, value); g_free(group); set_type_or_run_callbacks(config, name, value, TYPE_STRING); } gboolean mq_config_set(MqConfig *config, const gchar *name, const gchar *value) { struct item *item; gint integer_value; gdouble double_value; gchar *endptr; item = g_hash_table_lookup(config->types_and_cbs, name); g_assert(item); switch (item->type) { case TYPE_BOOLEAN: /* value is "on" or "off" (as implemented in * mq_html_input_checkbox()). */ mq_config_set_boolean(config, name, value[1] == 'n' ? TRUE : FALSE); return TRUE; case TYPE_INTEGER: integer_value = strtol(value, &endptr, 10); if (*endptr != '\0') { return FALSE; } mq_config_set_integer(config, name, integer_value); return TRUE; case TYPE_DOUBLE: double_value = strtod(value, &endptr); if (*endptr != '\0') { return FALSE; } mq_config_set_double(config, name, double_value); return TRUE; case TYPE_STRING: mq_config_set_string(config, name, value); return TRUE; default: g_assert_not_reached(); break; } g_assert_not_reached(); return FALSE; } void mq_config_notify_boolean(MqConfig *config, const gchar *name, MqConfigBooleanCallback callback, gpointer user_data) { struct item *item; struct callbacks *cbs; item = g_hash_table_lookup(config->types_and_cbs, name); g_assert(item->type == TYPE_BOOLEAN); cbs = g_malloc(sizeof(*cbs)); cbs->cb.boolean_cb = callback; cbs->user_data = user_data; cbs->next = item->callbacks; item->callbacks = cbs; } void mq_config_notify_integer(MqConfig *config, const gchar *name, MqConfigIntegerCallback callback, gpointer user_data) { struct item *item; struct callbacks *cbs; item = g_hash_table_lookup(config->types_and_cbs, name); g_assert(item->type == TYPE_INTEGER); cbs = g_malloc(sizeof(*cbs)); cbs->cb.integer_cb = callback; cbs->user_data = user_data; cbs->next = item->callbacks; item->callbacks = cbs; } void mq_config_notify_double(MqConfig *config, const gchar *name, MqConfigDoubleCallback callback, gpointer user_data) { struct item *item; struct callbacks *cbs; item = g_hash_table_lookup(config->types_and_cbs, name); g_assert(item->type == TYPE_DOUBLE); cbs = g_malloc(sizeof(*cbs)); cbs->cb.double_cb = callback; cbs->user_data = user_data; cbs->next = item->callbacks; item->callbacks = cbs; } void mq_config_notify_string(MqConfig *config, const gchar *name, MqConfigStringCallback callback, gpointer user_data) { struct item *item; struct callbacks *cbs; item = g_hash_table_lookup(config->types_and_cbs, name); g_assert(item->type == TYPE_STRING); cbs = g_malloc(sizeof(*cbs)); cbs->cb.string_cb = callback; cbs->user_data = user_data; cbs->next = item->callbacks; item->callbacks = cbs; }