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 http://www.gnu.org/licenses/ .
18 * Author: Carl Worth <cworth@cworth.org>
21 #include "notmuch-client.h"
27 static const char toplevel_config_comment[] =
28 " .notmuch-config - Configuration file for the notmuch mail system\n"
30 " For more information about notmuch, see http://notmuchmail.org";
32 static const char database_config_comment[] =
33 " Database configuration\n"
35 " The only value supported here is 'path' which should be the top-level\n"
36 " directory where your mail currently exists and to where mail will be\n"
37 " delivered in the future. Files should be individual email messages.\n"
38 " Notmuch will store its database within a sub-directory of the path\n"
39 " configured here named \".notmuch\".\n";
41 static const char new_config_comment[] =
42 " Configuration for \"notmuch new\"\n"
44 " The following options are supported here:\n"
46 "\ttags A list (separated by ';') of the tags that will be\n"
47 "\t added to all messages incorporated by \"notmuch new\".\n"
49 "\tignore A list (separated by ';') of file and directory names\n"
50 "\t that will not be searched for messages by \"notmuch new\".\n";
52 static const char user_config_comment[] =
53 " User configuration\n"
55 " Here is where you can let notmuch know how you would like to be\n"
56 " addressed. Valid settings are\n"
58 "\tname Your full name.\n"
59 "\tprimary_email Your primary email address.\n"
60 "\tother_email A list (separated by ';') of other email addresses\n"
61 "\t at which you receive email.\n"
63 " Notmuch will use the various email addresses configured here when\n"
64 " formatting replies. It will avoid including your own addresses in the\n"
65 " recipient list of replies, and will set the From address based on the\n"
66 " address to which the original email was addressed.\n";
68 static const char maildir_config_comment[] =
69 " Maildir compatibility configuration\n"
71 " The following option is supported here:\n"
73 "\tsynchronize_flags Valid values are true and false.\n"
75 "\tIf true, then the following maildir flags (in message filenames)\n"
76 "\twill be synchronized with the corresponding notmuch tags:\n"
84 "\t\tS unread (added when 'S' flag is not present)\n"
86 "\tThe \"notmuch new\" command will notice flag changes in filenames\n"
87 "\tand update tags, while the \"notmuch tag\" and \"notmuch restore\"\n"
88 "\tcommands will notice tag changes and update flags in filenames\n";
90 static const char search_config_comment[] =
91 " Search configuration\n"
93 " The following option is supported here:\n"
96 "\t\tA ;-separated list of tags that will be excluded from\n"
97 "\t\tsearch results by default. Using an excluded tag in a\n"
98 "\t\tquery will override that exclusion.\n";
100 struct _notmuch_config {
106 char *user_primary_email;
107 const char **user_other_email;
108 size_t user_other_email_length;
109 const char **new_tags;
110 size_t new_tags_length;
111 const char **new_ignore;
112 size_t new_ignore_length;
113 notmuch_bool_t maildir_synchronize_flags;
114 const char **search_exclude_tags;
115 size_t search_exclude_tags_length;
119 notmuch_config_destructor (notmuch_config_t *config)
121 if (config->key_file)
122 g_key_file_free (config->key_file);
128 get_name_from_passwd_file (void *ctx)
132 struct passwd passwd, *ignored;
136 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
137 if (pw_buf_size == -1) pw_buf_size = 64;
138 pw_buf = talloc_size (ctx, pw_buf_size);
140 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
141 pw_buf_size, &ignored)) == ERANGE) {
142 pw_buf_size = pw_buf_size * 2;
143 pw_buf = talloc_zero_size(ctx, pw_buf_size);
147 char *comma = strchr (passwd.pw_gecos, ',');
149 name = talloc_strndup (ctx, passwd.pw_gecos,
150 comma - passwd.pw_gecos);
152 name = talloc_strdup (ctx, passwd.pw_gecos);
154 name = talloc_strdup (ctx, "");
157 talloc_free (pw_buf);
163 get_username_from_passwd_file (void *ctx)
167 struct passwd passwd, *ignored;
171 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
172 if (pw_buf_size == -1) pw_buf_size = 64;
173 pw_buf = talloc_zero_size (ctx, pw_buf_size);
175 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
176 pw_buf_size, &ignored)) == ERANGE) {
177 pw_buf_size = pw_buf_size * 2;
178 pw_buf = talloc_zero_size(ctx, pw_buf_size);
182 name = talloc_strdup (ctx, passwd.pw_name);
184 name = talloc_strdup (ctx, "");
186 talloc_free (pw_buf);
191 /* Open the named notmuch configuration file. If the filename is NULL,
192 * the value of the environment variable $NOTMUCH_CONFIG will be used.
193 * If $NOTMUCH_CONFIG is unset, the default configuration file
194 * ($HOME/.notmuch-config) will be used.
196 * If any error occurs, (out of memory, or a permission-denied error,
197 * etc.), this function will print a message to stderr and return
200 * FILE NOT FOUND: When the specified configuration file (whether from
201 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
202 * exist, the behavior of this function depends on the 'is_new_ret'
205 * If is_new_ret is NULL, then a "file not found" message will be
206 * printed to stderr and NULL will be returned.
208 * If is_new_ret is non-NULL then a default configuration will be
209 * returned and *is_new_ret will be set to 1 on return so that
210 * the caller can recognize this case.
212 * These default configuration settings are determined as
215 * database_path: $HOME/mail
217 * user_name: From /etc/passwd
219 * user_primary_mail: $EMAIL variable if set, otherwise
220 * constructed from the username and
221 * hostname of the current machine.
223 * user_other_email: Not set.
225 * The default configuration also contains comments to guide the
226 * user in editing the file directly.
229 notmuch_config_open (void *ctx,
230 const char *filename,
231 notmuch_bool_t *is_new_ret)
233 GError *error = NULL;
236 char *notmuch_config_env = NULL;
237 int file_had_database_group;
238 int file_had_new_group;
239 int file_had_user_group;
240 int file_had_maildir_group;
241 int file_had_search_group;
246 notmuch_config_t *config = talloc (ctx, notmuch_config_t);
247 if (config == NULL) {
248 fprintf (stderr, "Out of memory.\n");
252 talloc_set_destructor (config, notmuch_config_destructor);
255 config->filename = talloc_strdup (config, filename);
256 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
257 config->filename = talloc_strdup (config, notmuch_config_env);
259 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
263 config->key_file = g_key_file_new ();
265 config->database_path = NULL;
266 config->user_name = NULL;
267 config->user_primary_email = NULL;
268 config->user_other_email = NULL;
269 config->user_other_email_length = 0;
270 config->new_tags = NULL;
271 config->new_tags_length = 0;
272 config->new_ignore = NULL;
273 config->new_ignore_length = 0;
274 config->maildir_synchronize_flags = TRUE;
275 config->search_exclude_tags = NULL;
276 config->search_exclude_tags_length = 0;
278 if (! g_key_file_load_from_file (config->key_file,
280 G_KEY_FILE_KEEP_COMMENTS,
283 /* If the caller passed a non-NULL value for is_new_ret, then
284 * the caller is prepared for a default configuration file in
285 * the case of FILE NOT FOUND. Otherwise, any read failure is
289 error->domain == G_FILE_ERROR &&
290 error->code == G_FILE_ERROR_NOENT)
292 g_error_free (error);
297 fprintf (stderr, "Error reading configuration file %s: %s\n",
298 config->filename, error->message);
299 talloc_free (config);
300 g_error_free (error);
305 /* Whenever we know of configuration sections that don't appear in
306 * the configuration file, we add some comments to help the user
307 * understand what can be done.
309 * It would be convenient to just add those comments now, but
310 * apparently g_key_file will clear any comments when keys are
311 * added later that create the groups. So we have to check for the
312 * groups now, but add the comments only after setting all of our
315 file_had_database_group = g_key_file_has_group (config->key_file,
317 file_had_new_group = g_key_file_has_group (config->key_file, "new");
318 file_had_user_group = g_key_file_has_group (config->key_file, "user");
319 file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
320 file_had_search_group = g_key_file_has_group (config->key_file, "search");
323 if (notmuch_config_get_database_path (config) == NULL) {
324 char *path = talloc_asprintf (config, "%s/mail",
326 notmuch_config_set_database_path (config, path);
330 if (notmuch_config_get_user_name (config) == NULL) {
331 char *name = get_name_from_passwd_file (config);
332 notmuch_config_set_user_name (config, name);
336 if (notmuch_config_get_user_primary_email (config) == NULL) {
337 char *email = getenv ("EMAIL");
339 notmuch_config_set_user_primary_email (config, email);
342 struct hostent *hostent;
343 const char *domainname;
345 char *username = get_username_from_passwd_file (config);
347 gethostname (hostname, 256);
348 hostname[255] = '\0';
350 hostent = gethostbyname (hostname);
351 if (hostent && (domainname = strchr (hostent->h_name, '.')))
354 domainname = "(none)";
356 email = talloc_asprintf (config, "%s@%s.%s",
357 username, hostname, domainname);
359 notmuch_config_set_user_primary_email (config, email);
361 talloc_free (username);
366 if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
367 const char *tags[] = { "unread", "inbox" };
368 notmuch_config_set_new_tags (config, tags, 2);
371 if (notmuch_config_get_new_ignore (config, &tmp) == NULL) {
372 notmuch_config_set_new_ignore (config, NULL, 0);
375 if (notmuch_config_get_search_exclude_tags (config, &tmp) == NULL) {
377 const char *tags[] = { "deleted", "spam" };
378 notmuch_config_set_search_exclude_tags (config, tags, 2);
380 notmuch_config_set_search_exclude_tags (config, NULL, 0);
385 config->maildir_synchronize_flags =
386 g_key_file_get_boolean (config->key_file,
387 "maildir", "synchronize_flags", &error);
389 notmuch_config_set_maildir_synchronize_flags (config, TRUE);
390 g_error_free (error);
393 /* Whenever we know of configuration sections that don't appear in
394 * the configuration file, we add some comments to help the user
395 * understand what can be done. */
398 g_key_file_set_comment (config->key_file, NULL, NULL,
399 toplevel_config_comment, NULL);
402 if (! file_had_database_group)
404 g_key_file_set_comment (config->key_file, "database", NULL,
405 database_config_comment, NULL);
408 if (! file_had_new_group)
410 g_key_file_set_comment (config->key_file, "new", NULL,
411 new_config_comment, NULL);
414 if (! file_had_user_group)
416 g_key_file_set_comment (config->key_file, "user", NULL,
417 user_config_comment, NULL);
420 if (! file_had_maildir_group)
422 g_key_file_set_comment (config->key_file, "maildir", NULL,
423 maildir_config_comment, NULL);
426 if (! file_had_search_group) {
427 g_key_file_set_comment (config->key_file, "search", NULL,
428 search_config_comment, NULL);
432 *is_new_ret = is_new;
437 /* Close the given notmuch_config_t object, freeing all resources.
439 * Note: Any changes made to the configuration are *not* saved by this
440 * function. To save changes, call notmuch_config_save before
441 * notmuch_config_close.
444 notmuch_config_close (notmuch_config_t *config)
446 talloc_free (config);
449 /* Save any changes made to the notmuch configuration.
451 * Any comments originally in the file will be preserved.
453 * Returns 0 if successful, and 1 in case of any error, (after
454 * printing a description of the error to stderr).
457 notmuch_config_save (notmuch_config_t *config)
461 GError *error = NULL;
463 data = g_key_file_to_data (config->key_file, &length, NULL);
465 fprintf (stderr, "Out of memory.\n");
469 if (! g_file_set_contents (config->filename, data, length, &error)) {
470 fprintf (stderr, "Error saving configuration to %s: %s\n",
471 config->filename, error->message);
472 g_error_free (error);
482 _config_get_list (notmuch_config_t *config,
483 const char *section, const char *key,
484 const char ***outlist, size_t *list_length, size_t *ret_length)
488 if (*outlist == NULL) {
490 char **inlist = g_key_file_get_string_list (config->key_file,
491 section, key, list_length, NULL);
495 *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
497 for (i = 0; i < *list_length; i++)
498 (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
500 (*outlist)[i] = NULL;
507 *ret_length = *list_length;
513 _config_set_list (notmuch_config_t *config,
514 const char *group, const char *name,
516 size_t length, const char ***config_var )
518 g_key_file_set_string_list (config->key_file, group, name, list, length);
519 talloc_free (*config_var);
524 notmuch_config_get_database_path (notmuch_config_t *config)
528 if (config->database_path == NULL) {
529 path = g_key_file_get_string (config->key_file,
530 "database", "path", NULL);
532 config->database_path = talloc_strdup (config, path);
537 return config->database_path;
541 notmuch_config_set_database_path (notmuch_config_t *config,
542 const char *database_path)
544 g_key_file_set_string (config->key_file,
545 "database", "path", database_path);
547 talloc_free (config->database_path);
548 config->database_path = NULL;
552 notmuch_config_get_user_name (notmuch_config_t *config)
556 if (config->user_name == NULL) {
557 name = g_key_file_get_string (config->key_file,
558 "user", "name", NULL);
560 config->user_name = talloc_strdup (config, name);
565 return config->user_name;
569 notmuch_config_set_user_name (notmuch_config_t *config,
570 const char *user_name)
572 g_key_file_set_string (config->key_file,
573 "user", "name", user_name);
575 talloc_free (config->user_name);
576 config->user_name = NULL;
580 notmuch_config_get_user_primary_email (notmuch_config_t *config)
584 if (config->user_primary_email == NULL) {
585 email = g_key_file_get_string (config->key_file,
586 "user", "primary_email", NULL);
588 config->user_primary_email = talloc_strdup (config, email);
593 return config->user_primary_email;
597 notmuch_config_set_user_primary_email (notmuch_config_t *config,
598 const char *primary_email)
600 g_key_file_set_string (config->key_file,
601 "user", "primary_email", primary_email);
603 talloc_free (config->user_primary_email);
604 config->user_primary_email = NULL;
608 notmuch_config_get_user_other_email (notmuch_config_t *config, size_t *length)
610 return _config_get_list (config, "user", "other_email",
611 &(config->user_other_email),
612 &(config->user_other_email_length), length);
616 notmuch_config_get_new_tags (notmuch_config_t *config, size_t *length)
618 return _config_get_list (config, "new", "tags",
620 &(config->new_tags_length), length);
624 notmuch_config_get_new_ignore (notmuch_config_t *config, size_t *length)
626 return _config_get_list (config, "new", "ignore",
627 &(config->new_ignore),
628 &(config->new_ignore_length), length);
632 notmuch_config_set_user_other_email (notmuch_config_t *config,
636 _config_set_list (config, "user", "other_email", list, length,
637 &(config->user_other_email));
641 notmuch_config_set_new_tags (notmuch_config_t *config,
645 _config_set_list (config, "new", "tags", list, length,
646 &(config->new_tags));
650 notmuch_config_set_new_ignore (notmuch_config_t *config,
654 _config_set_list (config, "new", "ignore", list, length,
655 &(config->new_ignore));
659 notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length)
661 return _config_get_list (config, "search", "exclude_tags",
662 &(config->search_exclude_tags),
663 &(config->search_exclude_tags_length), length);
667 notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
671 _config_set_list (config, "search", "exclude_tags", list, length,
672 &(config->search_exclude_tags));
675 /* Given a configuration item of the form <group>.<key> return the
676 * component group and key. If any error occurs, print a message on
677 * stderr and return 1. Otherwise, return 0.
679 * Note: This function modifies the original 'item' string.
682 _item_split (char *item, char **group, char **key)
688 period = index (item, '.');
689 if (period == NULL || *(period+1) == '\0') {
691 "Invalid configuration name: %s\n"
692 "(Should be of the form <section>.<item>)\n", item);
703 notmuch_config_command_get (void *ctx, char *item)
705 notmuch_config_t *config;
707 config = notmuch_config_open (ctx, NULL, NULL);
711 if (strcmp(item, "database.path") == 0) {
712 printf ("%s\n", notmuch_config_get_database_path (config));
713 } else if (strcmp(item, "user.name") == 0) {
714 printf ("%s\n", notmuch_config_get_user_name (config));
715 } else if (strcmp(item, "user.primary_email") == 0) {
716 printf ("%s\n", notmuch_config_get_user_primary_email (config));
717 } else if (strcmp(item, "user.other_email") == 0) {
718 const char **other_email;
721 other_email = notmuch_config_get_user_other_email (config, &length);
722 for (i = 0; i < length; i++)
723 printf ("%s\n", other_email[i]);
724 } else if (strcmp(item, "new.tags") == 0) {
728 tags = notmuch_config_get_new_tags (config, &length);
729 for (i = 0; i < length; i++)
730 printf ("%s\n", tags[i]);
736 if (_item_split (item, &group, &key))
739 value = g_key_file_get_string_list (config->key_file,
743 fprintf (stderr, "Unknown configuration item: %s.%s\n",
748 for (i = 0; i < length; i++)
749 printf ("%s\n", value[i]);
754 notmuch_config_close (config);
760 notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
762 notmuch_config_t *config;
766 if (_item_split (item, &group, &key))
769 config = notmuch_config_open (ctx, NULL, NULL);
773 /* With only the name of an item, we clear it from the
774 * configuration file.
776 * With a single value, we set it as a string.
778 * With multiple values, we set them as a string list.
782 g_key_file_remove_key (config->key_file, group, key, NULL);
785 g_key_file_set_string (config->key_file, group, key, argv[0]);
788 g_key_file_set_string_list (config->key_file, group, key,
789 (const gchar **) argv, argc);
793 ret = notmuch_config_save (config);
794 notmuch_config_close (config);
800 notmuch_config_command (void *ctx, int argc, char *argv[])
802 argc--; argv++; /* skip subcommand argument */
805 fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
809 if (strcmp (argv[0], "get") == 0)
810 return notmuch_config_command_get (ctx, argv[1]);
811 else if (strcmp (argv[0], "set") == 0)
812 return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
814 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
820 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
822 return config->maildir_synchronize_flags;
826 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
827 notmuch_bool_t synchronize_flags)
829 g_key_file_set_boolean (config->key_file,
830 "maildir", "synchronize_flags", synchronize_flags);
831 config->maildir_synchronize_flags = synchronize_flags;