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_config {
124 char *user_primary_email;
125 const char **user_other_email;
126 size_t user_other_email_length;
127 const char **new_tags;
128 size_t new_tags_length;
129 const char **new_ignore;
130 size_t new_ignore_length;
131 bool maildir_synchronize_flags;
132 const char **search_exclude_tags;
133 size_t search_exclude_tags_length;
137 notmuch_config_destructor (notmuch_config_t *config)
139 if (config->key_file)
140 g_key_file_free (config->key_file);
147 get_config_from_file (notmuch_config_t *config, bool create_new)
149 #define BUF_SIZE 4096
150 char *config_str = NULL;
152 int config_bufsize = BUF_SIZE;
154 GError *error = NULL;
157 FILE *fp = fopen (config->filename, "r");
159 if (errno == ENOENT) {
160 /* If create_new is true, then the caller is prepared for a
161 * default configuration file in the case of FILE NOT FOUND.
164 config->is_new = true;
167 fprintf (stderr, "Configuration file %s not found.\n"
168 "Try running 'notmuch setup' to create a configuration.\n",
172 fprintf (stderr, "Error opening config file '%s': %s\n",
173 config->filename, strerror (errno));
178 config_str = talloc_zero_array (config, char, config_bufsize);
179 if (config_str == NULL) {
180 fprintf (stderr, "Error reading '%s': Out of memory\n", config->filename);
184 while ((len = fread (config_str + config_len, 1,
185 config_bufsize - config_len, fp)) > 0) {
187 if (config_len == config_bufsize) {
188 config_bufsize += BUF_SIZE;
189 config_str = talloc_realloc (config, config_str, char, config_bufsize);
190 if (config_str == NULL) {
191 fprintf (stderr, "Error reading '%s': Failed to reallocate memory\n",
199 fprintf (stderr, "Error reading '%s': I/O error\n", config->filename);
203 if (g_key_file_load_from_data (config->key_file, config_str, config_len,
204 G_KEY_FILE_KEEP_COMMENTS, &error)) {
209 fprintf (stderr, "Error parsing config file '%s': %s\n",
210 config->filename, error->message);
212 g_error_free (error);
219 talloc_free (config_str);
224 /* Open the named notmuch configuration file. If the filename is NULL,
225 * the value of the environment variable $NOTMUCH_CONFIG will be used.
226 * If $NOTMUCH_CONFIG is unset, the default configuration file
227 * ($HOME/.notmuch-config) will be used.
229 * If any error occurs, (out of memory, or a permission-denied error,
230 * etc.), this function will print a message to stderr and return
233 * FILE NOT FOUND: When the specified configuration file (whether from
234 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
235 * exist, the behavior of this function depends on the 'is_new_ret'
238 * If is_new_ret is NULL, then a "file not found" message will be
239 * printed to stderr and NULL will be returned.
241 * If is_new_ret is non-NULL then a default configuration will be
242 * returned and *is_new_ret will be set to 1 on return so that
243 * the caller can recognize this case.
245 * These default configuration settings are determined as
248 * database_path: $MAILDIR, otherwise $HOME/mail
250 * user_name: $NAME variable if set, otherwise
251 * read from /etc/passwd
253 * user_primary_mail: $EMAIL variable if set, otherwise
254 * constructed from the username and
255 * hostname of the current machine.
257 * user_other_email: Not set.
259 * The default configuration also contains comments to guide the
260 * user in editing the file directly.
263 notmuch_config_open (notmuch_database_t *notmuch,
264 const char *filename,
265 notmuch_command_mode_t config_mode)
267 char *notmuch_config_env = NULL;
269 notmuch_config_t *config = talloc_zero (notmuch, notmuch_config_t);
271 if (config == NULL) {
272 fprintf (stderr, "Out of memory.\n");
276 talloc_set_destructor (config, notmuch_config_destructor);
278 /* non-zero defaults */
279 config->maildir_synchronize_flags = true;
282 config->filename = talloc_strdup (config, filename);
283 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
284 config->filename = talloc_strdup (config, notmuch_config_env);
286 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
290 config->key_file = g_key_file_new ();
292 if (config_mode & NOTMUCH_COMMAND_CONFIG_OPEN) {
293 bool create_new = (config_mode & NOTMUCH_COMMAND_CONFIG_CREATE) != 0;
295 if (! get_config_from_file (config, create_new)) {
296 talloc_free (config);
302 g_key_file_set_comment (config->key_file, NULL, NULL,
303 toplevel_config_comment, NULL);
305 for (size_t i = 0; i < ARRAY_SIZE (group_comment_table); i++) {
306 const char *name = group_comment_table[i].group_name;
307 if (! g_key_file_has_group (config->key_file, name)) {
308 /* Force group to exist before adding comment */
309 g_key_file_set_value (config->key_file, name, "dummy_key", "dummy_val");
310 g_key_file_remove_key (config->key_file, name, "dummy_key", NULL);
311 g_key_file_set_comment (config->key_file, name, NULL,
312 group_comment_table[i].comment, NULL);
318 /* Close the given notmuch_config_t object, freeing all resources.
320 * Note: Any changes made to the configuration are *not* saved by this
321 * function. To save changes, call notmuch_config_save before
322 * notmuch_config_close.
325 notmuch_config_close (notmuch_config_t *config)
327 talloc_free (config);
331 _notmuch_config_get_path (notmuch_config_t *config)
333 return config->filename;
335 /* Save any changes made to the notmuch configuration.
337 * Any comments originally in the file will be preserved.
339 * Returns 0 if successful, and 1 in case of any error, (after
340 * printing a description of the error to stderr).
343 notmuch_config_save (notmuch_config_t *config)
346 char *data, *filename;
347 GError *error = NULL;
349 data = g_key_file_to_data (config->key_file, &length, NULL);
351 fprintf (stderr, "Out of memory.\n");
355 /* Try not to overwrite symlinks. */
356 filename = canonicalize_file_name (config->filename);
358 if (errno == ENOENT) {
359 filename = strdup (config->filename);
361 fprintf (stderr, "Out of memory.\n");
366 fprintf (stderr, "Error canonicalizing %s: %s\n", config->filename,
373 if (! g_file_set_contents (filename, data, length, &error)) {
374 if (strcmp (filename, config->filename) != 0) {
375 fprintf (stderr, "Error saving configuration to %s (-> %s): %s\n",
376 config->filename, filename, error->message);
378 fprintf (stderr, "Error saving configuration to %s: %s\n",
379 filename, error->message);
381 g_error_free (error);
393 notmuch_config_is_new (notmuch_config_t *config)
395 return config->is_new;
399 _config_get (notmuch_config_t *config, char **field,
400 const char *group, const char *key)
402 /* read from config file and cache value, if not cached already */
403 if (*field == NULL) {
405 value = g_key_file_get_string (config->key_file, group, key, NULL);
407 *field = talloc_strdup (config, value);
415 _config_set (notmuch_config_t *config, char **field,
416 const char *group, const char *key, const char *value)
418 g_key_file_set_string (config->key_file, group, key, value);
420 /* drop the cached value */
421 talloc_free (*field);
426 _config_get_list (notmuch_config_t *config,
427 const char *section, const char *key,
428 const char ***outlist, size_t *list_length, size_t *ret_length)
432 /* read from config file and cache value, if not cached already */
433 if (*outlist == NULL) {
435 char **inlist = g_key_file_get_string_list (config->key_file,
436 section, key, list_length, NULL);
440 *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
442 for (i = 0; i < *list_length; i++)
443 (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
445 (*outlist)[i] = NULL;
452 *ret_length = *list_length;
458 _config_set_list (notmuch_config_t *config,
459 const char *group, const char *key,
461 size_t length, const char ***config_var )
463 g_key_file_set_string_list (config->key_file, group, key, list, length);
465 /* drop the cached value */
466 talloc_free (*config_var);
471 notmuch_config_get_database_path (notmuch_config_t *config)
473 char *db_path = (char *) _config_get (config, &config->database_path, "database", "path");
475 if (db_path && *db_path != '/') {
476 /* If the path in the configuration file begins with any
477 * character other than /, presume that it is relative to
478 * $HOME and update as appropriate.
480 char *abs_path = talloc_asprintf (config, "%s/%s", getenv ("HOME"), db_path);
481 talloc_free (db_path);
482 db_path = config->database_path = abs_path;
489 notmuch_config_set_database_path (notmuch_config_t *config,
490 const char *database_path)
492 _config_set (config, &config->database_path, "database", "path", database_path);
496 notmuch_config_get_user_name (notmuch_config_t *config)
498 return _config_get (config, &config->user_name, "user", "name");
502 notmuch_config_set_user_name (notmuch_config_t *config,
503 const char *user_name)
505 _config_set (config, &config->user_name, "user", "name", user_name);
509 notmuch_config_get_user_primary_email (notmuch_config_t *config)
511 return _config_get (config, &config->user_primary_email, "user", "primary_email");
515 notmuch_config_set_user_primary_email (notmuch_config_t *config,
516 const char *primary_email)
518 _config_set (config, &config->user_primary_email, "user", "primary_email", primary_email);
522 notmuch_config_get_user_other_email (notmuch_config_t *config, size_t *length)
524 return _config_get_list (config, "user", "other_email",
525 &(config->user_other_email),
526 &(config->user_other_email_length), length);
530 notmuch_config_get_new_tags (notmuch_config_t *config, size_t *length)
532 return _config_get_list (config, "new", "tags",
534 &(config->new_tags_length), length);
538 notmuch_config_get_new_ignore (notmuch_config_t *config, size_t *length)
540 return _config_get_list (config, "new", "ignore",
541 &(config->new_ignore),
542 &(config->new_ignore_length), length);
546 notmuch_config_set_user_other_email (notmuch_config_t *config,
550 _config_set_list (config, "user", "other_email", list, length,
551 &(config->user_other_email));
555 notmuch_config_set_new_tags (notmuch_config_t *config,
559 _config_set_list (config, "new", "tags", list, length,
560 &(config->new_tags));
564 notmuch_config_set_new_ignore (notmuch_config_t *config,
568 _config_set_list (config, "new", "ignore", list, length,
569 &(config->new_ignore));
573 notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length)
575 return _config_get_list (config, "search", "exclude_tags",
576 &(config->search_exclude_tags),
577 &(config->search_exclude_tags_length), length);
581 notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
585 _config_set_list (config, "search", "exclude_tags", list, length,
586 &(config->search_exclude_tags));
590 /* Given a configuration item of the form <group>.<key> return the
591 * component group and key. If any error occurs, print a message on
592 * stderr and return 1. Otherwise, return 0.
594 * Note: This function modifies the original 'item' string.
597 _item_split (char *item, char **group, char **key)
603 period = strchr (item, '.');
604 if (period == NULL || *(period + 1) == '\0') {
606 "Invalid configuration name: %s\n"
607 "(Should be of the form <section>.<item>)\n", item);
617 /* These are more properly called Xapian fields, but the user facing
618 * docs call them prefixes, so make the error message match */
620 validate_field_name (const char *str)
624 if (! g_utf8_validate (str, -1, NULL)) {
625 fprintf (stderr, "Invalid utf8: %s\n", str);
629 key = g_utf8_strrchr (str, -1, '.');
631 INTERNAL_ERROR ("Impossible code path on input: %s\n", str);
637 fprintf (stderr, "Empty prefix name: %s\n", str);
641 if (! unicode_word_utf8 (key)) {
642 fprintf (stderr, "Non-word character in prefix name: %s\n", key);
646 if (key[0] >= 'a' && key[0] <= 'z') {
647 fprintf (stderr, "Prefix names starting with lower case letters are reserved: %s\n", key);
654 #define BUILT_WITH_PREFIX "built_with."
656 typedef struct config_key {
660 bool (*validate)(const char *);
663 static struct config_key
664 config_key_table[] = {
665 { "index.decrypt", true, false, NULL },
666 { "index.header.", true, true, validate_field_name },
667 { "query.", true, true, NULL },
670 static config_key_info_t *
671 _config_key_info (const char *item)
673 for (size_t i = 0; i < ARRAY_SIZE (config_key_table); i++) {
674 if (config_key_table[i].prefix &&
675 strncmp (item, config_key_table[i].name,
676 strlen (config_key_table[i].name)) == 0)
677 return config_key_table + i;
678 if (strcmp (item, config_key_table[i].name) == 0)
679 return config_key_table + i;
685 notmuch_config_command_get (notmuch_database_t *notmuch, char *item)
687 notmuch_config_values_t *list;
689 for (list = notmuch_config_get_values_string (notmuch, item);
690 notmuch_config_values_valid (list);
691 notmuch_config_values_move_to_next (list)) {
692 const char *val = notmuch_config_values_get (list);
699 _set_db_config (notmuch_database_t *notmuch, const char *key, int argc, char **argv)
701 const char *val = "";
704 /* XXX handle lists? */
705 fprintf (stderr, "notmuch config set: at most one value expected for %s\n", key);
713 if (print_status_database ("notmuch config", notmuch,
714 notmuch_database_reopen (notmuch,
715 NOTMUCH_DATABASE_MODE_READ_WRITE)))
718 if (print_status_database ("notmuch config", notmuch,
719 notmuch_database_set_config (notmuch, key, val)))
722 if (print_status_database ("notmuch config", notmuch,
723 notmuch_database_close (notmuch)))
730 notmuch_config_command_set (notmuch_config_t *config, notmuch_database_t *notmuch, char *item,
731 int argc, char *argv[])
734 config_key_info_t *key_info;
736 if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) {
737 fprintf (stderr, "Error: read only option: %s\n", item);
741 key_info = _config_key_info (item);
742 if (key_info && key_info->validate && (! key_info->validate (item)))
745 if (key_info && key_info->in_db) {
746 return _set_db_config (notmuch, item, argc, argv);
749 if (_item_split (item, &group, &key))
752 /* With only the name of an item, we clear it from the
753 * configuration file.
755 * With a single value, we set it as a string.
757 * With multiple values, we set them as a string list.
761 g_key_file_remove_key (config->key_file, group, key, NULL);
764 g_key_file_set_string (config->key_file, group, key, argv[0]);
767 g_key_file_set_string_list (config->key_file, group, key,
768 (const gchar **) argv, argc);
772 return notmuch_config_save (config);
777 _notmuch_config_list_built_with ()
779 printf ("%scompact=%s\n",
781 notmuch_built_with ("compact") ? "true" : "false");
782 printf ("%sfield_processor=%s\n",
784 notmuch_built_with ("field_processor") ? "true" : "false");
785 printf ("%sretry_lock=%s\n",
787 notmuch_built_with ("retry_lock") ? "true" : "false");
791 notmuch_config_command_list (notmuch_database_t *notmuch)
793 notmuch_config_pairs_t *list;
795 _notmuch_config_list_built_with ();
796 for (list = notmuch_config_get_pairs (notmuch, "");
797 notmuch_config_pairs_valid (list);
798 notmuch_config_pairs_move_to_next (list)) {
799 const char *value = notmuch_config_pairs_value (list);
801 printf ("%s=%s\n", notmuch_config_pairs_key (list), value);
803 notmuch_config_pairs_destroy (list);
808 notmuch_config_command (notmuch_config_t *config, notmuch_database_t *notmuch,
809 int argc, char *argv[])
814 opt_index = notmuch_minimal_options ("config", argc, argv);
818 if (notmuch_requested_db_uuid)
819 fprintf (stderr, "Warning: ignoring --uuid=%s\n",
820 notmuch_requested_db_uuid);
822 /* skip at least subcommand argument */
827 fprintf (stderr, "Error: notmuch config requires at least one argument.\n");
831 if (strcmp (argv[0], "get") == 0) {
833 fprintf (stderr, "Error: notmuch config get requires exactly "
837 ret = notmuch_config_command_get (notmuch, argv[1]);
838 } else if (strcmp (argv[0], "set") == 0) {
840 fprintf (stderr, "Error: notmuch config set requires at least "
844 ret = notmuch_config_command_set (config, notmuch, argv[1], argc - 2, argv + 2);
845 } else if (strcmp (argv[0], "list") == 0) {
846 ret = notmuch_config_command_list (notmuch);
848 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
853 return ret ? EXIT_FAILURE : EXIT_SUCCESS;
858 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
860 return config->maildir_synchronize_flags;
864 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
865 bool synchronize_flags)
867 g_key_file_set_boolean (config->key_file,
868 "maildir", "synchronize_flags", synchronize_flags);
869 config->maildir_synchronize_flags = synchronize_flags;