]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch-config.c
cli: run uncrustify
[notmuch] / notmuch-config.c
index de9a8a41bc3a625e32d14ade4b02418055086db3..1b079e857d030e894dd4daed5fa5b306b9e1e2b6 100644 (file)
@@ -24,6 +24,8 @@
 #include <netdb.h>
 #include <assert.h>
 
+#include "unicode-util.h"
+
 static const char toplevel_config_comment[] =
     " .notmuch-config - Configuration file for the notmuch mail system\n"
     "\n"
@@ -104,15 +106,17 @@ static const char search_config_comment[] =
 static const char crypto_config_comment[] =
     " Cryptography related configuration\n"
     "\n"
-    " The following option is supported here:\n"
+    " The following old option is now ignored:\n"
     "\n"
-    "\tgpg_path\n"
-    "\t\tbinary name or full path to invoke gpg.\n";
+    "\tgpgpath\n"
+    "\t\tThis option was used by older builds of notmuch to choose\n"
+    "\t\tthe version of gpg to use.\n"
+    "\t\tSetting $PATH is a better approach.\n";
 
 struct _notmuch_config {
     char *filename;
     GKeyFile *key_file;
-    notmuch_bool_t is_new;
+    bool is_new;
 
     char *database_path;
     char *crypto_gpg_path;
@@ -124,7 +128,7 @@ struct _notmuch_config {
     size_t new_tags_length;
     const char **new_ignore;
     size_t new_ignore_length;
-    notmuch_bool_t maildir_synchronize_flags;
+    bool maildir_synchronize_flags;
     const char **search_exclude_tags;
     size_t search_exclude_tags_length;
 };
@@ -147,14 +151,14 @@ get_name_from_passwd_file (void *ctx)
     char *name;
     int e;
 
-    pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
+    pw_buf_size = sysconf (_SC_GETPW_R_SIZE_MAX);
     if (pw_buf_size == -1) pw_buf_size = 64;
     pw_buf = talloc_size (ctx, pw_buf_size);
 
     while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
-                            pw_buf_size, &ignored)) == ERANGE) {
-        pw_buf_size = pw_buf_size * 2;
-        pw_buf = talloc_zero_size(ctx, pw_buf_size);
+                           pw_buf_size, &ignored)) == ERANGE) {
+       pw_buf_size = pw_buf_size * 2;
+       pw_buf = talloc_zero_size (ctx, pw_buf_size);
     }
 
     if (e == 0) {
@@ -182,14 +186,14 @@ get_username_from_passwd_file (void *ctx)
     char *name;
     int e;
 
-    pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
+    pw_buf_size = sysconf (_SC_GETPW_R_SIZE_MAX);
     if (pw_buf_size == -1) pw_buf_size = 64;
     pw_buf = talloc_zero_size (ctx, pw_buf_size);
 
     while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
-                            pw_buf_size, &ignored)) == ERANGE) {
-        pw_buf_size = pw_buf_size * 2;
-        pw_buf = talloc_zero_size(ctx, pw_buf_size);
+                           pw_buf_size, &ignored)) == ERANGE) {
+       pw_buf_size = pw_buf_size * 2;
+       pw_buf = talloc_zero_size (ctx, pw_buf_size);
     }
 
     if (e == 0)
@@ -202,6 +206,84 @@ get_username_from_passwd_file (void *ctx)
     return name;
 }
 
