]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch-config.c
cli/config: don't try to open config file for 'notmuch help'
[notmuch] / notmuch-config.c
index db487dbe828ba808b6c3223a8d33df581864d74f..959410cce7b81b95a29969b67f41840610dc9f12 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: Carl Worth <cworth@cworth.org>
  */
@@ -27,7 +27,7 @@
 static const char toplevel_config_comment[] =
     " .notmuch-config - Configuration file for the notmuch mail system\n"
     "\n"
-    " For more information about notmuch, see http://notmuchmail.org";
+    " For more information about notmuch, see https://notmuchmail.org";
 
 static const char database_config_comment[] =
     " Database configuration\n"
@@ -101,12 +101,21 @@ static const char search_config_comment[] =
     "\t\tsearch results by default.  Using an excluded tag in a\n"
     "\t\tquery will override that exclusion.\n";
 
+static const char crypto_config_comment[] =
+    " Cryptography related configuration\n"
+    "\n"
+    " The following option is supported here:\n"
+    "\n"
+    "\tgpg_path\n"
+    "\t\tbinary name or full path to invoke gpg.\n";
+
 struct _notmuch_config {
     char *filename;
     GKeyFile *key_file;
     notmuch_bool_t is_new;
 
     char *database_path;
+    char *crypto_gpg_path;
     char *user_name;
     char *user_primary_email;
     const char **user_other_email;
@@ -193,6 +202,85 @@ get_username_from_passwd_file (void *ctx)
     return name;
 }
 
