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"
92 "\tauto_exclude_tags A ;-separated list of tags that will be\n"
93 "\t excluded from search results by default. Using an excluded tag\n"
94 "\t in a query will override that exclusion.\n";
96 struct _notmuch_config {
102 char *user_primary_email;
103 const char **user_other_email;
104 size_t user_other_email_length;
105 const char **new_tags;
106 size_t new_tags_length;
107 notmuch_bool_t maildir_synchronize_flags;
108 const char **auto_exclude_tags;
109 size_t auto_exclude_tags_length;
113 notmuch_config_destructor (notmuch_config_t *config)
115 if (config->key_file)
116 g_key_file_free (config->key_file);
122 get_name_from_passwd_file (void *ctx)
126 struct passwd passwd, *ignored;
130 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
131 if (pw_buf_size == -1) pw_buf_size = 64;
132 pw_buf = talloc_size (ctx, pw_buf_size);
134 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
135 pw_buf_size, &ignored)) == ERANGE) {
136 pw_buf_size = pw_buf_size * 2;
137 pw_buf = talloc_zero_size(ctx, pw_buf_size);
141 char *comma = strchr (passwd.pw_gecos, ',');
143 name = talloc_strndup (ctx, passwd.pw_gecos,
144 comma - passwd.pw_gecos);
146 name = talloc_strdup (ctx, passwd.pw_gecos);
148 name = talloc_strdup (ctx, "");
151 talloc_free (pw_buf);
157 get_username_from_passwd_file (void *ctx)
161 struct passwd passwd, *ignored;
165 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
166 if (pw_buf_size == -1) pw_buf_size = 64;
167 pw_buf = talloc_zero_size (ctx, pw_buf_size);
169 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
170 pw_buf_size, &ignored)) == ERANGE) {
171 pw_buf_size = pw_buf_size * 2;
172 pw_buf = talloc_zero_size(ctx, pw_buf_size);
176 name = talloc_strdup (ctx, passwd.pw_name);
178 name = talloc_strdup (ctx, "");
180 talloc_free (pw_buf);
185 /* Open the named notmuch configuration file. If the filename is NULL,
186 * the value of the environment variable $NOTMUCH_CONFIG will be used.
187 * If $NOTMUCH_CONFIG is unset, the default configuration file
188 * ($HOME/.notmuch-config) will be used.
190 * If any error occurs, (out of memory, or a permission-denied error,
191 * etc.), this function will print a message to stderr and return
194 * FILE NOT FOUND: When the specified configuration file (whether from
195 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
196 * exist, the behavior of this function depends on the 'is_new_ret'
199 * If is_new_ret is NULL, then a "file not found" message will be
200 * printed to stderr and NULL will be returned.
202 * If is_new_ret is non-NULL then a default configuration will be
203 * returned and *is_new_ret will be set to 1 on return so that
204 * the caller can recognize this case.
206 * These default configuration settings are determined as
209 * database_path: $HOME/mail
211 * user_name: From /etc/passwd
213 * user_primary_mail: $EMAIL variable if set, otherwise
214 * constructed from the username and
215 * hostname of the current machine.
217 * user_other_email: Not set.
219 * The default configuration also contains comments to guide the
220 * user in editing the file directly.
223 notmuch_config_open (void *ctx,
224 const char *filename,
225 notmuch_bool_t *is_new_ret)
227 GError *error = NULL;
230 char *notmuch_config_env = NULL;
231 int file_had_database_group;
232 int file_had_new_group;
233 int file_had_user_group;
234 int file_had_maildir_group;
235 int file_had_search_group;
240 notmuch_config_t *config = talloc (ctx, notmuch_config_t);
241 if (config == NULL) {
242 fprintf (stderr, "Out of memory.\n");
246 talloc_set_destructor (config, notmuch_config_destructor);
249 config->filename = talloc_strdup (config, filename);
250 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
251 config->filename = talloc_strdup (config, notmuch_config_env);
253 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
257 config->key_file = g_key_file_new ();
259 config->database_path = NULL;
260 config->user_name = NULL;
261 config->user_primary_email = NULL;
262 config->user_other_email = NULL;
263 config->user_other_email_length = 0;
264 config->new_tags = NULL;
265 config->new_tags_length = 0;
266 config->maildir_synchronize_flags = TRUE;
267 config->auto_exclude_tags = NULL;
268 config->auto_exclude_tags_length = 0;
270 if (! g_key_file_load_from_file (config->key_file,
272 G_KEY_FILE_KEEP_COMMENTS,
275 /* If the caller passed a non-NULL value for is_new_ret, then
276 * the caller is prepared for a default configuration file in
277 * the case of FILE NOT FOUND. Otherwise, any read failure is
281 error->domain == G_FILE_ERROR &&
282 error->code == G_FILE_ERROR_NOENT)
284 g_error_free (error);
289 fprintf (stderr, "Error reading configuration file %s: %s\n",
290 config->filename, error->message);
291 talloc_free (config);
292 g_error_free (error);
297 /* Whenever we know of configuration sections that don't appear in
298 * the configuration file, we add some comments to help the user
299 * understand what can be done.
301 * It would be convenient to just add those comments now, but
302 * apparently g_key_file will clear any comments when keys are
303 * added later that create the groups. So we have to check for the
304 * groups now, but add the comments only after setting all of our
307 file_had_database_group = g_key_file_has_group (config->key_file,
309 file_had_new_group = g_key_file_has_group (config->key_file, "new");
310 file_had_user_group = g_key_file_has_group (config->key_file, "user");
311 file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
312 file_had_search_group = g_key_file_has_group (config->key_file, "search");
315 if (notmuch_config_get_database_path (config) == NULL) {
316 char *path = talloc_asprintf (config, "%s/mail",
318 notmuch_config_set_database_path (config, path);
322 if (notmuch_config_get_user_name (config) == NULL) {
323 char *name = get_name_from_passwd_file (config);
324 notmuch_config_set_user_name (config, name);
328 if (notmuch_config_get_user_primary_email (config) == NULL) {
329 char *email = getenv ("EMAIL");
331 notmuch_config_set_user_primary_email (config, email);
334 struct hostent *hostent;
335 const char *domainname;
337 char *username = get_username_from_passwd_file (config);
339 gethostname (hostname, 256);
340 hostname[255] = '\0';
342 hostent = gethostbyname (hostname);
343 if (hostent && (domainname = strchr (hostent->h_name, '.')))
346 domainname = "(none)";
348 email = talloc_asprintf (config, "%s@%s.%s",
349 username, hostname, domainname);
351 notmuch_config_set_user_primary_email (config, email);
353 talloc_free (username);
358 if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
359 const char *tags[] = { "unread", "inbox" };
360 notmuch_config_set_new_tags (config, tags, 2);
363 if (notmuch_config_get_auto_exclude_tags (config, &tmp) == NULL) {
364 const char *tags[] = { "deleted", "spam" };
365 notmuch_config_set_auto_exclude_tags (config, tags, 2);
369 config->maildir_synchronize_flags =
370 g_key_file_get_boolean (config->key_file,
371 "maildir", "synchronize_flags", &error);
373 notmuch_config_set_maildir_synchronize_flags (config, TRUE);
374 g_error_free (error);
377 /* Whenever we know of configuration sections that don't appear in
378 * the configuration file, we add some comments to help the user
379 * understand what can be done. */
382 g_key_file_set_comment (config->key_file, NULL, NULL,
383 toplevel_config_comment, NULL);
386 if (! file_had_database_group)
388 g_key_file_set_comment (config->key_file, "database", NULL,
389 database_config_comment, NULL);
392 if (! file_had_new_group)
394 g_key_file_set_comment (config->key_file, "new", NULL,
395 new_config_comment, NULL);
398 if (! file_had_user_group)
400 g_key_file_set_comment (config->key_file, "user", NULL,
401 user_config_comment, NULL);
404 if (! file_had_maildir_group)
406 g_key_file_set_comment (config->key_file, "maildir", NULL,
407 maildir_config_comment, NULL);
410 if (! file_had_search_group) {
411 g_key_file_set_comment (config->key_file, "search", NULL,
412 search_config_comment, NULL);
416 *is_new_ret = is_new;
421 /* Close the given notmuch_config_t object, freeing all resources.
423 * Note: Any changes made to the configuration are *not* saved by this
424 * function. To save changes, call notmuch_config_save before
425 * notmuch_config_close.
428 notmuch_config_close (notmuch_config_t *config)
430 talloc_free (config);
433 /* Save any changes made to the notmuch configuration.
435 * Any comments originally in the file will be preserved.
437 * Returns 0 if successful, and 1 in case of any error, (after
438 * printing a description of the error to stderr).
441 notmuch_config_save (notmuch_config_t *config)
445 GError *error = NULL;
447 data = g_key_file_to_data (config->key_file, &length, NULL);
449 fprintf (stderr, "Out of memory.\n");
453 if (! g_file_set_contents (config->filename, data, length, &error)) {
454 fprintf (stderr, "Error saving configuration to %s: %s\n",
455 config->filename, error->message);
456 g_error_free (error);
466 notmuch_config_get_database_path (notmuch_config_t *config)
470 if (config->database_path == NULL) {
471 path = g_key_file_get_string (config->key_file,
472 "database", "path", NULL);
474 config->database_path = talloc_strdup (config, path);
479 return config->database_path;
483 notmuch_config_set_database_path (notmuch_config_t *config,
484 const char *database_path)
486 g_key_file_set_string (config->key_file,
487 "database", "path", database_path);
489 talloc_free (config->database_path);
490 config->database_path = NULL;
494 notmuch_config_get_user_name (notmuch_config_t *config)
498 if (config->user_name == NULL) {
499 name = g_key_file_get_string (config->key_file,
500 "user", "name", NULL);
502 config->user_name = talloc_strdup (config, name);
507 return config->user_name;
511 notmuch_config_set_user_name (notmuch_config_t *config,
512 const char *user_name)
514 g_key_file_set_string (config->key_file,
515 "user", "name", user_name);
517 talloc_free (config->user_name);
518 config->user_name = NULL;
522 notmuch_config_get_user_primary_email (notmuch_config_t *config)
526 if (config->user_primary_email == NULL) {
527 email = g_key_file_get_string (config->key_file,
528 "user", "primary_email", NULL);
530 config->user_primary_email = talloc_strdup (config, email);
535 return config->user_primary_email;
539 notmuch_config_set_user_primary_email (notmuch_config_t *config,
540 const char *primary_email)
542 g_key_file_set_string (config->key_file,
543 "user", "primary_email", primary_email);
545 talloc_free (config->user_primary_email);
546 config->user_primary_email = NULL;
550 _config_get_list (notmuch_config_t *config,
551 const char *section, const char *key,
552 const char ***outlist, size_t *list_length, size_t *ret_length)
556 if (*outlist == NULL) {
558 char **inlist = g_key_file_get_string_list (config->key_file,
559 section, key, list_length, NULL);
563 *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
565 for (i = 0; i < *list_length; i++)
566 (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
568 (*outlist)[i] = NULL;
575 *ret_length = *list_length;
581 notmuch_config_get_user_other_email (notmuch_config_t *config, size_t *length)
583 return _config_get_list (config, "user", "other_email",
584 &(config->user_other_email),
585 &(config->user_other_email_length), length);
589 notmuch_config_get_new_tags (notmuch_config_t *config, size_t *length)
591 return _config_get_list (config, "new", "tags",
593 &(config->new_tags_length), length);
597 _config_set_list (notmuch_config_t *config,
598 const char *group, const char *name,
600 size_t length, const char ***config_var )
602 g_key_file_set_string_list (config->key_file, group, name, list, length);
603 talloc_free (*config_var);
608 notmuch_config_set_user_other_email (notmuch_config_t *config,
612 _config_set_list (config, "user", "other_email", list, length,
613 &(config->user_other_email));
617 notmuch_config_set_new_tags (notmuch_config_t *config,
621 _config_set_list (config, "new", "tags", list, length,
622 &(config->new_tags));
626 notmuch_config_get_auto_exclude_tags (notmuch_config_t *config, size_t *length)
628 return _config_get_list (config, "search", "auto_exclude_tags",
629 &(config->auto_exclude_tags),
630 &(config->auto_exclude_tags_length), length);
634 notmuch_config_set_auto_exclude_tags (notmuch_config_t *config,
638 _config_set_list (config, "search", "auto_exclude_tags", list, length,
639 &(config->auto_exclude_tags));
642 /* Given a configuration item of the form <group>.<key> return the
643 * component group and key. If any error occurs, print a message on
644 * stderr and return 1. Otherwise, return 0.
646 * Note: This function modifies the original 'item' string.
649 _item_split (char *item, char **group, char **key)
655 period = index (item, '.');
656 if (period == NULL || *(period+1) == '\0') {
658 "Invalid configuration name: %s\n"
659 "(Should be of the form <section>.<item>)\n", item);
670 notmuch_config_command_get (void *ctx, char *item)
672 notmuch_config_t *config;
674 config = notmuch_config_open (ctx, NULL, NULL);
678 if (strcmp(item, "database.path") == 0) {
679 printf ("%s\n", notmuch_config_get_database_path (config));
680 } else if (strcmp(item, "user.name") == 0) {
681 printf ("%s\n", notmuch_config_get_user_name (config));
682 } else if (strcmp(item, "user.primary_email") == 0) {
683 printf ("%s\n", notmuch_config_get_user_primary_email (config));
684 } else if (strcmp(item, "user.other_email") == 0) {
685 const char **other_email;
688 other_email = notmuch_config_get_user_other_email (config, &length);
689 for (i = 0; i < length; i++)
690 printf ("%s\n", other_email[i]);
691 } else if (strcmp(item, "new.tags") == 0) {
695 tags = notmuch_config_get_new_tags (config, &length);
696 for (i = 0; i < length; i++)
697 printf ("%s\n", tags[i]);
703 if (_item_split (item, &group, &key))
706 value = g_key_file_get_string_list (config->key_file,
710 fprintf (stderr, "Unknown configuration item: %s.%s\n",
715 for (i = 0; i < length; i++)
716 printf ("%s\n", value[i]);
721 notmuch_config_close (config);
727 notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
729 notmuch_config_t *config;
733 if (_item_split (item, &group, &key))
736 config = notmuch_config_open (ctx, NULL, NULL);
740 /* With only the name of an item, we clear it from the
741 * configuration file.
743 * With a single value, we set it as a string.
745 * With multiple values, we set them as a string list.
749 g_key_file_remove_key (config->key_file, group, key, NULL);
752 g_key_file_set_string (config->key_file, group, key, argv[0]);
755 g_key_file_set_string_list (config->key_file, group, key,
756 (const gchar **) argv, argc);
760 ret = notmuch_config_save (config);
761 notmuch_config_close (config);
767 notmuch_config_command (void *ctx, int argc, char *argv[])
769 argc--; argv++; /* skip subcommand argument */
772 fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
776 if (strcmp (argv[0], "get") == 0)
777 return notmuch_config_command_get (ctx, argv[1]);
778 else if (strcmp (argv[0], "set") == 0)
779 return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
781 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
787 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
789 return config->maildir_synchronize_flags;
793 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
794 notmuch_bool_t synchronize_flags)
796 g_key_file_set_boolean (config->key_file,
797 "maildir", "synchronize_flags", synchronize_flags);
798 config->maildir_synchronize_flags = synchronize_flags;