X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=notmuch-config.c;h=934827874b1eb3548fa6b9fa1fdf4d9955174ee6;hp=61fda3ea4bafd49ef14d075c2cef70c8d5f2a20c;hb=447ad6b4984c71881b7f97641c77f0a39b71a991;hpb=ad6a6edb38e428f7bbcd81b49fd2ef1e5197f8c7 diff --git a/notmuch-config.c b/notmuch-config.c index 61fda3ea..93482787 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -49,8 +49,9 @@ static const char new_config_comment[] = "\tignore A list (separated by ';') of file and directory names\n" "\t that will not be searched for messages by \"notmuch new\".\n" "\n" - "\t NOTE: *Every* file/directory that goes by one of those names will\n" - "\t be ignored, independent of its depth/location in the mail store.\n"; + "\t NOTE: *Every* file/directory that goes by one of those\n" + "\t names will be ignored, independent of its depth/location\n" + "\t in the mail store.\n"; static const char user_config_comment[] = " User configuration\n" @@ -100,11 +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; @@ -215,9 +226,10 @@ get_username_from_passwd_file (void *ctx) * These default configuration settings are determined as * follows: * - * database_path: $HOME/mail + * database_path: $MAILDIR, otherwise $HOME/mail * - * user_name: From /etc/passwd + * user_name: $NAME variable if set, otherwise + * read from /etc/passwd * * user_primary_mail: $EMAIL variable if set, otherwise * constructed from the username and @@ -231,10 +243,9 @@ get_username_from_passwd_file (void *ctx) notmuch_config_t * notmuch_config_open (void *ctx, const char *filename, - notmuch_bool_t *is_new_ret) + notmuch_bool_t create_new) { GError *error = NULL; - int is_new = 0; size_t tmp; char *notmuch_config_env = NULL; int file_had_database_group; @@ -242,9 +253,7 @@ notmuch_config_open (void *ctx, int file_had_user_group; int file_had_maildir_group; int file_had_search_group; - - if (is_new_ret) - *is_new_ret = 0; + int file_had_crypto_group; notmuch_config_t *config = talloc (ctx, notmuch_config_t); if (config == NULL) { @@ -265,6 +274,7 @@ 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; @@ -277,23 +287,29 @@ notmuch_config_open (void *ctx, 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 the caller passed a non-NULL value for is_new_ret, 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 (is_new_ret && - error->domain == G_FILE_ERROR && - error->code == G_FILE_ERROR_NOENT) - { - g_error_free (error); - is_new = 1; + 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 { @@ -321,17 +337,25 @@ 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 = talloc_asprintf (config, "%s/mail", - getenv ("HOME")); + char *path = getenv ("MAILDIR"); + if (path) + path = talloc_strdup (config, path); + else + path = talloc_asprintf (config, "%s/mail", + getenv ("HOME")); notmuch_config_set_database_path (config, path); talloc_free (path); } if (notmuch_config_get_user_name (config) == NULL) { - char *name = get_name_from_passwd_file (config); + char *name = getenv ("NAME"); + if (name) + name = talloc_strdup (config, name); + else + name = get_name_from_passwd_file (config); notmuch_config_set_user_name (config, name); talloc_free (name); } @@ -376,8 +400,9 @@ notmuch_config_open (void *ctx, } if (notmuch_config_get_search_exclude_tags (config, &tmp) == NULL) { - if (is_new) { - /* We do not set default search_exclude_tags for 0.12 */ + if (config->is_new) { + const char *tags[] = { "deleted", "spam" }; + notmuch_config_set_search_exclude_tags (config, tags, 2); } else { notmuch_config_set_search_exclude_tags (config, NULL, 0); } @@ -392,46 +417,40 @@ 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. */ - if (is_new) - { + if (config->is_new) g_key_file_set_comment (config->key_file, NULL, NULL, toplevel_config_comment, NULL); - } if (! file_had_database_group) - { g_key_file_set_comment (config->key_file, "database", NULL, database_config_comment, NULL); - } if (! file_had_new_group) - { g_key_file_set_comment (config->key_file, "new", NULL, new_config_comment, NULL); - } if (! file_had_user_group) - { g_key_file_set_comment (config->key_file, "user", NULL, user_config_comment, NULL); - } if (! file_had_maildir_group) - { g_key_file_set_comment (config->key_file, "maildir", NULL, maildir_config_comment, NULL); - } - if (! file_had_search_group) { + if (! file_had_search_group) g_key_file_set_comment (config->key_file, "search", NULL, search_config_comment, NULL); - } - if (is_new_ret) - *is_new_ret = is_new; + if (! file_had_crypto_group) + g_key_file_set_comment (config->key_file, "crypto", NULL, + crypto_config_comment, NULL); return config; } @@ -459,7 +478,7 @@ int notmuch_config_save (notmuch_config_t *config) { size_t length; - char *data; + char *data, *filename; GError *error = NULL; data = g_key_file_to_data (config->key_file, &length, NULL); @@ -468,18 +487,76 @@ notmuch_config_save (notmuch_config_t *config) return 1; } - if (! g_file_set_contents (config->filename, data, length, &error)) { - fprintf (stderr, "Error saving configuration to %s: %s\n", - config->filename, error->message); + /* Try not to overwrite symlinks. */ + filename = canonicalize_file_name (config->filename); + if (! filename) { + if (errno == ENOENT) { + filename = strdup (config->filename); + if (! filename) { + fprintf (stderr, "Out of memory.\n"); + g_free (data); + return 1; + } + } else { + fprintf (stderr, "Error canonicalizing %s: %s\n", config->filename, + strerror (errno)); + g_free (data); + return 1; + } + } + + if (! g_file_set_contents (filename, data, length, &error)) { + if (strcmp (filename, config->filename) != 0) { + fprintf (stderr, "Error saving configuration to %s (-> %s): %s\n", + config->filename, filename, error->message); + } else { + fprintf (stderr, "Error saving configuration to %s: %s\n", + filename, error->message); + } g_error_free (error); + free (filename); g_free (data); return 1; } + free (filename); g_free (data); return 0; } +notmuch_bool_t +notmuch_config_is_new (notmuch_config_t *config) +{ + return config->is_new; +} + +static const char * +_config_get (notmuch_config_t *config, char **field, + const char *group, const char *key) +{ + /* read from config file and cache value, if not cached already */ + if (*field == NULL) { + char *value; + value = g_key_file_get_string (config->key_file, group, key, NULL); + if (value) { + *field = talloc_strdup (config, value); + free (value); + } + } + return *field; +} + +static void +_config_set (notmuch_config_t *config, char **field, + const char *group, const char *key, const char *value) +{ + g_key_file_set_string (config->key_file, group, key, value); + + /* drop the cached value */ + talloc_free (*field); + *field = NULL; +} + static const char ** _config_get_list (notmuch_config_t *config, const char *section, const char *key, @@ -487,6 +564,7 @@ _config_get_list (notmuch_config_t *config, { 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, @@ -518,6 +596,8 @@ _config_set_list (notmuch_config_t *config, size_t length, const char ***config_var ) { g_key_file_set_string_list (config->key_file, group, name, list, length); + + /* drop the cached value */ talloc_free (*config_var); *config_var = NULL; } @@ -525,85 +605,40 @@ _config_set_list (notmuch_config_t *config, const char * notmuch_config_get_database_path (notmuch_config_t *config) { - char *path; - - if (config->database_path == NULL) { - path = g_key_file_get_string (config->key_file, - "database", "path", NULL); - if (path) { - config->database_path = talloc_strdup (config, path); - free (path); - } - } - - return config->database_path; + return _config_get (config, &config->database_path, "database", "path"); } void notmuch_config_set_database_path (notmuch_config_t *config, const char *database_path) { - g_key_file_set_string (config->key_file, - "database", "path", database_path); - - talloc_free (config->database_path); - config->database_path = NULL; + _config_set (config, &config->database_path, "database", "path", database_path); } const char * notmuch_config_get_user_name (notmuch_config_t *config) { - char *name; - - if (config->user_name == NULL) { - name = g_key_file_get_string (config->key_file, - "user", "name", NULL); - if (name) { - config->user_name = talloc_strdup (config, name); - free (name); - } - } - - return config->user_name; + return _config_get (config, &config->user_name, "user", "name"); } void notmuch_config_set_user_name (notmuch_config_t *config, const char *user_name) { - g_key_file_set_string (config->key_file, - "user", "name", user_name); - - talloc_free (config->user_name); - config->user_name = NULL; + _config_set (config, &config->user_name, "user", "name", user_name); } const char * notmuch_config_get_user_primary_email (notmuch_config_t *config) { - char *email; - - if (config->user_primary_email == NULL) { - email = g_key_file_get_string (config->key_file, - "user", "primary_email", NULL); - if (email) { - config->user_primary_email = talloc_strdup (config, email); - free (email); - } - } - - return config->user_primary_email; + return _config_get (config, &config->user_primary_email, "user", "primary_email"); } void notmuch_config_set_user_primary_email (notmuch_config_t *config, const char *primary_email) { - g_key_file_set_string (config->key_file, - "user", "primary_email", primary_email); - - talloc_free (config->user_primary_email); - config->user_primary_email = NULL; + _config_set (config, &config->user_primary_email, "user", "primary_email", primary_email); } const char ** @@ -674,6 +709,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 . return the * component group and key. If any error occurs, print a message on * stderr and return 1. Otherwise, return 0. @@ -687,7 +736,7 @@ _item_split (char *item, char **group, char **key) *group = item; - period = index (item, '.'); + period = strchr (item, '.'); if (period == NULL || *(period+1) == '\0') { fprintf (stderr, "Invalid configuration name: %s\n" @@ -702,14 +751,8 @@ _item_split (char *item, char **group, char **key) } static int -notmuch_config_command_get (void *ctx, char *item) +notmuch_config_command_get (notmuch_config_t *config, char *item) { - notmuch_config_t *config; - - config = notmuch_config_open (ctx, NULL, NULL); - if (config == NULL) - return 1; - if (strcmp(item, "database.path") == 0) { printf ("%s\n", notmuch_config_get_database_path (config)); } else if (strcmp(item, "user.name") == 0) { @@ -750,28 +793,20 @@ notmuch_config_command_get (void *ctx, char *item) for (i = 0; i < length; i++) printf ("%s\n", value[i]); - free (value); + g_strfreev (value); } - notmuch_config_close (config); - return 0; } static int -notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[]) +notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[]) { - notmuch_config_t *config; char *group, *key; - int ret; if (_item_split (item, &group, &key)) return 1; - config = notmuch_config_open (ctx, NULL, NULL); - if (config == NULL) - return 1; - /* With only the name of an item, we clear it from the * configuration file. * @@ -792,30 +827,90 @@ notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[]) break; } - ret = notmuch_config_save (config); - notmuch_config_close (config); + return notmuch_config_save (config); +} + +static int +notmuch_config_command_list (notmuch_config_t *config) +{ + char **groups; + size_t g, groups_length; + + groups = g_key_file_get_groups (config->key_file, &groups_length); + if (groups == NULL) + return 1; + + for (g = 0; g < groups_length; g++) { + char **keys; + size_t k, keys_length; + + keys = g_key_file_get_keys (config->key_file, + groups[g], &keys_length, NULL); + if (keys == NULL) + continue; + + for (k = 0; k < keys_length; k++) { + char *value; + + value = g_key_file_get_string (config->key_file, + groups[g], keys[k], NULL); + if (value != NULL) { + printf ("%s.%s=%s\n", groups[g], keys[k], value); + free (value); + } + } + + g_strfreev (keys); + } + + g_strfreev (groups); - return ret; + return 0; } int -notmuch_config_command (void *ctx, int argc, char *argv[]) +notmuch_config_command (notmuch_config_t *config, int argc, char *argv[]) { - argc--; argv++; /* skip subcommand argument */ + int ret; + int opt_index; - if (argc < 2) { - fprintf (stderr, "Error: notmuch config requires at least two arguments.\n"); - return 1; + opt_index = notmuch_minimal_options ("config", argc, argv); + if (opt_index < 0) + return EXIT_FAILURE; + + /* 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"); + return EXIT_FAILURE; + } + + if (strcmp (argv[0], "get") == 0) { + if (argc != 2) { + fprintf (stderr, "Error: notmuch config get requires exactly " + "one argument.\n"); + return EXIT_FAILURE; + } + ret = notmuch_config_command_get (config, argv[1]); + } else if (strcmp (argv[0], "set") == 0) { + if (argc < 2) { + fprintf (stderr, "Error: notmuch config set requires at least " + "one argument.\n"); + return EXIT_FAILURE; + } + ret = notmuch_config_command_set (config, argv[1], argc - 2, argv + 2); + } else if (strcmp (argv[0], "list") == 0) { + ret = notmuch_config_command_list (config); + } else { + fprintf (stderr, "Unrecognized argument for notmuch config: %s\n", + argv[0]); + return EXIT_FAILURE; } - if (strcmp (argv[0], "get") == 0) - return notmuch_config_command_get (ctx, argv[1]); - else if (strcmp (argv[0], "set") == 0) - return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2); + return ret ? EXIT_FAILURE : EXIT_SUCCESS; - fprintf (stderr, "Unrecognized argument for notmuch config: %s\n", - argv[0]); - return 1; } notmuch_bool_t