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"
26 static const char toplevel_config_comment[] =
27 " .notmuch-config - Configuration file for the notmuch mail system\n"
29 " For more information about notmuch, see http://notmuchmail.org";
31 static const char database_config_comment[] =
32 " Database configuration\n"
34 " The only value supported here is 'path' which should be the top-level\n"
35 " directory where your mail currently exists and to where mail will be\n"
36 " delivered in the future. Files should be individual email messages.\n"
37 " Notmuch will store its database within a sub-directory of the path\n"
38 " configured here named \".notmuch\".\n";
40 static const char new_config_comment[] =
41 " Configuration for \"notmuch new\"\n"
43 " The following options are supported here:\n"
45 "\ttags A list (separated by ';') of the tags that will be\n"
46 "\t added to all messages incorporated by \"notmuch new\".\n";
48 static const char user_config_comment[] =
49 " User configuration\n"
51 " Here is where you can let notmuch know how you would like to be\n"
52 " addressed. Valid settings are\n"
54 "\tname Your full name.\n"
55 "\tprimary_email Your primary email address.\n"
56 "\tother_email A list (separated by ';') of other email addresses\n"
57 "\t at which you receive email.\n"
59 " Notmuch will use the various email addresses configured here when\n"
60 " formatting replies. It will avoid including your own addresses in the\n"
61 " recipient list of replies, and will set the From address based on the\n"
62 " address to which the original email was addressed.\n";
64 static const char maildir_config_comment[] =
65 " Maildir compatibility configuration\n"
67 " The following option is supported here:\n"
69 "\tsynchronize_flags Valid values are true and false.\n"
71 "\tIf true, then the following maildir flags (in message filenames)\n"
72 "\twill be syncrhonized with the corresponding notmuch tags:\n"
80 "\t\tS unread (added when 'S' tag is not present)\n"
82 "\tThe \"notmuch new\" command will notice flag changes in filenames\n"
83 "\tand update tags, while the \"notmuch tag\" and \"notmuch restore\"\n"
84 "\tcommands will notice tag changes and update flags in filenames\n";
86 struct _notmuch_config {
92 char *user_primary_email;
93 const char **user_other_email;
94 size_t user_other_email_length;
95 const char **new_tags;
96 size_t new_tags_length;
97 notmuch_bool_t maildir_synchronize_flags;
100 #define MAILDIR_SYNC_UNDEF ((notmuch_bool_t)-1)
103 notmuch_config_destructor (notmuch_config_t *config)
105 if (config->key_file)
106 g_key_file_free (config->key_file);
112 get_name_from_passwd_file (void *ctx)
114 long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
115 char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
116 struct passwd passwd, *ignored;
120 if (pw_buf_size == -1) pw_buf_size = 64;
122 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
123 pw_buf_size, &ignored)) == ERANGE) {
124 pw_buf_size = pw_buf_size * 2;
125 pw_buf = talloc_zero_size(ctx, pw_buf_size);
129 char *comma = strchr (passwd.pw_gecos, ',');
131 name = talloc_strndup (ctx, passwd.pw_gecos,
132 comma - passwd.pw_gecos);
134 name = talloc_strdup (ctx, passwd.pw_gecos);
136 name = talloc_strdup (ctx, "");
139 talloc_free (pw_buf);
145 get_username_from_passwd_file (void *ctx)
147 long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
148 char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
149 struct passwd passwd, *ignored;
153 if (pw_buf_size == -1) pw_buf_size = 64;
154 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
155 pw_buf_size, &ignored)) == ERANGE) {
156 pw_buf_size = pw_buf_size * 2;
157 pw_buf = talloc_zero_size(ctx, pw_buf_size);
161 name = talloc_strdup (ctx, passwd.pw_name);
163 name = talloc_strdup (ctx, "");
165 talloc_free (pw_buf);
170 /* Open the named notmuch configuration file. If the filename is NULL,
171 * the value of the environment variable $NOTMUCH_CONFIG will be used.
172 * If $NOTMUCH_CONFIG is unset, the default configuration file
173 * ($HOME/.notmuch-config) will be used.
175 * If any error occurs, (out of memory, or a permission-denied error,
176 * etc.), this function will print a message to stderr and return
179 * FILE NOT FOUND: When the specified configuration file (whether from
180 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
181 * exist, the behavior of this function depends on the 'is_new_ret'
184 * If is_new_ret is NULL, then a "file not found" message will be
185 * printed to stderr and NULL will be returned.
187 * If is_new_ret is non-NULL then a default configuration will be
188 * returned and *is_new_ret will be set to 1 on return so that
189 * the caller can recognize this case.
191 * These default configuration settings are determined as
194 * database_path: $HOME/mail
196 * user_name: From /etc/passwd
198 * user_primary_mail: $EMAIL variable if set, otherwise
199 * constructed from the username and
200 * hostname of the current machine.
202 * user_other_email: Not set.
204 * The default configuration also contains comments to guide the
205 * user in editing the file directly.
208 notmuch_config_open (void *ctx,
209 const char *filename,
210 notmuch_bool_t *is_new_ret)
212 GError *error = NULL;
215 char *notmuch_config_env = NULL;
216 int file_had_database_group;
217 int file_had_new_group;
218 int file_had_user_group;
219 int file_had_maildir_group;
224 notmuch_config_t *config = talloc (ctx, notmuch_config_t);
225 if (config == NULL) {
226 fprintf (stderr, "Out of memory.\n");
230 talloc_set_destructor (config, notmuch_config_destructor);
233 config->filename = talloc_strdup (config, filename);
234 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
235 config->filename = talloc_strdup (config, notmuch_config_env);
237 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
241 config->key_file = g_key_file_new ();
243 config->database_path = NULL;
244 config->user_name = NULL;
245 config->user_primary_email = NULL;
246 config->user_other_email = NULL;
247 config->user_other_email_length = 0;
248 config->new_tags = NULL;
249 config->new_tags_length = 0;
250 config->maildir_synchronize_flags = MAILDIR_SYNC_UNDEF;
252 if (! g_key_file_load_from_file (config->key_file,
254 G_KEY_FILE_KEEP_COMMENTS,
257 /* If the caller passed a non-NULL value for is_new_ret, then
258 * the caller is prepared for a default configuration file in
259 * the case of FILE NOT FOUND. Otherwise, any read failure is
263 error->domain == G_FILE_ERROR &&
264 error->code == G_FILE_ERROR_NOENT)
266 g_error_free (error);
271 fprintf (stderr, "Error reading configuration file %s: %s\n",
272 config->filename, error->message);
273 talloc_free (config);
274 g_error_free (error);
279 /* Whenever we know of configuration sections that don't appear in
280 * the configuration file, we add some comments to help the user
281 * understand what can be done.
283 * It would be convenient to just add those comments now, but
284 * apparently g_key_file will clear any comments when keys are
285 * added later that create the groups. So we have to check for the
286 * groups now, but add the comments only after setting all of our
289 file_had_database_group = g_key_file_has_group (config->key_file,
291 file_had_new_group = g_key_file_has_group (config->key_file, "new");
292 file_had_user_group = g_key_file_has_group (config->key_file, "user");
293 file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
296 if (notmuch_config_get_database_path (config) == NULL) {
297 char *path = talloc_asprintf (config, "%s/mail",
299 notmuch_config_set_database_path (config, path);
303 if (notmuch_config_get_user_name (config) == NULL) {
304 char *name = get_name_from_passwd_file (config);
305 notmuch_config_set_user_name (config, name);
309 if (notmuch_config_get_user_primary_email (config) == NULL) {
310 char *email = getenv ("EMAIL");
312 notmuch_config_set_user_primary_email (config, email);
315 struct hostent *hostent;
316 const char *domainname;
318 char *username = get_username_from_passwd_file (config);
320 gethostname (hostname, 256);
321 hostname[255] = '\0';
323 hostent = gethostbyname (hostname);
324 if (hostent && (domainname = strchr (hostent->h_name, '.')))
327 domainname = "(none)";
329 email = talloc_asprintf (config, "%s@%s.%s",
330 username, hostname, domainname);
332 notmuch_config_set_user_primary_email (config, email);
334 talloc_free (username);
339 if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
340 const char *tags[] = { "unread", "inbox" };
341 notmuch_config_set_new_tags (config, tags, 2);
344 if (notmuch_config_get_maildir_synchronize_flags (config) == MAILDIR_SYNC_UNDEF) {
345 notmuch_config_set_maildir_synchronize_flags (config, FALSE);
348 /* Whenever we know of configuration sections that don't appear in
349 * the configuration file, we add some comments to help the user
350 * understand what can be done. */
353 g_key_file_set_comment (config->key_file, NULL, NULL,
354 toplevel_config_comment, NULL);
357 if (! file_had_database_group)
359 g_key_file_set_comment (config->key_file, "database", NULL,
360 database_config_comment, NULL);
363 if (! file_had_new_group)
365 g_key_file_set_comment (config->key_file, "new", NULL,
366 new_config_comment, NULL);
369 if (! file_had_user_group)
371 g_key_file_set_comment (config->key_file, "user", NULL,
372 user_config_comment, NULL);
375 if (! file_had_maildir_group)
377 g_key_file_set_comment (config->key_file, "maildir", NULL,
378 maildir_config_comment, NULL);
382 *is_new_ret = is_new;
387 /* Close the given notmuch_config_t object, freeing all resources.
389 * Note: Any changes made to the configuration are *not* saved by this
390 * function. To save changes, call notmuch_config_save before
391 * notmuch_config_close.
394 notmuch_config_close (notmuch_config_t *config)
396 talloc_free (config);
399 /* Save any changes made to the notmuch configuration.
401 * Any comments originally in the file will be preserved.
403 * Returns 0 if successful, and 1 in case of any error, (after
404 * printing a description of the error to stderr).
407 notmuch_config_save (notmuch_config_t *config)
411 GError *error = NULL;
413 data = g_key_file_to_data (config->key_file, &length, NULL);
415 fprintf (stderr, "Out of memory.\n");
419 if (! g_file_set_contents (config->filename, data, length, &error)) {
420 fprintf (stderr, "Error saving configuration to %s: %s\n",
421 config->filename, error->message);
422 g_error_free (error);
432 notmuch_config_get_database_path (notmuch_config_t *config)
436 if (config->database_path == NULL) {
437 path = g_key_file_get_string (config->key_file,
438 "database", "path", NULL);
440 config->database_path = talloc_strdup (config, path);
445 return config->database_path;
449 notmuch_config_set_database_path (notmuch_config_t *config,
450 const char *database_path)
452 g_key_file_set_string (config->key_file,
453 "database", "path", database_path);
455 talloc_free (config->database_path);
456 config->database_path = NULL;
460 notmuch_config_get_user_name (notmuch_config_t *config)
464 if (config->user_name == NULL) {
465 name = g_key_file_get_string (config->key_file,
466 "user", "name", NULL);
468 config->user_name = talloc_strdup (config, name);
473 return config->user_name;
477 notmuch_config_set_user_name (notmuch_config_t *config,
478 const char *user_name)
480 g_key_file_set_string (config->key_file,
481 "user", "name", user_name);
483 talloc_free (config->user_name);
484 config->user_name = NULL;
488 notmuch_config_get_user_primary_email (notmuch_config_t *config)
492 if (config->user_primary_email == NULL) {
493 email = g_key_file_get_string (config->key_file,
494 "user", "primary_email", NULL);
496 config->user_primary_email = talloc_strdup (config, email);
501 return config->user_primary_email;
505 notmuch_config_set_user_primary_email (notmuch_config_t *config,
506 const char *primary_email)
508 g_key_file_set_string (config->key_file,
509 "user", "primary_email", primary_email);
511 talloc_free (config->user_primary_email);
512 config->user_primary_email = NULL;
516 notmuch_config_get_user_other_email (notmuch_config_t *config,
520 size_t emails_length;
523 if (config->user_other_email == NULL) {
524 emails = g_key_file_get_string_list (config->key_file,
525 "user", "other_email",
526 &emails_length, NULL);
528 config->user_other_email = talloc_size (config,
530 (emails_length + 1));
531 for (i = 0; i < emails_length; i++)
532 config->user_other_email[i] = talloc_strdup (config->user_other_email,
534 config->user_other_email[i] = NULL;
538 config->user_other_email_length = emails_length;
542 *length = config->user_other_email_length;
543 return config->user_other_email;
547 notmuch_config_set_user_other_email (notmuch_config_t *config,
548 const char *other_email[],
551 g_key_file_set_string_list (config->key_file,
552 "user", "other_email",
553 other_email, length);
555 talloc_free (config->user_other_email);
556 config->user_other_email = NULL;
560 notmuch_config_get_new_tags (notmuch_config_t *config,
567 if (config->new_tags == NULL) {
568 tags = g_key_file_get_string_list (config->key_file,
572 config->new_tags = talloc_size (config,
575 for (i = 0; i < tags_length; i++)
576 config->new_tags[i] = talloc_strdup (config->new_tags,
578 config->new_tags[i] = NULL;
582 config->new_tags_length = tags_length;
586 *length = config->new_tags_length;
587 return config->new_tags;
591 notmuch_config_set_new_tags (notmuch_config_t *config,
592 const char *new_tags[],
595 g_key_file_set_string_list (config->key_file,
599 talloc_free (config->new_tags);
600 config->new_tags = NULL;
603 /* Given a configuration item of the form <group>.<key> return the
604 * component group and key. If any error occurs, print a message on
605 * stderr and return 1. Otherwise, return 0.
607 * Note: This function modifies the original 'item' string.
610 _item_split (char *item, char **group, char **key)
616 period = index (item, '.');
617 if (period == NULL || *(period+1) == '\0') {
619 "Invalid configuration name: %s\n"
620 "(Should be of the form <section>.<item>)\n", item);
631 notmuch_config_command_get (void *ctx, char *item)
633 notmuch_config_t *config;
635 config = notmuch_config_open (ctx, NULL, NULL);
639 if (strcmp(item, "database.path") == 0) {
640 printf ("%s\n", notmuch_config_get_database_path (config));
641 } else if (strcmp(item, "user.name") == 0) {
642 printf ("%s\n", notmuch_config_get_user_name (config));
643 } else if (strcmp(item, "user.primary_email") == 0) {
644 printf ("%s\n", notmuch_config_get_user_primary_email (config));
645 } else if (strcmp(item, "user.other_email") == 0) {
646 const char **other_email;
649 other_email = notmuch_config_get_user_other_email (config, &length);
650 for (i = 0; i < length; i++)
651 printf ("%s\n", other_email[i]);
652 } else if (strcmp(item, "new.tags") == 0) {
656 tags = notmuch_config_get_new_tags (config, &length);
657 for (i = 0; i < length; i++)
658 printf ("%s\n", tags[i]);
664 if (_item_split (item, &group, &key))
667 value = g_key_file_get_string_list (config->key_file,
671 fprintf (stderr, "Unknown configuration item: %s.%s\n",
676 for (i = 0; i < length; i++)
677 printf ("%s\n", value[i]);
682 notmuch_config_close (config);
688 notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
690 notmuch_config_t *config;
694 if (_item_split (item, &group, &key))
697 config = notmuch_config_open (ctx, NULL, NULL);
701 /* With only the name of an item, we clear it from the
702 * configuration file.
704 * With a single value, we set it as a string.
706 * With multiple values, we set them as a string list.
710 g_key_file_remove_key (config->key_file, group, key, NULL);
713 g_key_file_set_string (config->key_file, group, key, argv[0]);
716 g_key_file_set_string_list (config->key_file, group, key,
717 (const gchar **) argv, argc);
721 ret = notmuch_config_save (config);
722 notmuch_config_close (config);
728 notmuch_config_command (void *ctx, int argc, char *argv[])
731 fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
735 if (strcmp (argv[0], "get") == 0)
736 return notmuch_config_command_get (ctx, argv[1]);
737 else if (strcmp (argv[0], "set") == 0)
738 return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
740 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
746 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
749 if (config->maildir_synchronize_flags == MAILDIR_SYNC_UNDEF) {
750 config->maildir_synchronize_flags =
751 g_key_file_get_boolean (config->key_file,
752 "maildir", "synchronize_flags", &err);
754 config->maildir_synchronize_flags = MAILDIR_SYNC_UNDEF;
758 return config->maildir_synchronize_flags;
762 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
763 notmuch_bool_t synchronize_flags)
765 g_key_file_set_boolean (config->key_file,
766 "maildir", "synchronize_flags", synchronize_flags);
767 config->maildir_synchronize_flags = synchronize_flags;