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 static const char user_config_comment[] =
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";
65 static const char maildir_config_comment[] =
66 " Maildir compatibility configuration\n"
68 " The following option is supported here:\n"
70 "\tsynchronize_flags Valid values are true and false.\n"
72 "\tIf true, then the following maildir flags (in message filenames)\n"
73 "\twill be synchronized with the corresponding notmuch tags:\n"
81 "\t\tS unread (added when 'S' flag is not present)\n"
83 "\tThe \"notmuch new\" command will notice flag changes in filenames\n"
84 "\tand update tags, while the \"notmuch tag\" and \"notmuch restore\"\n"
85 "\tcommands will notice tag changes and update flags in filenames\n";
87 static const char search_config_comment[] =
88 " Search configuration\n"
90 " The following option is supported here:\n"
93 "\t\tA ;-separated list of tags that will be excluded from\n"
94 "\t\tsearch results by default. Using an excluded tag in a\n"
95 "\t\tquery will override that exclusion.\n";
97 struct _notmuch_config {
103 char *user_primary_email;
104 const char **user_other_email;
105 size_t user_other_email_length;
106 const char **new_tags;
107 size_t new_tags_length;
108 notmuch_bool_t maildir_synchronize_flags;
109 const char **search_exclude_tags;
110 size_t search_exclude_tags_length;
114 notmuch_config_destructor (notmuch_config_t *config)
116 if (config->key_file)
117 g_key_file_free (config->key_file);
123 get_name_from_passwd_file (void *ctx)
127 struct passwd passwd, *ignored;
131 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
132 if (pw_buf_size == -1) pw_buf_size = 64;
133 pw_buf = talloc_size (ctx, pw_buf_size);
135 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
136 pw_buf_size, &ignored)) == ERANGE) {
137 pw_buf_size = pw_buf_size * 2;
138 pw_buf = talloc_zero_size(ctx, pw_buf_size);
142 char *comma = strchr (passwd.pw_gecos, ',');
144 name = talloc_strndup (ctx, passwd.pw_gecos,
145 comma - passwd.pw_gecos);
147 name = talloc_strdup (ctx, passwd.pw_gecos);
149 name = talloc_strdup (ctx, "");
152 talloc_free (pw_buf);
158 get_username_from_passwd_file (void *ctx)
162 struct passwd passwd, *ignored;
166 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
167 if (pw_buf_size == -1) pw_buf_size = 64;
168 pw_buf = talloc_zero_size (ctx, pw_buf_size);
170 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
171 pw_buf_size, &ignored)) == ERANGE) {
172 pw_buf_size = pw_buf_size * 2;
173 pw_buf = talloc_zero_size(ctx, pw_buf_size);
177 name = talloc_strdup (ctx, passwd.pw_name);
179 name = talloc_strdup (ctx, "");
181 talloc_free (pw_buf);
186 /* Open the named notmuch configuration file. If the filename is NULL,
187 * the value of the environment variable $NOTMUCH_CONFIG will be used.
188 * If $NOTMUCH_CONFIG is unset, the default configuration file
189 * ($HOME/.notmuch-config) will be used.
191 * If any error occurs, (out of memory, or a permission-denied error,
192 * etc.), this function will print a message to stderr and return
195 * FILE NOT FOUND: When the specified configuration file (whether from
196 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
197 * exist, the behavior of this function depends on the 'is_new_ret'
200 * If is_new_ret is NULL, then a "file not found" message will be
201 * printed to stderr and NULL will be returned.
203 * If is_new_ret is non-NULL then a default configuration will be
204 * returned and *is_new_ret will be set to 1 on return so that
205 * the caller can recognize this case.
207 * These default configuration settings are determined as
210 * database_path: $HOME/mail
212 * user_name: From /etc/passwd
214 * user_primary_mail: $EMAIL variable if set, otherwise
215 * constructed from the username and
216 * hostname of the current machine.
218 * user_other_email: Not set.
220 * The default configuration also contains comments to guide the
221 * user in editing the file directly.
224 notmuch_config_open (void *ctx,
225 const char *filename,
226 notmuch_bool_t *is_new_ret)
228 GError *error = NULL;
231 char *notmuch_config_env = NULL;
232 int file_had_database_group;
233 int file_had_new_group;
234 int file_had_user_group;
235 int file_had_maildir_group;
236 int file_had_search_group;
241 notmuch_config_t *config = talloc (ctx, notmuch_config_t);
242 if (config == NULL) {
243 fprintf (stderr, "Out of memory.\n");
247 talloc_set_destructor (config, notmuch_config_destructor);
250 config->filename = talloc_strdup (config, filename);
251 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
252 config->filename = talloc_strdup (config, notmuch_config_env);
254 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
258 config->key_file = g_key_file_new ();
260 config->database_path = NULL;
261 config->user_name = NULL;
262 config->user_primary_email = NULL;
263 config->user_other_email = NULL;
264 config->user_other_email_length = 0;
265 config->new_tags = NULL;
266 config->new_tags_length = 0;
267 config->maildir_synchronize_flags = TRUE;
268 config->search_exclude_tags = NULL;
269 config->search_exclude_tags_length = 0;
271 if (! g_key_file_load_from_file (config->key_file,
273 G_KEY_FILE_KEEP_COMMENTS,
276 /* If the caller passed a non-NULL value for is_new_ret, then
277 * the caller is prepared for a default configuration file in
278 * the case of FILE NOT FOUND. Otherwise, any read failure is
282 error->domain == G_FILE_ERROR &&
283 error->code == G_FILE_ERROR_NOENT)
285 g_error_free (error);
290 fprintf (stderr, "Error reading configuration file %s: %s\n",
291 config->filename, error->message);
292 talloc_free (config);
293 g_error_free (error);
298 /* Whenever we know of configuration sections that don't appear in
299 * the configuration file, we add some comments to help the user
300 * understand what can be done.
302 * It would be convenient to just add those comments now, but
303 * apparently g_key_file will clear any comments when keys are
304 * added later that create the groups. So we have to check for the
305 * groups now, but add the comments only after setting all of our
308 file_had_database_group = g_key_file_has_group (config->key_file,
310 file_had_new_group = g_key_file_has_group (config->key_file, "new");
311 file_had_user_group = g_key_file_has_group (config->key_file, "user");
312 file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
313 file_had_search_group = g_key_file_has_group (config->key_file, "search");
316 if (notmuch_config_get_database_path (config) == NULL) {
317 char *path = talloc_asprintf (config, "%s/mail",
319 notmuch_config_set_database_path (config, path);
323 if (notmuch_config_get_user_name (config) == NULL) {
324 char *name = get_name_from_passwd_file (config);
325 notmuch_config_set_user_name (config, name);
329 if (notmuch_config_get_user_primary_email (config) == NULL) {
330 char *email = getenv ("EMAIL");
332 notmuch_config_set_user_primary_email (config, email);
335 struct hostent *hostent;
336 const char *domainname;
338 char *username = get_username_from_passwd_file (config);
340 gethostname (hostname, 256);
341 hostname[255] = '\0';
343 hostent = gethostbyname (hostname);
344 if (hostent && (domainname = strchr (hostent->h_name, '.')))
347 domainname = "(none)";
349 email = talloc_asprintf (config, "%s@%s.%s",
350 username, hostname, domainname);
352 notmuch_config_set_user_primary_email (config, email);
354 talloc_free (username);
359 if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
360 const char *tags[] = { "unread", "inbox" };
361 notmuch_config_set_new_tags (config, tags, 2);
364 if (notmuch_config_get_search_exclude_tags (config, &tmp) == NULL) {
366 const char *tags[] = { "deleted", "spam" };
367 notmuch_config_set_search_exclude_tags (config, tags, 2);
369 notmuch_config_set_search_exclude_tags (config, NULL, 0);
374 config->maildir_synchronize_flags =
375 g_key_file_get_boolean (config->key_file,
376 "maildir", "synchronize_flags", &error);
378 notmuch_config_set_maildir_synchronize_flags (config, TRUE);
379 g_error_free (error);
382 /* Whenever we know of configuration sections that don't appear in
383 * the configuration file, we add some comments to help the user
384 * understand what can be done. */
387 g_key_file_set_comment (config->key_file, NULL, NULL,
388 toplevel_config_comment, NULL);
391 if (! file_had_database_group)
393 g_key_file_set_comment (config->key_file, "database", NULL,
394 database_config_comment, NULL);
397 if (! file_had_new_group)
399 g_key_file_set_comment (config->key_file, "new", NULL,
400 new_config_comment, NULL);
403 if (! file_had_user_group)
405 g_key_file_set_comment (config->key_file, "user", NULL,
406 user_config_comment, NULL);
409 if (! file_had_maildir_group)
411 g_key_file_set_comment (config->key_file, "maildir", NULL,
412 maildir_config_comment, NULL);
415 if (! file_had_search_group) {
416 g_key_file_set_comment (config->key_file, "search", NULL,
417 search_config_comment, NULL);
421 *is_new_ret = is_new;
426 /* Close the given notmuch_config_t object, freeing all resources.
428 * Note: Any changes made to the configuration are *not* saved by this
429 * function. To save changes, call notmuch_config_save before
430 * notmuch_config_close.
433 notmuch_config_close (notmuch_config_t *config)
435 talloc_free (config);
438 /* Save any changes made to the notmuch configuration.
440 * Any comments originally in the file will be preserved.
442 * Returns 0 if successful, and 1 in case of any error, (after
443 * printing a description of the error to stderr).
446 notmuch_config_save (notmuch_config_t *config)
450 GError *error = NULL;
452 data = g_key_file_to_data (config->key_file, &length, NULL);
454 fprintf (stderr, "Out of memory.\n");
458 if (! g_file_set_contents (config->filename, data, length, &error)) {
459 fprintf (stderr, "Error saving configuration to %s: %s\n",
460 config->filename, error->message);
461 g_error_free (error);
471 notmuch_config_get_database_path (notmuch_config_t *config)
475 if (config->database_path == NULL) {
476 path = g_key_file_get_string (config->key_file,
477 "database", "path", NULL);
479 config->database_path = talloc_strdup (config, path);
484 return config->database_path;
488 notmuch_config_set_database_path (notmuch_config_t *config,
489 const char *database_path)
491 g_key_file_set_string (config->key_file,
492 "database", "path", database_path);
494 talloc_free (config->database_path);
495 config->database_path = NULL;
499 notmuch_config_get_user_name (notmuch_config_t *config)
503 if (config->user_name == NULL) {
504 name = g_key_file_get_string (config->key_file,
505 "user", "name", NULL);
507 config->user_name = talloc_strdup (config, name);
512 return config->user_name;
516 notmuch_config_set_user_name (notmuch_config_t *config,
517 const char *user_name)
519 g_key_file_set_string (config->key_file,
520 "user", "name", user_name);
522 talloc_free (config->user_name);
523 config->user_name = NULL;
527 notmuch_config_get_user_primary_email (notmuch_config_t *config)
531 if (config->user_primary_email == NULL) {
532 email = g_key_file_get_string (config->key_file,
533 "user", "primary_email", NULL);
535 config->user_primary_email = talloc_strdup (config, email);
540 return config->user_primary_email;
544 notmuch_config_set_user_primary_email (notmuch_config_t *config,
545 const char *primary_email)
547 g_key_file_set_string (config->key_file,
548 "user", "primary_email", primary_email);
550 talloc_free (config->user_primary_email);
551 config->user_primary_email = NULL;
555 _config_get_list (notmuch_config_t *config,
556 const char *section, const char *key,
557 const char ***outlist, size_t *list_length, size_t *ret_length)
561 if (*outlist == NULL) {
563 char **inlist = g_key_file_get_string_list (config->key_file,
564 section, key, list_length, NULL);
568 *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
570 for (i = 0; i < *list_length; i++)
571 (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
573 (*outlist)[i] = NULL;
580 *ret_length = *list_length;
586 notmuch_config_get_user_other_email (notmuch_config_t *config, size_t *length)
588 return _config_get_list (config, "user", "other_email",
589 &(config->user_other_email),
590 &(config->user_other_email_length), length);
594 notmuch_config_get_new_tags (notmuch_config_t *config, size_t *length)
596 return _config_get_list (config, "new", "tags",
598 &(config->new_tags_length), length);
602 _config_set_list (notmuch_config_t *config,
603 const char *group, const char *name,
605 size_t length, const char ***config_var )
607 g_key_file_set_string_list (config->key_file, group, name, list, length);
608 talloc_free (*config_var);
613 notmuch_config_set_user_other_email (notmuch_config_t *config,
617 _config_set_list (config, "user", "other_email", list, length,
618 &(config->user_other_email));
622 notmuch_config_set_new_tags (notmuch_config_t *config,
626 _config_set_list (config, "new", "tags", list, length,
627 &(config->new_tags));
631 notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length)
633 return _config_get_list (config, "search", "exclude_tags",
634 &(config->search_exclude_tags),
635 &(config->search_exclude_tags_length), length);
639 notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
643 _config_set_list (config, "search", "exclude_tags", list, length,
644 &(config->search_exclude_tags));
647 /* Given a configuration item of the form <group>.<key> return the
648 * component group and key. If any error occurs, print a message on
649 * stderr and return 1. Otherwise, return 0.
651 * Note: This function modifies the original 'item' string.
654 _item_split (char *item, char **group, char **key)
660 period = index (item, '.');
661 if (period == NULL || *(period+1) == '\0') {
663 "Invalid configuration name: %s\n"
664 "(Should be of the form <section>.<item>)\n", item);
675 notmuch_config_command_get (void *ctx, char *item)
677 notmuch_config_t *config;
679 config = notmuch_config_open (ctx, NULL, NULL);
683 if (strcmp(item, "database.path") == 0) {
684 printf ("%s\n", notmuch_config_get_database_path (config));
685 } else if (strcmp(item, "user.name") == 0) {
686 printf ("%s\n", notmuch_config_get_user_name (config));
687 } else if (strcmp(item, "user.primary_email") == 0) {
688 printf ("%s\n", notmuch_config_get_user_primary_email (config));
689 } else if (strcmp(item, "user.other_email") == 0) {
690 const char **other_email;
693 other_email = notmuch_config_get_user_other_email (config, &length);
694 for (i = 0; i < length; i++)
695 printf ("%s\n", other_email[i]);
696 } else if (strcmp(item, "new.tags") == 0) {
700 tags = notmuch_config_get_new_tags (config, &length);
701 for (i = 0; i < length; i++)
702 printf ("%s\n", tags[i]);
708 if (_item_split (item, &group, &key))
711 value = g_key_file_get_string_list (config->key_file,
715 fprintf (stderr, "Unknown configuration item: %s.%s\n",
720 for (i = 0; i < length; i++)
721 printf ("%s\n", value[i]);
726 notmuch_config_close (config);
732 notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
734 notmuch_config_t *config;
738 if (_item_split (item, &group, &key))
741 config = notmuch_config_open (ctx, NULL, NULL);
745 /* With only the name of an item, we clear it from the
746 * configuration file.
748 * With a single value, we set it as a string.
750 * With multiple values, we set them as a string list.
754 g_key_file_remove_key (config->key_file, group, key, NULL);
757 g_key_file_set_string (config->key_file, group, key, argv[0]);
760 g_key_file_set_string_list (config->key_file, group, key,
761 (const gchar **) argv, argc);
765 ret = notmuch_config_save (config);
766 notmuch_config_close (config);
772 notmuch_config_command (void *ctx, int argc, char *argv[])
774 argc--; argv++; /* skip subcommand argument */
777 fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
781 if (strcmp (argv[0], "get") == 0)
782 return notmuch_config_command_get (ctx, argv[1]);
783 else if (strcmp (argv[0], "set") == 0)
784 return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
786 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
792 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
794 return config->maildir_synchronize_flags;
798 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
799 notmuch_bool_t synchronize_flags)
801 g_key_file_set_boolean (config->key_file,
802 "maildir", "synchronize_flags", synchronize_flags);
803 config->maildir_synchronize_flags = synchronize_flags;