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 synchronized with the corresponding notmuch tags:\n"
80 "\t\tS unread (added when 'S' flag 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;
101 notmuch_config_destructor (notmuch_config_t *config)
103 if (config->key_file)
104 g_key_file_free (config->key_file);
110 get_name_from_passwd_file (void *ctx)
114 struct passwd passwd, *ignored;
118 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
119 if (pw_buf_size == -1) pw_buf_size = 64;
120 pw_buf = talloc_size (ctx, pw_buf_size);
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)
149 struct passwd passwd, *ignored;
153 pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
154 if (pw_buf_size == -1) pw_buf_size = 64;
155 pw_buf = talloc_zero_size (ctx, pw_buf_size);
157 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
158 pw_buf_size, &ignored)) == ERANGE) {
159 pw_buf_size = pw_buf_size * 2;
160 pw_buf = talloc_zero_size(ctx, pw_buf_size);
164 name = talloc_strdup (ctx, passwd.pw_name);
166 name = talloc_strdup (ctx, "");
168 talloc_free (pw_buf);
173 /* Open the named notmuch configuration file. If the filename is NULL,
174 * the value of the environment variable $NOTMUCH_CONFIG will be used.
175 * If $NOTMUCH_CONFIG is unset, the default configuration file
176 * ($HOME/.notmuch-config) will be used.
178 * If any error occurs, (out of memory, or a permission-denied error,
179 * etc.), this function will print a message to stderr and return
182 * FILE NOT FOUND: When the specified configuration file (whether from
183 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
184 * exist, the behavior of this function depends on the 'is_new_ret'
187 * If is_new_ret is NULL, then a "file not found" message will be
188 * printed to stderr and NULL will be returned.
190 * If is_new_ret is non-NULL then a default configuration will be
191 * returned and *is_new_ret will be set to 1 on return so that
192 * the caller can recognize this case.
194 * These default configuration settings are determined as
197 * database_path: $HOME/mail
199 * user_name: From /etc/passwd
201 * user_primary_mail: $EMAIL variable if set, otherwise
202 * constructed from the username and
203 * hostname of the current machine.
205 * user_other_email: Not set.
207 * The default configuration also contains comments to guide the
208 * user in editing the file directly.
211 notmuch_config_open (void *ctx,
212 const char *filename,
213 notmuch_bool_t *is_new_ret)
215 GError *error = NULL;
218 char *notmuch_config_env = NULL;
219 int file_had_database_group;
220 int file_had_new_group;
221 int file_had_user_group;
222 int file_had_maildir_group;
227 notmuch_config_t *config = talloc (ctx, notmuch_config_t);
228 if (config == NULL) {
229 fprintf (stderr, "Out of memory.\n");
233 talloc_set_destructor (config, notmuch_config_destructor);
236 config->filename = talloc_strdup (config, filename);
237 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
238 config->filename = talloc_strdup (config, notmuch_config_env);
240 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
244 config->key_file = g_key_file_new ();
246 config->database_path = NULL;
247 config->user_name = NULL;
248 config->user_primary_email = NULL;
249 config->user_other_email = NULL;
250 config->user_other_email_length = 0;
251 config->new_tags = NULL;
252 config->new_tags_length = 0;
253 config->maildir_synchronize_flags = TRUE;
255 if (! g_key_file_load_from_file (config->key_file,
257 G_KEY_FILE_KEEP_COMMENTS,
260 /* If the caller passed a non-NULL value for is_new_ret, then
261 * the caller is prepared for a default configuration file in
262 * the case of FILE NOT FOUND. Otherwise, any read failure is
266 error->domain == G_FILE_ERROR &&
267 error->code == G_FILE_ERROR_NOENT)
269 g_error_free (error);
274 fprintf (stderr, "Error reading configuration file %s: %s\n",
275 config->filename, error->message);
276 talloc_free (config);
277 g_error_free (error);
282 /* Whenever we know of configuration sections that don't appear in
283 * the configuration file, we add some comments to help the user
284 * understand what can be done.
286 * It would be convenient to just add those comments now, but
287 * apparently g_key_file will clear any comments when keys are
288 * added later that create the groups. So we have to check for the
289 * groups now, but add the comments only after setting all of our
292 file_had_database_group = g_key_file_has_group (config->key_file,
294 file_had_new_group = g_key_file_has_group (config->key_file, "new");
295 file_had_user_group = g_key_file_has_group (config->key_file, "user");
296 file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
299 if (notmuch_config_get_database_path (config) == NULL) {
300 char *path = talloc_asprintf (config, "%s/mail",
302 notmuch_config_set_database_path (config, path);
306 if (notmuch_config_get_user_name (config) == NULL) {
307 char *name = get_name_from_passwd_file (config);
308 notmuch_config_set_user_name (config, name);
312 if (notmuch_config_get_user_primary_email (config) == NULL) {
313 char *email = getenv ("EMAIL");
315 notmuch_config_set_user_primary_email (config, email);
318 struct hostent *hostent;
319 const char *domainname;
321 char *username = get_username_from_passwd_file (config);
323 gethostname (hostname, 256);
324 hostname[255] = '\0';
326 hostent = gethostbyname (hostname);
327 if (hostent && (domainname = strchr (hostent->h_name, '.')))
330 domainname = "(none)";
332 email = talloc_asprintf (config, "%s@%s.%s",
333 username, hostname, domainname);
335 notmuch_config_set_user_primary_email (config, email);
337 talloc_free (username);
342 if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
343 const char *tags[] = { "unread", "inbox" };
344 notmuch_config_set_new_tags (config, tags, 2);
348 config->maildir_synchronize_flags =
349 g_key_file_get_boolean (config->key_file,
350 "maildir", "synchronize_flags", &error);
352 notmuch_config_set_maildir_synchronize_flags (config, TRUE);
353 g_error_free (error);
356 /* Whenever we know of configuration sections that don't appear in
357 * the configuration file, we add some comments to help the user
358 * understand what can be done. */
361 g_key_file_set_comment (config->key_file, NULL, NULL,
362 toplevel_config_comment, NULL);
365 if (! file_had_database_group)
367 g_key_file_set_comment (config->key_file, "database", NULL,
368 database_config_comment, NULL);
371 if (! file_had_new_group)
373 g_key_file_set_comment (config->key_file, "new", NULL,
374 new_config_comment, NULL);
377 if (! file_had_user_group)
379 g_key_file_set_comment (config->key_file, "user", NULL,
380 user_config_comment, NULL);
383 if (! file_had_maildir_group)
385 g_key_file_set_comment (config->key_file, "maildir", NULL,
386 maildir_config_comment, NULL);
390 *is_new_ret = is_new;
395 /* Close the given notmuch_config_t object, freeing all resources.
397 * Note: Any changes made to the configuration are *not* saved by this
398 * function. To save changes, call notmuch_config_save before
399 * notmuch_config_close.
402 notmuch_config_close (notmuch_config_t *config)
404 talloc_free (config);
407 /* Save any changes made to the notmuch configuration.
409 * Any comments originally in the file will be preserved.
411 * Returns 0 if successful, and 1 in case of any error, (after
412 * printing a description of the error to stderr).
415 notmuch_config_save (notmuch_config_t *config)
419 GError *error = NULL;
421 data = g_key_file_to_data (config->key_file, &length, NULL);
423 fprintf (stderr, "Out of memory.\n");
427 if (! g_file_set_contents (config->filename, data, length, &error)) {
428 fprintf (stderr, "Error saving configuration to %s: %s\n",
429 config->filename, error->message);
430 g_error_free (error);
440 notmuch_config_get_database_path (notmuch_config_t *config)
444 if (config->database_path == NULL) {
445 path = g_key_file_get_string (config->key_file,
446 "database", "path", NULL);
448 config->database_path = talloc_strdup (config, path);
453 return config->database_path;
457 notmuch_config_set_database_path (notmuch_config_t *config,
458 const char *database_path)
460 g_key_file_set_string (config->key_file,
461 "database", "path", database_path);
463 talloc_free (config->database_path);
464 config->database_path = NULL;
468 notmuch_config_get_user_name (notmuch_config_t *config)
472 if (config->user_name == NULL) {
473 name = g_key_file_get_string (config->key_file,
474 "user", "name", NULL);
476 config->user_name = talloc_strdup (config, name);
481 return config->user_name;
485 notmuch_config_set_user_name (notmuch_config_t *config,
486 const char *user_name)
488 g_key_file_set_string (config->key_file,
489 "user", "name", user_name);
491 talloc_free (config->user_name);
492 config->user_name = NULL;
496 notmuch_config_get_user_primary_email (notmuch_config_t *config)
500 if (config->user_primary_email == NULL) {
501 email = g_key_file_get_string (config->key_file,
502 "user", "primary_email", NULL);
504 config->user_primary_email = talloc_strdup (config, email);
509 return config->user_primary_email;
513 notmuch_config_set_user_primary_email (notmuch_config_t *config,
514 const char *primary_email)
516 g_key_file_set_string (config->key_file,
517 "user", "primary_email", primary_email);
519 talloc_free (config->user_primary_email);
520 config->user_primary_email = NULL;
524 notmuch_config_get_user_other_email (notmuch_config_t *config,
528 size_t emails_length;
531 if (config->user_other_email == NULL) {
532 emails = g_key_file_get_string_list (config->key_file,
533 "user", "other_email",
534 &emails_length, NULL);
536 config->user_other_email = talloc_size (config,
538 (emails_length + 1));
539 for (i = 0; i < emails_length; i++)
540 config->user_other_email[i] = talloc_strdup (config->user_other_email,
542 config->user_other_email[i] = NULL;
546 config->user_other_email_length = emails_length;
550 *length = config->user_other_email_length;
551 return config->user_other_email;
555 notmuch_config_set_user_other_email (notmuch_config_t *config,
556 const char *other_email[],
559 g_key_file_set_string_list (config->key_file,
560 "user", "other_email",
561 other_email, length);
563 talloc_free (config->user_other_email);
564 config->user_other_email = NULL;
568 notmuch_config_get_new_tags (notmuch_config_t *config,
575 if (config->new_tags == NULL) {
576 tags = g_key_file_get_string_list (config->key_file,
580 config->new_tags = talloc_size (config,
583 for (i = 0; i < tags_length; i++)
584 config->new_tags[i] = talloc_strdup (config->new_tags,
586 config->new_tags[i] = NULL;
590 config->new_tags_length = tags_length;
594 *length = config->new_tags_length;
595 return config->new_tags;
599 notmuch_config_set_new_tags (notmuch_config_t *config,
600 const char *new_tags[],
603 g_key_file_set_string_list (config->key_file,
607 talloc_free (config->new_tags);
608 config->new_tags = NULL;
611 /* Given a configuration item of the form <group>.<key> return the
612 * component group and key. If any error occurs, print a message on
613 * stderr and return 1. Otherwise, return 0.
615 * Note: This function modifies the original 'item' string.
618 _item_split (char *item, char **group, char **key)
624 period = index (item, '.');
625 if (period == NULL || *(period+1) == '\0') {
627 "Invalid configuration name: %s\n"
628 "(Should be of the form <section>.<item>)\n", item);
639 notmuch_config_command_get (void *ctx, char *item)
641 notmuch_config_t *config;
643 config = notmuch_config_open (ctx, NULL, NULL);
647 if (strcmp(item, "database.path") == 0) {
648 printf ("%s\n", notmuch_config_get_database_path (config));
649 } else if (strcmp(item, "user.name") == 0) {
650 printf ("%s\n", notmuch_config_get_user_name (config));
651 } else if (strcmp(item, "user.primary_email") == 0) {
652 printf ("%s\n", notmuch_config_get_user_primary_email (config));
653 } else if (strcmp(item, "user.other_email") == 0) {
654 const char **other_email;
657 other_email = notmuch_config_get_user_other_email (config, &length);
658 for (i = 0; i < length; i++)
659 printf ("%s\n", other_email[i]);
660 } else if (strcmp(item, "new.tags") == 0) {
664 tags = notmuch_config_get_new_tags (config, &length);
665 for (i = 0; i < length; i++)
666 printf ("%s\n", tags[i]);
672 if (_item_split (item, &group, &key))
675 value = g_key_file_get_string_list (config->key_file,
679 fprintf (stderr, "Unknown configuration item: %s.%s\n",
684 for (i = 0; i < length; i++)
685 printf ("%s\n", value[i]);
690 notmuch_config_close (config);
696 notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
698 notmuch_config_t *config;
702 if (_item_split (item, &group, &key))
705 config = notmuch_config_open (ctx, NULL, NULL);
709 /* With only the name of an item, we clear it from the
710 * configuration file.
712 * With a single value, we set it as a string.
714 * With multiple values, we set them as a string list.
718 g_key_file_remove_key (config->key_file, group, key, NULL);
721 g_key_file_set_string (config->key_file, group, key, argv[0]);
724 g_key_file_set_string_list (config->key_file, group, key,
725 (const gchar **) argv, argc);
729 ret = notmuch_config_save (config);
730 notmuch_config_close (config);
736 notmuch_config_command (void *ctx, int argc, char *argv[])
739 fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
743 if (strcmp (argv[0], "get") == 0)
744 return notmuch_config_command_get (ctx, argv[1]);
745 else if (strcmp (argv[0], "set") == 0)
746 return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
748 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
754 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
756 return config->maildir_synchronize_flags;
760 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
761 notmuch_bool_t synchronize_flags)
763 g_key_file_set_boolean (config->key_file,
764 "maildir", "synchronize_flags", synchronize_flags);
765 config->maildir_synchronize_flags = synchronize_flags;