X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=lib%2Fconfig.cc;h=948751bc84b35fd7e2de04ccba14af13e6694ad1;hp=9ca74bf58b816331244f54953b34ede069d12d20;hb=4c79a2dabe38ac72eb9eb21620f2ffca5f1885c6;hpb=3458e3c89c1224d3e2dff7230780589ee3ee4c70 diff --git a/lib/config.cc b/lib/config.cc index 9ca74bf5..948751bc 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ @@ -24,27 +24,60 @@ static const std::string CONFIG_PREFIX = "C"; +struct _notmuch_config_list { + notmuch_database_t *notmuch; + Xapian::TermIterator iterator; + char *current_key; + char *current_val; +}; + +struct _notmuch_config_values { + const char *iterator; + size_t tok_len; + const char *string; + void *children; /* talloc_context */ +}; + +static const char * _notmuch_config_key_to_string (notmuch_config_key_t key); + +static int +_notmuch_config_list_destroy (notmuch_config_list_t *list) +{ + /* invoke destructor w/o deallocating memory */ + list->iterator.~TermIterator(); + return 0; +} + notmuch_status_t notmuch_database_set_config (notmuch_database_t *notmuch, const char *key, const char *value) { notmuch_status_t status; - Xapian::WritableDatabase *db; status = _notmuch_database_ensure_writable (notmuch); if (status) return status; + if (! notmuch->config) { + if ((status = _notmuch_config_load_from_database (notmuch))) + return status; + } + try { - db = static_cast (notmuch->xapian_db); - db->set_metadata (CONFIG_PREFIX + key, value); + notmuch->writable_xapian_db->set_metadata (CONFIG_PREFIX + key, value); } catch (const Xapian::Error &error) { status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; - notmuch->exception_reported = TRUE; + notmuch->exception_reported = true; _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n", - error.get_msg().c_str()); + error.get_msg ().c_str ()); } + + if (status) + return status; + + _notmuch_string_map_set (notmuch->config, key, value); + return NOTMUCH_STATUS_SUCCESS; } @@ -59,9 +92,9 @@ _metadata_value (notmuch_database_t *notmuch, value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX + key); } catch (const Xapian::Error &error) { status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; - notmuch->exception_reported = TRUE; + notmuch->exception_reported = true; _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n", - error.get_msg().c_str()); + error.get_msg ().c_str ()); } return status; } @@ -71,17 +104,374 @@ notmuch_database_get_config (notmuch_database_t *notmuch, const char *key, char **value) { - std::string strval; + const char* stored_val; notmuch_status_t status; + if (! notmuch->config) { + if ((status = _notmuch_config_load_from_database (notmuch))) + return status; + } + if (! value) return NOTMUCH_STATUS_NULL_POINTER; - status = _metadata_value (notmuch, key, strval); + stored_val = _notmuch_string_map_get (notmuch->config, key); + if (! stored_val) { + /* XXX in principle this API should be fixed so empty string + * is distinguished from not found */ + *value = strdup(""); + } else { + *value = strdup (stored_val); + } + + return NOTMUCH_STATUS_SUCCESS; +} + +notmuch_status_t +notmuch_database_get_config_list (notmuch_database_t *notmuch, + const char *prefix, + notmuch_config_list_t **out) +{ + notmuch_config_list_t *list = NULL; + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + + list = talloc (notmuch, notmuch_config_list_t); + if (! list) { + status = NOTMUCH_STATUS_OUT_OF_MEMORY; + goto DONE; + } + + list->notmuch = notmuch; + list->current_key = NULL; + list->current_val = NULL; + + try { + + new(&(list->iterator)) Xapian::TermIterator (notmuch->xapian_db->metadata_keys_begin + (CONFIG_PREFIX + (prefix ? prefix : ""))); + talloc_set_destructor (list, _notmuch_config_list_destroy); + + } catch (const Xapian::Error &error) { + _notmuch_database_log (notmuch, "A Xapian exception occurred getting metadata iterator: %s.\n", + error.get_msg ().c_str ()); + notmuch->exception_reported = true; + status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; + } + + *out = list; + + DONE: + if (status) { + if (list) { + talloc_free (list); + if (status != NOTMUCH_STATUS_XAPIAN_EXCEPTION) + _notmuch_config_list_destroy (list); + } + } else { + talloc_set_destructor (list, _notmuch_config_list_destroy); + } + + return status; +} + +notmuch_bool_t +notmuch_config_list_valid (notmuch_config_list_t *metadata) +{ + if (metadata->iterator == metadata->notmuch->xapian_db->metadata_keys_end ()) + return false; + + return true; +} + +static inline char * _key_from_iterator (notmuch_config_list_t *list) { + return talloc_strdup (list, (*list->iterator).c_str () + CONFIG_PREFIX.length ()); +} + +const char * +notmuch_config_list_key (notmuch_config_list_t *list) +{ + if (list->current_key) + talloc_free (list->current_key); + + list->current_key = _key_from_iterator (list); + + return list->current_key; +} + +const char * +notmuch_config_list_value (notmuch_config_list_t *list) +{ + std::string strval; + notmuch_status_t status; + char *key = _key_from_iterator (list); + + /* TODO: better error reporting?? */ + status = _metadata_value (list->notmuch, key, strval); + if (status) + return NULL; + + if (list->current_val) + talloc_free (list->current_val); + + list->current_val = talloc_strdup (list, strval.c_str ()); + talloc_free (key); + return list->current_val; +} + +void +notmuch_config_list_move_to_next (notmuch_config_list_t *list) +{ + list->iterator++; +} + +void +notmuch_config_list_destroy (notmuch_config_list_t *list) +{ + talloc_free (list); +} + +notmuch_status_t +_notmuch_config_load_from_database (notmuch_database_t *notmuch) +{ + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + notmuch_config_list_t *list; + + if (notmuch->config == NULL) + notmuch->config = _notmuch_string_map_create (notmuch); + + if (unlikely(notmuch->config == NULL)) + return NOTMUCH_STATUS_OUT_OF_MEMORY; + + status = notmuch_database_get_config_list (notmuch, "", &list); if (status) return status; - *value = strdup (strval.c_str ()); + for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { + _notmuch_string_map_append (notmuch->config, + notmuch_config_list_key (list), + notmuch_config_list_value (list)); + } + + return status; +} + +notmuch_config_values_t * +notmuch_config_get_values (notmuch_database_t *notmuch, notmuch_config_key_t key) +{ + notmuch_config_values_t *values = NULL; + bool ok = false; + + const char *key_str = _notmuch_config_key_to_string (key); + + if (! key_str) + goto DONE; + + values = talloc (notmuch, notmuch_config_values_t); + if (unlikely(! values)) + goto DONE; + + values->children = talloc_new (values); + + values->string = _notmuch_string_map_get (notmuch->config, key_str); + if (! values->string) + goto DONE; + + values->iterator = strsplit_len (values->string, ';', &(values->tok_len)); + ok = true; + + DONE: + if (!ok) { + if (values) + talloc_free(values); + return NULL; + } + return values; +} + +notmuch_bool_t +notmuch_config_values_valid (notmuch_config_values_t *values) { + if (! values) + return false; + + return (values->iterator != NULL); +} + +const char * +notmuch_config_values_get (notmuch_config_values_t *values) { + return talloc_strndup (values, values->iterator, values->tok_len); +} + +void +notmuch_config_values_start (notmuch_config_values_t *values) { + if (values == NULL) + return; + if (values->children) { + talloc_free (values->children); + } + + values->children = talloc_new (values); + + values->iterator = strsplit_len (values->string, ';', &(values->tok_len)); +} + +void +notmuch_config_values_move_to_next (notmuch_config_values_t *values) { + values->iterator += values->tok_len; + values->iterator = strsplit_len (values->iterator, ';', &(values->tok_len)); +} + +void +notmuch_config_values_destroy (notmuch_config_values_t *values) { + talloc_free (values); +} + +notmuch_status_t +_notmuch_config_load_from_file (notmuch_database_t *notmuch, + GKeyFile *file) +{ + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + gchar **groups, **keys, *val; + + if (notmuch->config == NULL) + notmuch->config = _notmuch_string_map_create (notmuch); + + if (unlikely(notmuch->config == NULL)) { + status = NOTMUCH_STATUS_OUT_OF_MEMORY; + goto DONE; + } + + for (groups = g_key_file_get_groups (file, NULL); *groups; groups++) { + for (keys = g_key_file_get_keys (file, *groups, NULL, NULL); *keys; keys++) { + char *absolute_key = talloc_asprintf(notmuch, "%s.%s", *groups, *keys); + val = g_key_file_get_value (file, *groups, *keys, NULL); + if (! val) { + status = NOTMUCH_STATUS_FILE_ERROR; + goto DONE; + } + _notmuch_string_map_set (notmuch->config, absolute_key, val); + talloc_free (absolute_key); + if (status) + goto DONE; + } + } + + DONE: + return status; +} + +notmuch_status_t +notmuch_config_get_bool (notmuch_database_t *notmuch, notmuch_config_key_t key, notmuch_bool_t *val) +{ + const char *key_string, *val_string; + + key_string = _notmuch_config_key_to_string (key); + if (! key_string) { + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT; + } + + val_string = _notmuch_string_map_get (notmuch->config, key_string); + if (! val_string) { + *val = FALSE; + return NOTMUCH_STATUS_SUCCESS; + } + + if (strcase_equal (val_string, "false") || strcase_equal (val_string, "no")) + *val = FALSE; + else if (strcase_equal (val_string, "true") || strcase_equal (val_string, "yes")) + *val = TRUE; + else + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT; return NOTMUCH_STATUS_SUCCESS; } + +static const char * +_notmuch_config_key_to_string (notmuch_config_key_t key) { + switch (key) { + case NOTMUCH_CONFIG_DATABASE_PATH: + return "database.path"; + case NOTMUCH_CONFIG_HOOK_DIR: + return "database.hook_dir"; + case NOTMUCH_CONFIG_EXCLUDE_TAGS: + return "search.exclude_tags"; + case NOTMUCH_CONFIG_NEW_TAGS: + return "new.tags"; + case NOTMUCH_CONFIG_NEW_IGNORE: + return "new.ignore"; + case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS: + return "maildir.synchronize_flags"; + case NOTMUCH_CONFIG_PRIMARY_EMAIL: + return "user.primary_email"; + case NOTMUCH_CONFIG_OTHER_EMAIL: + return "user.other_email"; + case NOTMUCH_CONFIG_USER_NAME: + return "user.name"; + default: + return NULL; + } +} + +static const char * +_notmuch_config_default (void *ctx, notmuch_config_key_t key) { + char *path; + + switch (key) { + case NOTMUCH_CONFIG_DATABASE_PATH: + path = getenv ("MAILDIR"); + if (path) + path = talloc_strdup (ctx, path); + else + path = talloc_asprintf (ctx, "%s/mail", + getenv ("HOME")); + return path; + case NOTMUCH_CONFIG_EXCLUDE_TAGS: + return ""; + case NOTMUCH_CONFIG_NEW_TAGS: + return "inbox;unread"; + case NOTMUCH_CONFIG_SYNC_MAILDIR_FLAGS: + return "true"; + case NOTMUCH_CONFIG_HOOK_DIR: + case NOTMUCH_CONFIG_NEW_IGNORE: + case NOTMUCH_CONFIG_USER_NAME: + case NOTMUCH_CONFIG_PRIMARY_EMAIL: + case NOTMUCH_CONFIG_OTHER_EMAIL: + return NULL; + default: + case NOTMUCH_CONFIG_LAST: + INTERNAL_ERROR ("illegal key enum %d", key); + } +} + +notmuch_status_t +_notmuch_config_load_defaults (notmuch_database_t *notmuch) { + notmuch_config_key_t key; + for (key = NOTMUCH_CONFIG_FIRST; + key < NOTMUCH_CONFIG_LAST; + key = notmuch_config_key_t(key + 1)) { + const char *val = notmuch_config_get (notmuch, key); + const char *key_string = _notmuch_config_key_to_string (key); + + val = _notmuch_string_map_get (notmuch->config, key_string); + if (! val) { + _notmuch_string_map_set (notmuch->config, key_string, _notmuch_config_default (notmuch, key)); + } + } + return NOTMUCH_STATUS_SUCCESS; +} + +const char * +notmuch_config_get (notmuch_database_t *notmuch, notmuch_config_key_t key) { + + return _notmuch_string_map_get (notmuch->config, _notmuch_config_key_to_string (key)); +} + +notmuch_status_t +notmuch_config_set (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val) { + + return notmuch_database_set_config (notmuch, _notmuch_config_key_to_string (key), val); +} + +void +_notmuch_config_cache (notmuch_database_t *notmuch, notmuch_config_key_t key, const char *val) { + _notmuch_string_map_set (notmuch->config, _notmuch_config_key_to_string (key), val); +}