+static bool
+get_config_from_file (notmuch_config_t *config, bool create_new)
+{
+    #define BUF_SIZE 4096
+    char *config_str = NULL;
+    int config_len = 0;
+    int config_bufsize = BUF_SIZE;
+    size_t len;
+    GError *error = NULL;
+    bool ret = false;
+
+    FILE *fp = fopen (config->filename, "r");
+    if (fp == NULL) {
+       if (errno == ENOENT) {
+           /* If create_new is true, then the caller is prepared for a
+            * default configuration file in the case of FILE NOT FOUND.
+            */
+           if (create_new) {
+               config->is_new = true;
+               ret = true;
+           } else {
+               fprintf (stderr, "Configuration file %s not found.\n"
+                        "Try running 'notmuch setup' to create a configuration.\n",
+                        config->filename);
+           }
+       } else {
+           fprintf (stderr, "Error opening config file '%s': %s\n",
+                    config->filename, strerror (errno));
+       }
+       goto out;
+    }
+
+    config_str = talloc_zero_array (config, char, config_bufsize);
+    if (config_str == NULL) {
+       fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename);
+       goto out;
+    }
+
+    while ((len = fread (config_str + config_len, 1,
+                        config_bufsize - config_len, fp)) > 0) {
+       config_len += len;
+       if (config_len == config_bufsize) {
+           config_bufsize += BUF_SIZE;
+           config_str = talloc_realloc (config, config_str, char, config_bufsize);
+           if (config_str == NULL) {
+               fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n",
+                        config->filename);
+               goto out;
+           }
+       }
+    }
+
+    if (ferror (fp)) {
+       fprintf (stderr, "Error reading '%s': I/O error\n", config->filename);
+       goto out;
+    }
+
+    if (g_key_file_load_from_data (config->key_file, config_str, config_len,
+                                  G_KEY_FILE_KEEP_COMMENTS, &error)) {
+       ret = true;
+       goto out;
+    }
+
+    fprintf (stderr, "Error parsing config file '%s': %s\n",
+            config->filename, error->message);
+
+    g_error_free (error);
+
+  out:
+    if (fp)
+       fclose (fp);
+
+    if (config_str)
+       talloc_free (config_str);
+
+    return ret;
+}
+
 /* Open the named notmuch configuration file. If the filename is NULL,
  * the value of the environment variable $NOTMUCH_CONFIG will be used.
  * If $NOTMUCH_CONFIG is unset, the default configuration file
@@ -218,20 +300,20 @@ get_username_from_passwd_file (void *ctx)
  *
  *     If is_new_ret is NULL, then a "file not found" message will be
  *     printed to stderr and NULL will be returned.
-
+ *
  *     If is_new_ret is non-NULL then a default configuration will be
  *     returned and *is_new_ret will be set to 1 on return so that
  *     the caller can recognize this case.
  *
- *     These default configuration settings are determined as
- *     follows:
+ *     These default configuration settings are determined as
+ *     follows:
  *
  *             database_path:          $MAILDIR, otherwise $HOME/mail
  *
  *             user_name:              $NAME variable if set, otherwise
  *                                     read from /etc/passwd
  *
- *             user_primary_mail:      $EMAIL variable if set, otherwise
+ *             user_primary_mail:      $EMAIL variable if set, otherwise
  *                                     constructed from the username and
  *                                     hostname of the current machine.
  *
@@ -243,7 +325,7 @@ get_username_from_passwd_file (void *ctx)
 notmuch_config_t *
 notmuch_config_open (void *ctx,
                     const char *filename,
-                    notmuch_bool_t create_new)
+                    notmuch_config_mode_t config_mode)
 {
     GError *error = NULL;
     size_t tmp;
@@ -255,14 +337,18 @@ notmuch_config_open (void *ctx,
     int file_had_search_group;
     int file_had_crypto_group;
 
-    notmuch_config_t *config = talloc (ctx, notmuch_config_t);
+    notmuch_config_t *config = talloc_zero (ctx, notmuch_config_t);
+
     if (config == NULL) {
        fprintf (stderr, "Out of memory.\n");
        return NULL;
     }
-    
+
     talloc_set_destructor (config, notmuch_config_destructor);
 
+    /* non-zero defaults */
+    config->maildir_synchronize_flags = true;
+
     if (filename) {
        config->filename = talloc_strdup (config, filename);
     } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