+static notmuch_bool_t
+get_config_from_file (notmuch_config_t *config, notmuch_bool_t 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;
+    notmuch_bool_t ret = FALSE;
+
+    FILE *fp = fopen(config->filename, "r");
+    if (fp == NULL) {
+       /* 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;
+           goto out;
+       } else if (errno == ENOENT) {
+           fprintf (stderr, "Configuration file %s not found.\n"
+                    "Try running 'notmuch setup' to create a configuration.\n",
+                    config->filename);
+           goto out;
+       } else {
+           fprintf (stderr, "Error opening config file '%s': %s\n"
+                    "Try running 'notmuch setup' to create a configuration.\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
@@ -234,7 +322,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;
@@ -244,8 +332,9 @@ notmuch_config_open (void *ctx,
     int file_had_user_group;
     int file_had_maildir_group;
     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;
@@ -253,6 +342,9 @@ notmuch_config_open (void *ctx,
     
     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"))) {
@@ -264,42 +356,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;
-
-    if (! g_key_file_load_from_file (config->key_file,
-                                    config->filename,
-                                    G_KEY_FILE_KEEP_COMMENTS,
-                                    &error))
-    {
-       /* If create_new is true, then the caller is prepared for a
-        * default configuration file in the case of FILE NOT
-        * FOUND. Otherwise, any read failure is an error.
-        */
-       if (create_new &&
-           error->domain == G_FILE_ERROR &&
-           error->code == G_FILE_ERROR_NOENT)
-       {
-           g_error_free (error);
-           config->is_new = TRUE;
-       }
-       else
-       {
-           fprintf (stderr, "Error reading configuration file %s: %s\n",
-                    config->filename, error->message);
+    if (config_mode & NOTMUCH_CONFIG_OPEN) {
+       notmuch_bool_t 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;
        }
     }
@@ -320,7 +381,7 @@ notmuch_config_open (void *ctx,
     file_had_user_group = g_key_file_has_group (config->key_file, "user");
     file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
     file_had_search_group = g_key_file_has_group (config->key_file, "search");
-
+    file_had_crypto_group = g_key_file_has_group (config->key_file, "crypto");
 
     if (notmuch_config_get_database_path (config) == NULL) {
        char *path = getenv ("MAILDIR");
@@ -400,6 +461,10 @@ notmuch_config_open (void *ctx,
        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. */
@@ -427,6 +492,10 @@ notmuch_config_open (void *ctx,
        g_key_file_set_comment (config->key_file, "search", NULL,
                                search_config_comment, NULL);
 
+    if (! file_had_crypto_group)
+       g_key_file_set_comment (config->key_file, "crypto", NULL,
+                               crypto_config_comment, NULL);
+
     return config;
 }
 
@@ -566,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);
@@ -684,6 +753,20 @@ notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
                      &(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
  * stderr and return 1. Otherwise, return 0.
@@ -711,6 +794,30 @@ _item_split (char *item, char **group, char **key)
     return 0;
 }
 
+#define BUILT_WITH_PREFIX "built_with."
+#define QUERY_PREFIX "query."
+
+static int
+_print_db_config(notmuch_config_t *config, const char *name)
+{
+    notmuch_database_t *notmuch;
+    char *val;
+
+    if (notmuch_database_open (notmuch_config_get_database_path (config),
+                              NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
+       return EXIT_FAILURE;
+
+    /* XXX Handle UUID mismatch? */
+
+    if (print_status_database ("notmuch config", notmuch,
+                              notmuch_database_get_config (notmuch, name, &val)))
+       return EXIT_FAILURE;
+
+     puts (val);
+
+    return EXIT_SUCCESS;
+}
+
 static int
 notmuch_config_command_get (notmuch_config_t *config, char *item)
 {
@@ -734,6 +841,11 @@ notmuch_config_command_get (notmuch_config_t *config, char *item)
        tags = notmuch_config_get_new_tags (config, &length);
        for (i = 0; i < length; i++)
            printf ("%s\n", tags[i]);
+    } 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) {
+       return _print_db_config (config, item);
     } else {
        char **value;
        size_t i, length;
@@ -760,11 +872,53 @@ notmuch_config_command_get (notmuch_config_t *config, char *item)
     return 0;
 }
 
+static int
+_set_db_config(notmuch_config_t *config, const char *key, int argc, char **argv)
+{
+    notmuch_database_t *notmuch;
+    const char *val = "";
+
+    if (argc > 1) {
+       /* XXX handle lists? */
+       fprintf (stderr, "notmuch config set: at most one value expected for %s\n", key);
+       return EXIT_FAILURE;
+    }
+
+    if (argc > 0) {
+       val = argv[0];
+    }
+
+    if (notmuch_database_open (notmuch_config_get_database_path (config),
+                              NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
+       return EXIT_FAILURE;
+
+    /* XXX Handle UUID mismatch? */
+
+    if (print_status_database ("notmuch config", notmuch,
+                              notmuch_database_set_config (notmuch, key, val)))
+       return EXIT_FAILURE;
+
+    if (print_status_database ("notmuch config", notmuch,
+                              notmuch_database_close (notmuch)))
+       return EXIT_FAILURE;
+
+    return EXIT_SUCCESS;
+}
+
 static int
 notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[])
 {
     char *group, *key;
 
+    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) {
+       return _set_db_config (config, item, argc, argv);
+    }
+
     if (_item_split (item, &group, &key))
        return 1;
 
@@ -791,6 +945,46 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char
     return notmuch_config_save (config);
 }
 
+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("%sretry_lock=%s\n",
+          BUILT_WITH_PREFIX,
+          notmuch_built_with ("retry_lock") ? "true" : "false");
+}
+
+static int
+_list_db_config (notmuch_config_t *config)
+{
+    notmuch_database_t *notmuch;
+    notmuch_config_list_t *list;
+
+    if (notmuch_database_open (notmuch_config_get_database_path (config),
+                              NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
+       return EXIT_FAILURE;
+
+    /* XXX Handle UUID mismatch? */
+
+
+    if (print_status_database ("notmuch config", notmuch,
+                              notmuch_database_get_config_list (notmuch, "", &list)))
+       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));
+    }
+    notmuch_config_list_destroy (list);
+
+   return EXIT_SUCCESS;
+}
+
 static int
 notmuch_config_command_list (notmuch_config_t *config)
 {
@@ -826,15 +1020,27 @@ notmuch_config_command_list (notmuch_config_t *config)
 
     g_strfreev (groups);
 
-    return 0;
+    _notmuch_config_list_built_with ();
+    return _list_db_config (config);
 }
 
 int
 notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
 {
     int ret;
+    int opt_index;
+
+    opt_index = notmuch_minimal_options ("config", argc, argv);
+    if (opt_index < 0)
+       return EXIT_FAILURE;
+
+    if (notmuch_requested_db_uuid)
+       fprintf (stderr, "Warning: ignoring --uuid=%s\n",
+                notmuch_requested_db_uuid);
 
-    argc--; argv++; /* skip subcommand argument */
+    /* skip at least subcommand argument */
+    argc-= opt_index;
+    argv+= opt_index;
 
     if (argc < 1) {
        fprintf (stderr, "Error: notmuch config requires at least one argument.\n");