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;
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)
112 long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
113 char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
114 struct passwd passwd, *ignored;
118 if (pw_buf_size == -1) pw_buf_size = 64;
120 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
121 pw_buf_size, &ignored)) == ERANGE) {
122 pw_buf_size = pw_buf_size * 2;
123 pw_buf = talloc_zero_size(ctx, pw_buf_size);
127 char *comma = strchr (passwd.pw_gecos, ',');
129 name = talloc_strndup (ctx, passwd.pw_gecos,
130 comma - passwd.pw_gecos);
132 name = talloc_strdup (ctx, passwd.pw_gecos);
134 name = talloc_strdup (ctx, "");
137 talloc_free (pw_buf);
143 get_username_from_passwd_file (void *ctx)
145 long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
146 char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
147 struct passwd passwd, *ignored;
151 if (pw_buf_size == -1) pw_buf_size = 64;
152 while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
153 pw_buf_size, &ignored)) == ERANGE) {
154 pw_buf_size = pw_buf_size * 2;
155 pw_buf = talloc_zero_size(ctx, pw_buf_size);
159 name = talloc_strdup (ctx, passwd.pw_name);
161 name = talloc_strdup (ctx, "");
163 talloc_free (pw_buf);
168 /* Open the named notmuch configuration file. If the filename is NULL,
169 * the value of the environment variable $NOTMUCH_CONFIG will be used.
170 * If $NOTMUCH_CONFIG is unset, the default configuration file
171 * ($HOME/.notmuch-config) will be used.
173 * If any error occurs, (out of memory, or a permission-denied error,
174 * etc.), this function will print a message to stderr and return
177 * FILE NOT FOUND: When the specified configuration file (whether from
178 * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
179 * exist, the behavior of this function depends on the 'is_new_ret'
182 * If is_new_ret is NULL, then a "file not found" message will be
183 * printed to stderr and NULL will be returned.
185 * If is_new_ret is non-NULL then a default configuration will be
186 * returned and *is_new_ret will be set to 1 on return so that
187 * the caller can recognize this case.
189 * These default configuration settings are determined as
192 * database_path: $HOME/mail
194 * user_name: From /etc/passwd
196 * user_primary_mail: $EMAIL variable if set, otherwise
197 * constructed from the username and
198 * hostname of the current machine.
200 * user_other_email: Not set.
202 * The default configuration also contains comments to guide the
203 * user in editing the file directly.
206 notmuch_config_open (void *ctx,
207 const char *filename,
208 notmuch_bool_t *is_new_ret)
210 GError *error = NULL;
213 char *notmuch_config_env = NULL;
214 int file_had_database_group;
215 int file_had_new_group;
216 int file_had_user_group;
217 int file_had_maildir_group;
222 notmuch_config_t *config = talloc (ctx, notmuch_config_t);
223 if (config == NULL) {
224 fprintf (stderr, "Out of memory.\n");
228 talloc_set_destructor (config, notmuch_config_destructor);
231 config->filename = talloc_strdup (config, filename);
232 } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
233 config->filename = talloc_strdup (config, notmuch_config_env);
235 config->filename = talloc_asprintf (config, "%s/.notmuch-config",
239 config->key_file = g_key_file_new ();
241 config->database_path = NULL;
242 config->user_name = NULL;
243 config->user_primary_email = NULL;
244 config->user_other_email = NULL;
245 config->user_other_email_length = 0;
246 config->new_tags = NULL;
247 config->new_tags_length = 0;
248 config->maildir_synchronize_flags = TRUE;
250 if (! g_key_file_load_from_file (config->key_file,
252 G_KEY_FILE_KEEP_COMMENTS,
255 /* If the caller passed a non-NULL value for is_new_ret, then
256 * the caller is prepared for a default configuration file in
257 * the case of FILE NOT FOUND. Otherwise, any read failure is
261 error->domain == G_FILE_ERROR &&
262 error->code == G_FILE_ERROR_NOENT)
264 g_error_free (error);
269 fprintf (stderr, "Error reading configuration file %s: %s\n",
270 config->filename, error->message);
271 talloc_free (config);
272 g_error_free (error);
277 /* Whenever we know of configuration sections that don't appear in
278 * the configuration file, we add some comments to help the user
279 * understand what can be done.
281 * It would be convenient to just add those comments now, but
282 * apparently g_key_file will clear any comments when keys are
283 * added later that create the groups. So we have to check for the
284 * groups now, but add the comments only after setting all of our
287 file_had_database_group = g_key_file_has_group (config->key_file,
289 file_had_new_group = g_key_file_has_group (config->key_file, "new");
290 file_had_user_group = g_key_file_has_group (config->key_file, "user");
291 file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
294 if (notmuch_config_get_database_path (config) == NULL) {
295 char *path = talloc_asprintf (config, "%s/mail",
297 notmuch_config_set_database_path (config, path);
301 if (notmuch_config_get_user_name (config) == NULL) {
302 char *name = get_name_from_passwd_file (config);
303 notmuch_config_set_user_name (config, name);
307 if (notmuch_config_get_user_primary_email (config) == NULL) {
308 char *email = getenv ("EMAIL");
310 notmuch_config_set_user_primary_email (config, email);
313 struct hostent *hostent;
314 const char *domainname;
316 char *username = get_username_from_passwd_file (config);
318 gethostname (hostname, 256);
319 hostname[255] = '\0';
321 hostent = gethostbyname (hostname);
322 if (hostent && (domainname = strchr (hostent->h_name, '.')))
325 domainname = "(none)";
327 email = talloc_asprintf (config, "%s@%s.%s",
328 username, hostname, domainname);
330 notmuch_config_set_user_primary_email (config, email);
332 talloc_free (username);
337 if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
338 const char *tags[] = { "unread", "inbox" };
339 notmuch_config_set_new_tags (config, tags, 2);
343 config->maildir_synchronize_flags =
344 g_key_file_get_boolean (config->key_file,
345 "maildir", "synchronize_flags", &error);
347 config->maildir_synchronize_flags = TRUE;
348 g_error_free (error);
351 /* Whenever we know of configuration sections that don't appear in
352 * the configuration file, we add some comments to help the user
353 * understand what can be done. */
356 g_key_file_set_comment (config->key_file, NULL, NULL,
357 toplevel_config_comment, NULL);
360 if (! file_had_database_group)
362 g_key_file_set_comment (config->key_file, "database", NULL,
363 database_config_comment, NULL);
366 if (! file_had_new_group)
368 g_key_file_set_comment (config->key_file, "new", NULL,
369 new_config_comment, NULL);
372 if (! file_had_user_group)
374 g_key_file_set_comment (config->key_file, "user", NULL,
375 user_config_comment, NULL);
378 if (! file_had_maildir_group)
380 g_key_file_set_comment (config->key_file, "maildir", NULL,
381 maildir_config_comment, NULL);
385 *is_new_ret = is_new;
390 /* Close the given notmuch_config_t object, freeing all resources.
392 * Note: Any changes made to the configuration are *not* saved by this
393 * function. To save changes, call notmuch_config_save before
394 * notmuch_config_close.
397 notmuch_config_close (notmuch_config_t *config)
399 talloc_free (config);
402 /* Save any changes made to the notmuch configuration.
404 * Any comments originally in the file will be preserved.
406 * Returns 0 if successful, and 1 in case of any error, (after
407 * printing a description of the error to stderr).
410 notmuch_config_save (notmuch_config_t *config)
414 GError *error = NULL;
416 data = g_key_file_to_data (config->key_file, &length, NULL);
418 fprintf (stderr, "Out of memory.\n");
422 if (! g_file_set_contents (config->filename, data, length, &error)) {
423 fprintf (stderr, "Error saving configuration to %s: %s\n",
424 config->filename, error->message);
425 g_error_free (error);
435 notmuch_config_get_database_path (notmuch_config_t *config)
439 if (config->database_path == NULL) {
440 path = g_key_file_get_string (config->key_file,
441 "database", "path", NULL);
443 config->database_path = talloc_strdup (config, path);
448 return config->database_path;
452 notmuch_config_set_database_path (notmuch_config_t *config,
453 const char *database_path)
455 g_key_file_set_string (config->key_file,
456 "database", "path", database_path);
458 talloc_free (config->database_path);
459 config->database_path = NULL;
463 notmuch_config_get_user_name (notmuch_config_t *config)
467 if (config->user_name == NULL) {
468 name = g_key_file_get_string (config->key_file,
469 "user", "name", NULL);
471 config->user_name = talloc_strdup (config, name);
476 return config->user_name;
480 notmuch_config_set_user_name (notmuch_config_t *config,
481 const char *user_name)
483 g_key_file_set_string (config->key_file,
484 "user", "name", user_name);
486 talloc_free (config->user_name);
487 config->user_name = NULL;
491 notmuch_config_get_user_primary_email (notmuch_config_t *config)
495 if (config->user_primary_email == NULL) {
496 email = g_key_file_get_string (config->key_file,
497 "user", "primary_email", NULL);
499 config->user_primary_email = talloc_strdup (config, email);
504 return config->user_primary_email;
508 notmuch_config_set_user_primary_email (notmuch_config_t *config,
509 const char *primary_email)
511 g_key_file_set_string (config->key_file,
512 "user", "primary_email", primary_email);
514 talloc_free (config->user_primary_email);
515 config->user_primary_email = NULL;
519 notmuch_config_get_user_other_email (notmuch_config_t *config,
523 size_t emails_length;
526 if (config->user_other_email == NULL) {
527 emails = g_key_file_get_string_list (config->key_file,
528 "user", "other_email",
529 &emails_length, NULL);
531 config->user_other_email = talloc_size (config,
533 (emails_length + 1));
534 for (i = 0; i < emails_length; i++)
535 config->user_other_email[i] = talloc_strdup (config->user_other_email,
537 config->user_other_email[i] = NULL;
541 config->user_other_email_length = emails_length;
545 *length = config->user_other_email_length;
546 return config->user_other_email;
550 notmuch_config_set_user_other_email (notmuch_config_t *config,
551 const char *other_email[],
554 g_key_file_set_string_list (config->key_file,
555 "user", "other_email",
556 other_email, length);
558 talloc_free (config->user_other_email);
559 config->user_other_email = NULL;
563 notmuch_config_get_new_tags (notmuch_config_t *config,
570 if (config->new_tags == NULL) {
571 tags = g_key_file_get_string_list (config->key_file,
575 config->new_tags = talloc_size (config,
578 for (i = 0; i < tags_length; i++)
579 config->new_tags[i] = talloc_strdup (config->new_tags,
581 config->new_tags[i] = NULL;
585 config->new_tags_length = tags_length;
589 *length = config->new_tags_length;
590 return config->new_tags;
594 notmuch_config_set_new_tags (notmuch_config_t *config,
595 const char *new_tags[],
598 g_key_file_set_string_list (config->key_file,
602 talloc_free (config->new_tags);
603 config->new_tags = NULL;
606 /* Given a configuration item of the form <group>.<key> return the
607 * component group and key. If any error occurs, print a message on
608 * stderr and return 1. Otherwise, return 0.
610 * Note: This function modifies the original 'item' string.
613 _item_split (char *item, char **group, char **key)
619 period = index (item, '.');
620 if (period == NULL || *(period+1) == '\0') {
622 "Invalid configuration name: %s\n"
623 "(Should be of the form <section>.<item>)\n", item);
634 notmuch_config_command_get (void *ctx, char *item)
636 notmuch_config_t *config;
638 config = notmuch_config_open (ctx, NULL, NULL);
642 if (strcmp(item, "database.path") == 0) {
643 printf ("%s\n", notmuch_config_get_database_path (config));
644 } else if (strcmp(item, "user.name") == 0) {
645 printf ("%s\n", notmuch_config_get_user_name (config));
646 } else if (strcmp(item, "user.primary_email") == 0) {
647 printf ("%s\n", notmuch_config_get_user_primary_email (config));
648 } else if (strcmp(item, "user.other_email") == 0) {
649 const char **other_email;
652 other_email = notmuch_config_get_user_other_email (config, &length);
653 for (i = 0; i < length; i++)
654 printf ("%s\n", other_email[i]);
655 } else if (strcmp(item, "new.tags") == 0) {
659 tags = notmuch_config_get_new_tags (config, &length);
660 for (i = 0; i < length; i++)
661 printf ("%s\n", tags[i]);
667 if (_item_split (item, &group, &key))
670 value = g_key_file_get_string_list (config->key_file,
674 fprintf (stderr, "Unknown configuration item: %s.%s\n",
679 for (i = 0; i < length; i++)
680 printf ("%s\n", value[i]);
685 notmuch_config_close (config);
691 notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
693 notmuch_config_t *config;
697 if (_item_split (item, &group, &key))
700 config = notmuch_config_open (ctx, NULL, NULL);
704 /* With only the name of an item, we clear it from the
705 * configuration file.
707 * With a single value, we set it as a string.
709 * With multiple values, we set them as a string list.
713 g_key_file_remove_key (config->key_file, group, key, NULL);
716 g_key_file_set_string (config->key_file, group, key, argv[0]);
719 g_key_file_set_string_list (config->key_file, group, key,
720 (const gchar **) argv, argc);
724 ret = notmuch_config_save (config);
725 notmuch_config_close (config);
731 notmuch_config_command (void *ctx, int argc, char *argv[])
734 fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
738 if (strcmp (argv[0], "get") == 0)
739 return notmuch_config_command_get (ctx, argv[1]);
740 else if (strcmp (argv[0], "set") == 0)
741 return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
743 fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
749 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
751 return config->maildir_synchronize_flags;
755 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
756 notmuch_bool_t synchronize_flags)
758 g_key_file_set_boolean (config->key_file,
759 "maildir", "synchronize_flags", synchronize_flags);
760 config->maildir_synchronize_flags = synchronize_flags;