]> git.notmuchmail.org Git - notmuch/blobdiff - lib/config.cc
Merge tag '0.31.4'
[notmuch] / lib / config.cc
index 9ca74bf58b816331244f54953b34ede069d12d20..948751bc84b35fd7e2de04ccba14af13e6694ad1 100644 (file)
@@ -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 <david@tethera.net>
  */
 
 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 <Xapian::WritableDatabase *> (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);
+}