@@ -274,49 +360,11 @@ notmuch_config_open (void *ctx,
 
     config->key_file = g_key_file_new ();
 
-    config->is_new = FALSE;
-    config->database_path = NULL;
-    config->user_name = NULL;
-    config->user_primary_email = NULL;
-    config->user_other_email = NULL;
-    config->user_other_email_length = 0;
-    config->new_tags = NULL;
-    config->new_tags_length = 0;
-    config->new_ignore = NULL;
-    config->new_ignore_length = 0;
-    config->maildir_synchronize_flags = TRUE;
-    config->search_exclude_tags = NULL;
-    config->search_exclude_tags_length = 0;
-    config->crypto_gpg_path = NULL;
-
-    if (! g_key_file_load_from_file (config->key_file,
-                                    config->filename,
-                                    G_KEY_FILE_KEEP_COMMENTS,
-                                    &error))
-    {
-       if (error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) {
-           /* If create_new is true, then the caller is prepared for a
-            * default configuration file in the case of FILE NOT
-            * FOUND.
-            */
-           if (create_new) {
-               g_error_free (error);
-               config->is_new = TRUE;
-           } else {
-               fprintf (stderr, "Configuration file %s not found.\n"
-                        "Try running 'notmuch setup' to create a configuration.\n",
-                        config->filename);
-               talloc_free (config);
-               g_error_free (error);
-               return NULL;
-           }
-       }
-       else
-       {
-           fprintf (stderr, "Error reading configuration file %s: %s\n",
-                    config->filename, error->message);
+    if (config_mode & NOTMUCH_CONFIG_OPEN) {
+       bool create_new = (config_mode & NOTMUCH_CONFIG_CREATE) != 0;
+
+       if (! get_config_from_file (config, create_new)) {
            talloc_free (config);
-           g_error_free (error);
            return NULL;
        }
     }
@@ -391,7 +439,7 @@ notmuch_config_open (void *ctx,
     }
 
     if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
-        const char *tags[] = { "unread", "inbox" };
+       const char *tags[] = { "unread", "inbox" };
        notmuch_config_set_new_tags (config, tags, 2);
     }
 
@@ -413,14 +461,10 @@ notmuch_config_open (void *ctx,
        g_key_file_get_boolean (config->key_file,
                                "maildir", "synchronize_flags", &error);
     if (error) {
-       notmuch_config_set_maildir_synchronize_flags (config, TRUE);
+       notmuch_config_set_maildir_synchronize_flags (config, true);
        g_error_free (error);
     }
 
-    if (notmuch_config_get_crypto_gpg_path (config) == NULL) {
-       notmuch_config_set_crypto_gpg_path (config, "gpg");
-    }
-    
     /* Whenever we know of configuration sections that don't appear in
      * the configuration file, we add some comments to help the user
      * understand what can be done. */
@@ -456,11 +500,11 @@ notmuch_config_open (void *ctx,
 }
 
 /* Close the given notmuch_config_t object, freeing all resources.
- * 
+ *
  * Note: Any changes made to the configuration are *not* saved by this
  * function. To save changes, call notmuch_config_save before
  * notmuch_config_close.
-*/
+ */
 void
 notmuch_config_close (notmuch_config_t *config)
 {
@@ -524,7 +568,7 @@ notmuch_config_save (notmuch_config_t *config)
     return 0;
 }
 
-notmuch_bool_t
+bool
 notmuch_config_is_new (notmuch_config_t *config)
 {
     return config->is_new;
@@ -562,13 +606,13 @@ _config_get_list (notmuch_config_t *config,
                  const char *section, const char *key,
                  const char ***outlist, size_t *list_length, size_t *ret_length)
 {
-    assert(outlist);
+    assert (outlist);
 
     /* read from config file and cache value, if not cached already */
     if (*outlist == NULL) {
 
        char **inlist = g_key_file_get_string_list (config->key_file,
-                                            section, key, list_length, NULL);
+                                                   section, key, list_length, NULL);
        if (inlist) {
            unsigned int i;
 
@@ -591,11 +635,11 @@ _config_get_list (notmuch_config_t *config,
 
 static void
 _config_set_list (notmuch_config_t *config,
-                 const char *group, const char *name,
+                 const char *group, const char *key,
                  const char *list[],
                  size_t length, const char ***config_var )
 {
-    g_key_file_set_string_list (config->key_file, group, name, list, length);
+    g_key_file_set_string_list (config->key_file, group, key, list, length);
 
     /* drop the cached value */
     talloc_free (*config_var);
@@ -605,7 +649,19 @@ _config_set_list (notmuch_config_t *config,
 const char *
 notmuch_config_get_database_path (notmuch_config_t *config)
 {
-    return _config_get (config, &config->database_path, "database", "path");
+    char *db_path = (char *) _config_get (config, &config->database_path, "database", "path");
+
+    if (db_path && *db_path != '/') {
+       /* If the path in the configuration file begins with any
+        * character other than /, presume that it is relative to
+        * $HOME and update as appropriate.
+        */
+       char *abs_path = talloc_asprintf (config, "%s/%s", getenv ("HOME"), db_path);
+       talloc_free (db_path);
+       db_path = config->database_path = abs_path;
+    }
+
+    return db_path;
 }
 
 void
@@ -671,16 +727,16 @@ notmuch_config_set_user_other_email (notmuch_config_t *config,
                                     size_t length)
 {
     _config_set_list (config, "user", "other_email", list, length,
-                    &(config->user_other_email));
+                     &(config->user_other_email));
 }
 
 void
 notmuch_config_set_new_tags (notmuch_config_t *config,
-                                    const char *list[],
-                                    size_t length)
+                            const char *list[],
+                            size_t length)
 {
     _config_set_list (config, "new", "tags", list, length,
-                    &(config->new_tags));
+                     &(config->new_tags));
 }
 
 void
@@ -689,7 +745,7 @@ notmuch_config_set_new_ignore (notmuch_config_t *config,
                               size_t length)
 {
     _config_set_list (config, "new", "ignore", list, length,
-                    &(config->new_ignore));
+                     &(config->new_ignore));
 }
 
 const char **
@@ -702,26 +758,13 @@ notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length
 
 void
 notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
-                                     const char *list[],
-                                     size_t length)
+                                       const char *list[],
+                                       size_t length)
 {
     _config_set_list (config, "search", "exclude_tags", list, length,
                      &(config->search_exclude_tags));
 }
 
-const char *
-notmuch_config_get_crypto_gpg_path (notmuch_config_t *config)
-{
-    return _config_get (config, &config->crypto_gpg_path, "crypto", "gpg_path");
-}
-
-void
-notmuch_config_set_crypto_gpg_path (notmuch_config_t *config,
-                             const char *gpg_path)
-{
-    _config_set (config, &config->crypto_gpg_path, "crypto", "gpg_path", gpg_path);
-}
-
 
 /* Given a configuration item of the form <group>.<key> return the
  * component group and key. If any error occurs, print a message on
@@ -737,7 +780,7 @@ _item_split (char *item, char **group, char **key)
     *group = item;
 
     period = strchr (item, '.');
-    if (period == NULL || *(period+1) == '\0') {
+    if (period == NULL || *(period + 1) == '\0') {
        fprintf (stderr,
                 "Invalid configuration name: %s\n"
                 "(Should be of the form <section>.<item>)\n", item);
@@ -750,11 +793,85 @@ _item_split (char *item, char **group, char **key)
     return 0;
 }
 
+/* These are more properly called Xapian fields, but the user facing
+ * docs call them prefixes, so make the error message match */
+static bool
+validate_field_name (const char *str)
+{
+    const char *key;
+
+    if (! g_utf8_validate (str, -1, NULL)) {
+       fprintf (stderr, "Invalid utf8: %s\n", str);
+       return false;
+    }
+
+    key = g_utf8_strrchr (str, -1, '.');
+    if (! key ) {
+       INTERNAL_ERROR ("Impossible code path on input: %s\n", str);
+    }
+
+    key++;
+
+    if (! *key) {
+       fprintf (stderr, "Empty prefix name: %s\n", str);
+       return false;
+    }
+
+    if (! unicode_word_utf8 (key)) {
+       fprintf (stderr, "Non-word character in prefix name: %s\n", key);
+       return false;
+    }
+
+    if (key[0] >= 'a' && key[0] <= 'z') {
+       fprintf (stderr, "Prefix names starting with lower case letters are reserved: %s\n", key);
+       return false;
+    }
+
+    return true;
+}
+
 #define BUILT_WITH_PREFIX "built_with."
-#define QUERY_PREFIX "query."
+
+typedef struct config_key {
+    const char *name;
+    bool in_db;
+    bool prefix;
+    bool (*validate)(const char *);
+} config_key_info_t;
+
+static struct config_key
+    config_key_table[] = {
+    { "index.decrypt",   true,   false,  NULL },
+    { "index.header.",   true,   true,   validate_field_name },
+    { "query.",          true,   true,   NULL },
+};
+
+static config_key_info_t *
+_config_key_info (const char *item)
+{
+    for (size_t i = 0; i < ARRAY_SIZE (config_key_table); i++) {
+       if (config_key_table[i].prefix &&
+           strncmp (item, config_key_table[i].name,
+                    strlen (config_key_table[i].name)) == 0)
+           return config_key_table + i;
+       if (strcmp (item, config_key_table[i].name) == 0)
+           return config_key_table + i;
+    }
+    return NULL;
+}
+
+static bool
+_stored_in_db (const char *item)
+{
+    config_key_info_t *info;
+
+    info = _config_key_info (item);
+
+    return (info && info->in_db);
+}
 
 static int
-_print_db_config(notmuch_config_t *config, const char *name)
+_print_db_config (notmuch_config_t *config, const char *name)
 {
     notmuch_database_t *notmuch;
     char *val;
@@ -769,7 +886,7 @@ _print_db_config(notmuch_config_t *config, const char *name)
                               notmuch_database_get_config (notmuch, name, &val)))
        return EXIT_FAILURE;
 
-     puts (val);
+    puts (val);
 
     return EXIT_SUCCESS;
 }
@@ -777,20 +894,20 @@ _print_db_config(notmuch_config_t *config, const char *name)
 static int
 notmuch_config_command_get (notmuch_config_t *config, char *item)
 {
-    if (strcmp(item, "database.path") == 0) {
+    if (strcmp (item, "database.path") == 0) {
        printf ("%s\n", notmuch_config_get_database_path (config));
-    } else if (strcmp(item, "user.name") == 0) {
+    } else if (strcmp (item, "user.name") == 0) {
        printf ("%s\n", notmuch_config_get_user_name (config));
-    } else if (strcmp(item, "user.primary_email") == 0) {
+    } else if (strcmp (item, "user.primary_email") == 0) {
        printf ("%s\n", notmuch_config_get_user_primary_email (config));
-    } else if (strcmp(item, "user.other_email") == 0) {
+    } else if (strcmp (item, "user.other_email") == 0) {
        const char **other_email;
        size_t i, length;
-       
+
        other_email = notmuch_config_get_user_other_email (config, &length);
        for (i = 0; i < length; i++)
            printf ("%s\n", other_email[i]);
-    } else if (strcmp(item, "new.tags") == 0) {
+    } else if (strcmp (item, "new.tags") == 0) {
        const char **tags;
        size_t i, length;
 
@@ -800,7 +917,7 @@ notmuch_config_command_get (notmuch_config_t *config, char *item)
     } else if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
        printf ("%s\n",
                notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" : "false");
-    } else if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
+    } else if (_stored_in_db (item)) {
        return _print_db_config (config, item);
     } else {
        char **value;
@@ -829,7 +946,7 @@ notmuch_config_command_get (notmuch_config_t *config, char *item)
 }
 
 static int
-_set_db_config(notmuch_config_t *config, const char *key, int argc, char **argv)
+_set_db_config (notmuch_config_t *config, const char *key, int argc, char **argv)
 {
     notmuch_database_t *notmuch;
     const char *val = "";
@@ -865,13 +982,18 @@ static int
 notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[])
 {
     char *group, *key;
+    config_key_info_t *key_info;
 
     if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
        fprintf (stderr, "Error: read only option: %s\n", item);
        return 1;
     }
 
-    if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
+    key_info = _config_key_info (item);
+    if (key_info && key_info->validate && (! key_info->validate (item)))
+       return 1;
+
+    if (key_info && key_info->in_db) {
        return _set_db_config (config, item, argc, argv);
     }
 
@@ -905,12 +1027,15 @@ static
 void
 _notmuch_config_list_built_with ()
 {
-    printf("%scompact=%s\n",
-          BUILT_WITH_PREFIX,
-          notmuch_built_with ("compact") ? "true" : "false");
-    printf("%sfield_processor=%s\n",
-          BUILT_WITH_PREFIX,
-          notmuch_built_with ("field_processor") ? "true" : "false");
+    printf ("%scompact=%s\n",
+           BUILT_WITH_PREFIX,
+           notmuch_built_with ("compact") ? "true" : "false");
+    printf ("%sfield_processor=%s\n",
+           BUILT_WITH_PREFIX,
+           notmuch_built_with ("field_processor") ? "true" : "false");
+    printf ("%sretry_lock=%s\n",
+           BUILT_WITH_PREFIX,
+           notmuch_built_with ("retry_lock") ? "true" : "false");
 }
 
 static int
@@ -931,11 +1056,11 @@ _list_db_config (notmuch_config_t *config)
        return EXIT_FAILURE;
 
     for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
-       printf("%s=%s\n", notmuch_config_list_key (list), notmuch_config_list_value(list));
+       printf ("%s=%s\n", notmuch_config_list_key (list), notmuch_config_list_value (list));
     }
     notmuch_config_list_destroy (list);
 
-   return EXIT_SUCCESS;
+    return EXIT_SUCCESS;
 }
 
 static int
@@ -992,8 +1117,8 @@ notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
                 notmuch_requested_db_uuid);
 
     /* skip at least subcommand argument */
-    argc-= opt_index;
-    argv+= opt_index;
+    argc -= opt_index;
+    argv += opt_index;
 
     if (argc < 1) {
        fprintf (stderr, "Error: notmuch config requires at least one argument.\n");
@@ -1026,7 +1151,7 @@ notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
 
 }
 
-notmuch_bool_t
+bool
 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
 {
     return config->maildir_synchronize_flags;
@@ -1034,7 +1159,7 @@ notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
 
 void
 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
-                                             notmuch_bool_t synchronize_flags)
+                                             bool synchronize_flags)
 {
     g_key_file_set_boolean (config->key_file,
                            "maildir", "synchronize_flags", synchronize_flags);