1 /* notmuch - Not much of an email program, (just index and search)
3 * Copyright © 2009 Carl Worth
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see https://www.gnu.org/licenses/ .
18 * Author: Carl Worth <cworth@cworth.org>
21 #include "notmuch-client.h"
27 #include "unicode-util.h"
29 static const char toplevel_config_comment[] =
30 " .notmuch-config - Configuration file for the notmuch mail system\n"
32 " For more information about notmuch, see https://notmuchmail.org";
35 const char *group_name;
37 } group_comment_table [] = {
40 " Database configuration\n"
42 " The only value supported here is 'path' which should be the top-level\n"
43 " directory where your mail currently exists and to where mail will be\n"
44 " delivered in the future. Files should be individual email messages.\n"
45 " Notmuch will store its database within a sub-directory of the path\n"
46 " configured here named \".notmuch\".\n"
50 " User configuration\n"
52 " Here is where you can let notmuch know how you would like to be\n"
53 " addressed. Valid settings are\n"
55 "\tname Your full name.\n"
56 "\tprimary_email Your primary email address.\n"
57 "\tother_email A list (separated by ';') of other email addresses\n"
58 "\t at which you receive email.\n"
60 " Notmuch will use the various email addresses configured here when\n"
61 " formatting replies. It will avoid including your own addresses in the\n"
62 " recipient list of replies, and will set the From address based on the\n"
63 " address to which the original email was addressed.\n"
67 " Configuration for \"notmuch new\"\n"
69 " The following options are supported here:\n"
71 "\ttags A list (separated by ';') of the tags that will be\n"
72 "\t added to all messages incorporated by \"notmuch new\".\n"
74 "\tignore A list (separated by ';') of file and directory names\n"
75 "\t that will not be searched for messages by \"notmuch new\".\n"
77 "\t NOTE: *Every* file/directory that goes by one of those\n"
78 "\t names will be ignored, independent of its depth/location\n"
79 "\t in the mail store.\n"
83 " Search configuration\n"
85 " The following option is supported here:\n"
88 "\t\tA ;-separated list of tags that will be excluded from\n"
89 "\t\tsearch results by default. Using an excluded tag in a\n"
90 "\t\tquery will override that exclusion.\n"
94 " Maildir compatibility configuration\n"
96 " The following option is supported here:\n"
98 "\tsynchronize_flags Valid values are true and false.\n"
100 "\tIf true, then the following maildir flags (in message filenames)\n"
101 "\twill be synchronized with the corresponding notmuch tags:\n"
109 "\t\tS unread (added when 'S' flag is not present)\n"
111 "\tThe \"notmuch new\" command will notice flag changes in filenames\n"
112 "\tand update tags, while the \"notmuch tag\" and \"notmuch restore\"\n"
113 "\tcommands will notice tag changes and update flags in filenames\n"
117 struct _notmuch_conffile {
124 notmuch_conffile_destructor (notmuch_conffile_t *config)
126 if (config->key_file)
127 g_key_file_free (config->key_file);
133 get_config_from_file (notmuch_conffile_t *config, bool create_new)
135 #define BUF_SIZE 4096
136 char *config_str = NULL;
138 int config_bufsize = BUF_SIZE;
140 GError *error = NULL;
143 FILE *fp = fopen (config->filename, "r");
145 if (errno == ENOENT) {
146 /* If create_new is true, then the caller is prepared for a
147 * default configuration file in the case of FILE NOT FOUND.
150 config->is_new = true;
153 fprintf (stderr, "Configuration file %s not found.\n"
154 "Try running 'notmuch setup' to create a configuration.\n",
158 fprintf (stderr, "Error opening config file '%s': %s\n",
159 config->filename, strerror (errno));
164 config_str = talloc_zero_array (config, char, config_bufsize);
165 if (config_str == NULL) {
166 fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename);
170 while ((len = fread (config_str + config_len, 1,
171 config_bufsize - config_len, fp)) > 0) {
173 if (config_len == config_bufsize) {
174 config_bufsize += BUF_SIZE;
175 config_str = talloc_realloc (config, config_str, char, config_bufsize);
176 if (config_str == NULL) {
177 fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n",
185 fprintf (stderr, "Error reading '%s': I/O error\n", config->filename);
189 if (g_key_file_load_from_data (config->key_file, config_str, config_len,
190 G_KEY_FILE_KEEP_COMMENTS, &error)) {
195 fprintf (stderr, "Error parsing config file '%s': %s\n",
196 config->filename, error->message);
198 g_error_free (error);
205 talloc_free (config_str);
210 /* Open the named notmuch configuration file. If the filename is NULL,
211 * the value of the environment variable $NOTMUCH_CONFIG will be used.
212 * If $NOTMUCH_CONFIG is unset, the default configuration file
213 * ($HOME/.notmuch-config) will be used.
215 * If any error occurs, (out of memory, or a permission-denied error,
216 * etc.), this function will print a message to stderr and return
219 * FILE NOT FOUND: When the specified configuration file (whether from
220 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
221 * exist, the behavior of this function depends on the 'is_new_ret'
224 * If is_new_ret is NULL, then a "file not found" message will be
225 * printed to stderr and NULL will be returned.
227 * If is_new_ret is non-NULL then a default configuration will be
228 * returned and *is_new_ret will be set to 1 on return so that
229 * the caller can recognize this case.
231 * These default configuration settings are determined as
234 * database_path: $MAILDIR, otherwise $HOME/mail
236 * user_name: $NAME variable if set, otherwise
237 * read from /etc/passwd
239 * user_primary_mail: $EMAIL variable if set, otherwise
240 * constructed from the username and
241 * hostname of the current machine.
243 * user_other_email: Not set.
245 * The default configuration also contains comments to guide the
246 * user in editing the file directly.
249 notmuch_conffile_open (notmuch_database_t *notmuch,
250 const char *filename,
253 char *notmuch_config_env = NULL;
255 notmuch_conffile_t *config = talloc_zero (notmuch, notmuch_conffile_t);
257 if (config == NULL) {
258 fprintf (stderr, "Out of memory.\n");
262 talloc_set_destructor (config, notmuch_conffile_destructor);
265 config->filename = talloc_strdup (config, filename);
266 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
267 config->filename = talloc_strdup (config, notmuch_config_env);
269 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
273 config->key_file = g_key_file_new ();
275 if (! get_config_from_file (config, create)) {
276 talloc_free (config);
281 g_key_file_set_comment (config->key_file, NULL, NULL,
282 toplevel_config_comment, NULL);
284 for (size_t i = 0; i < ARRAY_SIZE (group_comment_table); i++) {
285 const char *name = group_comment_table[i].group_name;
286 if (! g_key_file_has_group (config->key_file, name)) {
287 /* Force group to exist before adding comment */
288 g_key_file_set_value (config->key_file, name, "dummy_key", "dummy_val");
289 g_key_file_remove_key (config->key_file, name, "dummy_key", NULL);
290 g_key_file_set_comment (config->key_file, name, NULL,
291 group_comment_table[i].comment, NULL);
297 /* Close the given notmuch_conffile_t object, freeing all resources.
299 * Note: Any changes made to the configuration are *not* saved by this
300 * function. To save changes, call notmuch_conffile_save before
301 * notmuch_conffile_close.
304 notmuch_conffile_close (notmuch_conffile_t *config)
306 talloc_free (config);
309 /* Save any changes made to the notmuch configuration.
311 * Any comments originally in the file will be preserved.
313 * Returns 0 if successful, and 1 in case of any error, (after
314 * printing a description of the error to stderr).
317 notmuch_conffile_save (notmuch_conffile_t *config)
320 char *data, *filename;
321 GError *error = NULL;
323 data = g_key_file_to_data (config->key_file, &length, NULL);
325 fprintf (stderr, "Out of memory.\n");
329 /* Try not to overwrite symlinks. */
330 filename = canonicalize_file_name (config->filename);
332 if (errno == ENOENT) {
333 filename = strdup (config->filename);
335 fprintf (stderr, "Out of memory.\n");
340 fprintf (stderr, "Error canonicalizing %s: %s\n", config->filename,
347 if (! g_file_set_contents (filename, data, length, &error)) {
348 if (strcmp (filename, config->filename) != 0) {
349 fprintf (stderr, "Error saving configuration to %s (-> %s): %s\n",
350 config->filename, filename, error->message);
352 fprintf (stderr, "Error saving configuration to %s: %s\n",
353 filename, error->message);
355 g_error_free (error);
367 notmuch_conffile_is_new (notmuch_conffile_t *config)
369 return config->is_new;
373 _config_set (notmuch_conffile_t *config,
374 const char *group, const char *key, const char *value)
376 g_key_file_set_string (config->key_file, group, key, value);
380 _config_set_list (notmuch_conffile_t *config,
381 const char *group, const char *key,
385 g_key_file_set_string_list (config->key_file, group, key, list, length);
389 notmuch_conffile_set_database_path (notmuch_conffile_t *config,
390 const char *database_path)
392 _config_set (config, "database", "path", database_path);
396 notmuch_conffile_set_user_name (notmuch_conffile_t *config,
397 const char *user_name)
399 _config_set (config, "user", "name", user_name);
403 notmuch_conffile_set_user_primary_email (notmuch_conffile_t *config,
404 const char *primary_email)
406 _config_set (config, "user", "primary_email", primary_email);
410 notmuch_conffile_set_user_other_email (notmuch_conffile_t *config,
414 _config_set_list (config, "user", "other_email", list, length);
418 notmuch_conffile_set_new_tags (notmuch_conffile_t *config,
422 _config_set_list (config, "new", "tags", list, length);
426 notmuch_conffile_set_new_ignore (notmuch_conffile_t *config,
430 _config_set_list (config, "new", "ignore", list, length);
434 notmuch_conffile_set_search_exclude_tags (notmuch_conffile_t *config,
438 _config_set_list (config, "search", "exclude_tags", list, length);
442 /* Given a configuration item of the form <group>.<key> return the
443 * component group and key. If any error occurs, print a message on
444 * stderr and return 1. Otherwise, return 0.
446 * Note: This function modifies the original 'item' string.
449 _item_split (char *item, char **group, char **key)
455 period = strchr (item, '.');
456 if (period == NULL || *(period + 1) == '\0') {
458 "Invalid configuration name: %s\n"
459 "(Should be of the form <section>.<item>)\n", item);
469 /* These are more properly called Xapian fields, but the user facing
470 * docs call them prefixes, so make the error message match */
472 validate_field_name (const char *str)
476 if (! g_utf8_validate (str, -1, NULL)) {
477 fprintf (stderr, "Invalid utf8: %s\n", str);
481 key = g_utf8_strrchr (str, -1, '.');
483 INTERNAL_ERROR ("Impossible code path on input: %s\n", str);
489 fprintf (stderr, "Empty prefix name: %s\n", str);
493 if (! unicode_word_utf8 (key)) {
494 fprintf (stderr, "Non-word character in prefix name: %s\n", key);
498 if (key[0] >= 'a' && key[0] <= 'z') {
499 fprintf (stderr, "Prefix names starting with lower case letters are reserved: %s\n", key);
506 #define BUILT_WITH_PREFIX "built_with."
508 typedef struct config_key {
511 bool (*validate)(const char *);
514 static struct config_key
515 config_key_table[] = {
516 { "index.decrypt", false, NULL },
517 { "index.header.", true, validate_field_name },
518 { "query.", true, NULL },
521 static config_key_info_t *
522 _config_key_info (const char *item)
524 for (size_t i = 0; i < ARRAY_SIZE (config_key_table); i++) {
525 if (config_key_table[i].prefix &&
526 strncmp (item, config_key_table[i].name,
527 strlen (config_key_table[i].name)) == 0)
528 return config_key_table + i;
529 if (strcmp (item, config_key_table[i].name) == 0)
530 return config_key_table + i;
536 notmuch_config_command_get (notmuch_database_t *notmuch, char *item)
538 notmuch_config_values_t *list;
540 for (list = notmuch_config_get_values_string (notmuch, item);
541 notmuch_config_values_valid (list);
542 notmuch_config_values_move_to_next (list)) {
543 const char *val = notmuch_config_values_get (list);
550 _set_db_config (notmuch_database_t *notmuch, const char *key, int argc, char **argv)
552 const char *val = "";
555 /* XXX handle lists? */
556 fprintf (stderr, "notmuch config set: at most one value expected for %s\n", key);
564 if (print_status_database ("notmuch config", notmuch,
565 notmuch_database_reopen (notmuch,
566 NOTMUCH_DATABASE_MODE_READ_WRITE)))
569 if (print_status_database ("notmuch config", notmuch,
570 notmuch_database_set_config (notmuch, key, val)))
573 if (print_status_database ("notmuch config", notmuch,
574 notmuch_database_close (notmuch)))
581 notmuch_config_command_set (notmuch_database_t *notmuch,
582 int argc, char *argv[])
585 config_key_info_t *key_info;
586 notmuch_conffile_t *config;
587 bool update_database = false;
591 notmuch_opt_desc_t options[] = {
592 { .opt_bool = &update_database, .name = "database" },
596 opt_index = parse_arguments (argc, argv, options, 1);
604 fprintf (stderr, "Error: notmuch config set requires at least "
613 if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
614 fprintf (stderr, "Error: read only option: %s\n", item);
618 key_info = _config_key_info (item);
619 if (key_info && key_info->validate && (! key_info->validate (item)))
622 if (update_database) {
623 return _set_db_config (notmuch, item, argc, argv);
626 if (_item_split (item, &group, &key))
629 config = notmuch_conffile_open (notmuch,
630 notmuch_config_path (notmuch), false);
634 /* With only the name of an item, we clear it from the
635 * configuration file.
637 * With a single value, we set it as a string.
639 * With multiple values, we set them as a string list.
643 g_key_file_remove_key (config->key_file, group, key, NULL);
646 g_key_file_set_string (config->key_file, group, key, argv[0]);
649 g_key_file_set_string_list (config->key_file, group, key,
650 (const gchar **) argv, argc);
654 ret = notmuch_conffile_save (config);
656 notmuch_conffile_close (config);
663 _notmuch_config_list_built_with ()
665 printf ("%scompact=%s\n",
667 notmuch_built_with ("compact") ? "true" : "false");
668 printf ("%sfield_processor=%s\n",
670 notmuch_built_with ("field_processor") ? "true" : "false");
671 printf ("%sretry_lock=%s\n",
673 notmuch_built_with ("retry_lock") ? "true" : "false");
677 notmuch_config_command_list (notmuch_database_t *notmuch)
679 notmuch_config_pairs_t *list;
681 _notmuch_config_list_built_with ();
682 for (list = notmuch_config_get_pairs (notmuch, "");
683 notmuch_config_pairs_valid (list);
684 notmuch_config_pairs_move_to_next (list)) {
685 const char *value = notmuch_config_pairs_value (list);
687 printf ("%s=%s\n", notmuch_config_pairs_key (list), value);
689 notmuch_config_pairs_destroy (list);
694 notmuch_config_command (notmuch_database_t *notmuch, int argc, char *argv[])
699 opt_index = notmuch_minimal_options ("config", argc, argv);
703 if (notmuch_requested_db_uuid)
704 fprintf (stderr, "Warning: ignoring --uuid=%s\n",
705 notmuch_requested_db_uuid);
707 /* skip at least subcommand argument */
712 fprintf (stderr, "Error: notmuch config requires at least one argument.\n");
716 if (strcmp (argv[0], "get") == 0) {
718 fprintf (stderr, "Error: notmuch config get requires exactly "
722 ret = notmuch_config_command_get (notmuch, argv[1]);
723 } else if (strcmp (argv[0], "set") == 0) {
724 ret = notmuch_config_command_set (notmuch, argc, argv);
725 } else if (strcmp (argv[0], "list") == 0) {
726 ret = notmuch_config_command_list (notmuch);
728 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
733 return ret ? EXIT_FAILURE : EXIT_SUCCESS;
738 notmuch_conffile_set_maildir_synchronize_flags (notmuch_conffile_t *config,
739 bool synchronize_flags)
741 g_key_file_set_boolean (config->key_file,
742 "maildir", "synchronize_flags", synchronize_flags);