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 struct _notmuch_config {
93 char *user_primary_email;
94 const char **user_other_email;
95 size_t user_other_email_length;
96 const char **new_tags;
97 size_t new_tags_length;
98 notmuch_bool_t maildir_synchronize_flags;
102 notmuch_config_destructor (notmuch_config_t *config)
104 if (config->key_file)
105 g_key_file_free (config->key_file);
111 get_name_from_passwd_file (void *ctx)
115 struct passwd passwd, *ignored;
119 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
120 if (pw_buf_size == -1) pw_buf_size = 64;
121 pw_buf = talloc_size (ctx, pw_buf_size);
123 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
124 pw_buf_size, &ignored)) == ERANGE) {
125 pw_buf_size = pw_buf_size * 2;
126 pw_buf = talloc_zero_size(ctx, pw_buf_size);
130 char *comma = strchr (passwd.pw_gecos, ',');
132 name = talloc_strndup (ctx, passwd.pw_gecos,
133 comma - passwd.pw_gecos);
135 name = talloc_strdup (ctx, passwd.pw_gecos);
137 name = talloc_strdup (ctx, "");
140 talloc_free (pw_buf);
146 get_username_from_passwd_file (void *ctx)
150 struct passwd passwd, *ignored;
154 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
155 if (pw_buf_size == -1) pw_buf_size = 64;
156 pw_buf = talloc_zero_size (ctx, pw_buf_size);
158 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
159 pw_buf_size, &ignored)) == ERANGE) {
160 pw_buf_size = pw_buf_size * 2;
161 pw_buf = talloc_zero_size(ctx, pw_buf_size);
165 name = talloc_strdup (ctx, passwd.pw_name);
167 name = talloc_strdup (ctx, "");
169 talloc_free (pw_buf);
174 /* Open the named notmuch configuration file. If the filename is NULL,
175 * the value of the environment variable $NOTMUCH_CONFIG will be used.
176 * If $NOTMUCH_CONFIG is unset, the default configuration file
177 * ($HOME/.notmuch-config) will be used.
179 * If any error occurs, (out of memory, or a permission-denied error,
180 * etc.), this function will print a message to stderr and return
183 * FILE NOT FOUND: When the specified configuration file (whether from
184 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
185 * exist, the behavior of this function depends on the 'is_new_ret'
188 * If is_new_ret is NULL, then a "file not found" message will be
189 * printed to stderr and NULL will be returned.
191 * If is_new_ret is non-NULL then a default configuration will be
192 * returned and *is_new_ret will be set to 1 on return so that
193 * the caller can recognize this case.
195 * These default configuration settings are determined as
198 * database_path: $HOME/mail
200 * user_name: From /etc/passwd
202 * user_primary_mail: $EMAIL variable if set, otherwise
203 * constructed from the username and
204 * hostname of the current machine.
206 * user_other_email: Not set.
208 * The default configuration also contains comments to guide the
209 * user in editing the file directly.
212 notmuch_config_open (void *ctx,
213 const char *filename,
214 notmuch_bool_t *is_new_ret)
216 GError *error = NULL;
219 char *notmuch_config_env = NULL;
220 int file_had_database_group;
221 int file_had_new_group;
222 int file_had_user_group;
223 int file_had_maildir_group;
228 notmuch_config_t *config = talloc (ctx, notmuch_config_t);
229 if (config == NULL) {
230 fprintf (stderr, "Out of memory.\n");
234 talloc_set_destructor (config, notmuch_config_destructor);
237 config->filename = talloc_strdup (config, filename);
238 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
239 config->filename = talloc_strdup (config, notmuch_config_env);
241 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
245 config->key_file = g_key_file_new ();
247 config->database_path = NULL;
248 config->user_name = NULL;
249 config->user_primary_email = NULL;
250 config->user_other_email = NULL;
251 config->user_other_email_length = 0;
252 config->new_tags = NULL;
253 config->new_tags_length = 0;
254 config->maildir_synchronize_flags = TRUE;
256 if (! g_key_file_load_from_file (config->key_file,
258 G_KEY_FILE_KEEP_COMMENTS,
261 /* If the caller passed a non-NULL value for is_new_ret, then
262 * the caller is prepared for a default configuration file in
263 * the case of FILE NOT FOUND. Otherwise, any read failure is
267 error->domain == G_FILE_ERROR &&
268 error->code == G_FILE_ERROR_NOENT)
270 g_error_free (error);
275 fprintf (stderr, "Error reading configuration file %s: %s\n",
276 config->filename, error->message);
277 talloc_free (config);
278 g_error_free (error);
283 /* Whenever we know of configuration sections that don't appear in
284 * the configuration file, we add some comments to help the user
285 * understand what can be done.
287 * It would be convenient to just add those comments now, but
288 * apparently g_key_file will clear any comments when keys are
289 * added later that create the groups. So we have to check for the
290 * groups now, but add the comments only after setting all of our
293 file_had_database_group = g_key_file_has_group (config->key_file,
295 file_had_new_group = g_key_file_has_group (config->key_file, "new");
296 file_had_user_group = g_key_file_has_group (config->key_file, "user");
297 file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
300 if (notmuch_config_get_database_path (config) == NULL) {
301 char *path = talloc_asprintf (config, "%s/mail",
303 notmuch_config_set_database_path (config, path);
307 if (notmuch_config_get_user_name (config) == NULL) {
308 char *name = get_name_from_passwd_file (config);
309 notmuch_config_set_user_name (config, name);
313 if (notmuch_config_get_user_primary_email (config) == NULL) {
314 char *email = getenv ("EMAIL");
316 notmuch_config_set_user_primary_email (config, email);
319 struct hostent *hostent;
320 const char *domainname;
322 char *username = get_username_from_passwd_file (config);
324 gethostname (hostname, 256);
325 hostname[255] = '\0';
327 hostent = gethostbyname (hostname);
328 if (hostent && (domainname = strchr (hostent->h_name, '.')))
331 domainname = "(none)";
333 email = talloc_asprintf (config, "%s@%s.%s",
334 username, hostname, domainname);
336 notmuch_config_set_user_primary_email (config, email);
338 talloc_free (username);
343 if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
344 const char *tags[] = { "unread", "inbox" };
345 notmuch_config_set_new_tags (config, tags, 2);
349 config->maildir_synchronize_flags =
350 g_key_file_get_boolean (config->key_file,
351 "maildir", "synchronize_flags", &error);
353 notmuch_config_set_maildir_synchronize_flags (config, TRUE);
354 g_error_free (error);
357 /* Whenever we know of configuration sections that don't appear in
358 * the configuration file, we add some comments to help the user
359 * understand what can be done. */
362 g_key_file_set_comment (config->key_file, NULL, NULL,
363 toplevel_config_comment, NULL);
366 if (! file_had_database_group)
368 g_key_file_set_comment (config->key_file, "database", NULL,
369 database_config_comment, NULL);
372 if (! file_had_new_group)
374 g_key_file_set_comment (config->key_file, "new", NULL,
375 new_config_comment, NULL);
378 if (! file_had_user_group)
380 g_key_file_set_comment (config->key_file, "user", NULL,
381 user_config_comment, NULL);
384 if (! file_had_maildir_group)
386 g_key_file_set_comment (config->key_file, "maildir", NULL,
387 maildir_config_comment, NULL);
391 *is_new_ret = is_new;
396 /* Close the given notmuch_config_t object, freeing all resources.
398 * Note: Any changes made to the configuration are *not* saved by this
399 * function. To save changes, call notmuch_config_save before
400 * notmuch_config_close.
403 notmuch_config_close (notmuch_config_t *config)
405 talloc_free (config);
408 /* Save any changes made to the notmuch configuration.
410 * Any comments originally in the file will be preserved.
412 * Returns 0 if successful, and 1 in case of any error, (after
413 * printing a description of the error to stderr).
416 notmuch_config_save (notmuch_config_t *config)
420 GError *error = NULL;
422 data = g_key_file_to_data (config->key_file, &length, NULL);
424 fprintf (stderr, "Out of memory.\n");
428 if (! g_file_set_contents (config->filename, data, length, &error)) {
429 fprintf (stderr, "Error saving configuration to %s: %s\n",
430 config->filename, error->message);
431 g_error_free (error);
441 notmuch_config_get_database_path (notmuch_config_t *config)
445 if (config->database_path == NULL) {
446 path = g_key_file_get_string (config->key_file,
447 "database", "path", NULL);
449 config->database_path = talloc_strdup (config, path);
454 return config->database_path;
458 notmuch_config_set_database_path (notmuch_config_t *config,
459 const char *database_path)
461 g_key_file_set_string (config->key_file,
462 "database", "path", database_path);
464 talloc_free (config->database_path);
465 config->database_path = NULL;
469 notmuch_config_get_user_name (notmuch_config_t *config)
473 if (config->user_name == NULL) {
474 name = g_key_file_get_string (config->key_file,
475 "user", "name", NULL);
477 config->user_name = talloc_strdup (config, name);
482 return config->user_name;
486 notmuch_config_set_user_name (notmuch_config_t *config,
487 const char *user_name)
489 g_key_file_set_string (config->key_file,
490 "user", "name", user_name);
492 talloc_free (config->user_name);
493 config->user_name = NULL;
497 notmuch_config_get_user_primary_email (notmuch_config_t *config)
501 if (config->user_primary_email == NULL) {
502 email = g_key_file_get_string (config->key_file,
503 "user", "primary_email", NULL);
505 config->user_primary_email = talloc_strdup (config, email);
510 return config->user_primary_email;
514 notmuch_config_set_user_primary_email (notmuch_config_t *config,
515 const char *primary_email)
517 g_key_file_set_string (config->key_file,
518 "user", "primary_email", primary_email);
520 talloc_free (config->user_primary_email);
521 config->user_primary_email = NULL;
525 _config_get_list (notmuch_config_t *config,
526 const char *section, const char *key,
527 const char ***outlist, size_t *list_length, size_t *ret_length)
531 if (*outlist == NULL) {
533 char **inlist = g_key_file_get_string_list (config->key_file,
534 section, key, list_length, NULL);
538 *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
540 for (i = 0; i < *list_length; i++)
541 (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
543 (*outlist)[i] = NULL;
550 *ret_length = *list_length;
556 notmuch_config_get_user_other_email (notmuch_config_t *config, size_t *length)
558 return _config_get_list (config, "user", "other_email",
559 &(config->user_other_email),
560 &(config->user_other_email_length), length);
564 notmuch_config_get_new_tags (notmuch_config_t *config, size_t *length)
566 return _config_get_list (config, "new", "tags",
568 &(config->new_tags_length), length);
572 _config_set_list (notmuch_config_t *config,
573 const char *group, const char *name,
575 size_t length, const char ***config_var )
577 g_key_file_set_string_list (config->key_file, group, name, list, length);
578 talloc_free (*config_var);
583 notmuch_config_set_user_other_email (notmuch_config_t *config,
587 _config_set_list (config, "user", "other_email", list, length,
588 &(config->user_other_email));
592 notmuch_config_set_new_tags (notmuch_config_t *config,
596 _config_set_list (config, "new", "tags", list, length,
597 &(config->new_tags));
600 /* Given a configuration item of the form <group>.<key> return the
601 * component group and key. If any error occurs, print a message on
602 * stderr and return 1. Otherwise, return 0.
604 * Note: This function modifies the original 'item' string.
607 _item_split (char *item, char **group, char **key)
613 period = index (item, '.');
614 if (period == NULL || *(period+1) == '\0') {
616 "Invalid configuration name: %s\n"
617 "(Should be of the form <section>.<item>)\n", item);
628 notmuch_config_command_get (void *ctx, char *item)
630 notmuch_config_t *config;
632 config = notmuch_config_open (ctx, NULL, NULL);
636 if (strcmp(item, "database.path") == 0) {
637 printf ("%s\n", notmuch_config_get_database_path (config));
638 } else if (strcmp(item, "user.name") == 0) {
639 printf ("%s\n", notmuch_config_get_user_name (config));
640 } else if (strcmp(item, "user.primary_email") == 0) {
641 printf ("%s\n", notmuch_config_get_user_primary_email (config));
642 } else if (strcmp(item, "user.other_email") == 0) {
643 const char **other_email;
646 other_email = notmuch_config_get_user_other_email (config, &length);
647 for (i = 0; i < length; i++)
648 printf ("%s\n", other_email[i]);
649 } else if (strcmp(item, "new.tags") == 0) {
653 tags = notmuch_config_get_new_tags (config, &length);
654 for (i = 0; i < length; i++)
655 printf ("%s\n", tags[i]);
661 if (_item_split (item, &group, &key))
664 value = g_key_file_get_string_list (config->key_file,
668 fprintf (stderr, "Unknown configuration item: %s.%s\n",
673 for (i = 0; i < length; i++)
674 printf ("%s\n", value[i]);
679 notmuch_config_close (config);
685 notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
687 notmuch_config_t *config;
691 if (_item_split (item, &group, &key))
694 config = notmuch_config_open (ctx, NULL, NULL);
698 /* With only the name of an item, we clear it from the
699 * configuration file.
701 * With a single value, we set it as a string.
703 * With multiple values, we set them as a string list.
707 g_key_file_remove_key (config->key_file, group, key, NULL);
710 g_key_file_set_string (config->key_file, group, key, argv[0]);
713 g_key_file_set_string_list (config->key_file, group, key,
714 (const gchar **) argv, argc);
718 ret = notmuch_config_save (config);
719 notmuch_config_close (config);
725 notmuch_config_command (void *ctx, int argc, char *argv[])
727 argc--; argv++; /* skip subcommand argument */
730 fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
734 if (strcmp (argv[0], "get") == 0)
735 return notmuch_config_command_get (ctx, argv[1]);
736 else if (strcmp (argv[0], "set") == 0)
737 return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
739 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
745 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
747 return config->maildir_synchronize_flags;
751 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
752 notmuch_bool_t synchronize_flags)
754 g_key_file_set_boolean (config->key_file,
755 "maildir", "synchronize_flags", synchronize_flags);
756 config->maildir_synchronize_flags = synchronize_flags;