]> git.notmuchmail.org Git - notmuch/blob - notmuch-config.c
config: read user.name from $NAME if set
[notmuch] / notmuch-config.c
1 /* notmuch - Not much of an email program, (just index and search)
2  *
3  * Copyright © 2009 Carl Worth
4  *
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.
9  *
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.
14  *
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/ .
17  *
18  * Author: Carl Worth <cworth@cworth.org>
19  */
20
21 #include "notmuch-client.h"
22
23 #include <pwd.h>
24 #include <netdb.h>
25 #include <assert.h>
26
27 static const char toplevel_config_comment[] =
28     " .notmuch-config - Configuration file for the notmuch mail system\n"
29     "\n"
30     " For more information about notmuch, see http://notmuchmail.org";
31
32 static const char database_config_comment[] =
33     " Database configuration\n"
34     "\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";
40
41 static const char new_config_comment[] =
42     " Configuration for \"notmuch new\"\n"
43     "\n"
44     " The following options are supported here:\n"
45     "\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"
48     "\n"
49     "\tignore   A list (separated by ';') of file and directory names\n"
50     "\t that will not be searched for messages by \"notmuch new\".\n"
51     "\n"
52     "\t NOTE: *Every* file/directory that goes by one of those\n"
53     "\t names will be ignored, independent of its depth/location\n"
54     "\t in the mail store.\n";
55
56 static const char user_config_comment[] =
57     " User configuration\n"
58     "\n"
59     " Here is where you can let notmuch know how you would like to be\n"
60     " addressed. Valid settings are\n"
61     "\n"
62     "\tname             Your full name.\n"
63     "\tprimary_email    Your primary email address.\n"
64     "\tother_email      A list (separated by ';') of other email addresses\n"
65     "\t         at which you receive email.\n"
66     "\n"
67     " Notmuch will use the various email addresses configured here when\n"
68     " formatting replies. It will avoid including your own addresses in the\n"
69     " recipient list of replies, and will set the From address based on the\n"
70     " address to which the original email was addressed.\n";
71
72 static const char maildir_config_comment[] =
73     " Maildir compatibility configuration\n"
74     "\n"
75     " The following option is supported here:\n"
76     "\n"
77     "\tsynchronize_flags      Valid values are true and false.\n"
78     "\n"
79     "\tIf true, then the following maildir flags (in message filenames)\n"
80     "\twill be synchronized with the corresponding notmuch tags:\n"
81     "\n"
82     "\t\tFlag   Tag\n"
83     "\t\t----   -------\n"
84     "\t\tD      draft\n"
85     "\t\tF      flagged\n"
86     "\t\tP      passed\n"
87     "\t\tR      replied\n"
88     "\t\tS      unread (added when 'S' flag is not present)\n"
89     "\n"
90     "\tThe \"notmuch new\" command will notice flag changes in filenames\n"
91     "\tand update tags, while the \"notmuch tag\" and \"notmuch restore\"\n"
92     "\tcommands will notice tag changes and update flags in filenames\n";
93
94 static const char search_config_comment[] =
95     " Search configuration\n"
96     "\n"
97     " The following option is supported here:\n"
98     "\n"
99     "\texclude_tags\n"
100     "\t\tA ;-separated list of tags that will be excluded from\n"
101     "\t\tsearch results by default.  Using an excluded tag in a\n"
102     "\t\tquery will override that exclusion.\n";
103
104 struct _notmuch_config {
105     char *filename;
106     GKeyFile *key_file;
107     notmuch_bool_t is_new;
108
109     char *database_path;
110     char *user_name;
111     char *user_primary_email;
112     const char **user_other_email;
113     size_t user_other_email_length;
114     const char **new_tags;
115     size_t new_tags_length;
116     const char **new_ignore;
117     size_t new_ignore_length;
118     notmuch_bool_t maildir_synchronize_flags;
119     const char **search_exclude_tags;
120     size_t search_exclude_tags_length;
121 };
122
123 static int
124 notmuch_config_destructor (notmuch_config_t *config)
125 {
126     if (config->key_file)
127         g_key_file_free (config->key_file);
128
129     return 0;
130 }
131
132 static char *
133 get_name_from_passwd_file (void *ctx)
134 {
135     long pw_buf_size;
136     char *pw_buf;
137     struct passwd passwd, *ignored;
138     char *name;
139     int e;
140
141     pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
142     if (pw_buf_size == -1) pw_buf_size = 64;
143     pw_buf = talloc_size (ctx, pw_buf_size);
144
145     while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
146                             pw_buf_size, &ignored)) == ERANGE) {
147         pw_buf_size = pw_buf_size * 2;
148         pw_buf = talloc_zero_size(ctx, pw_buf_size);
149     }
150
151     if (e == 0) {
152         char *comma = strchr (passwd.pw_gecos, ',');
153         if (comma)
154             name = talloc_strndup (ctx, passwd.pw_gecos,
155                                    comma - passwd.pw_gecos);
156         else
157             name = talloc_strdup (ctx, passwd.pw_gecos);
158     } else {
159         name = talloc_strdup (ctx, "");
160     }
161
162     talloc_free (pw_buf);
163
164     return name;
165 }
166
167 static char *
168 get_username_from_passwd_file (void *ctx)
169 {
170     long pw_buf_size;
171     char *pw_buf;
172     struct passwd passwd, *ignored;
173     char *name;
174     int e;
175
176     pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
177     if (pw_buf_size == -1) pw_buf_size = 64;
178     pw_buf = talloc_zero_size (ctx, pw_buf_size);
179
180     while ((e = getpwuid_r (getuid (), &passwd, pw_buf,
181                             pw_buf_size, &ignored)) == ERANGE) {
182         pw_buf_size = pw_buf_size * 2;
183         pw_buf = talloc_zero_size(ctx, pw_buf_size);
184     }
185
186     if (e == 0)
187         name = talloc_strdup (ctx, passwd.pw_name);
188     else
189         name = talloc_strdup (ctx, "");
190
191     talloc_free (pw_buf);
192
193     return name;
194 }
195
196 /* Open the named notmuch configuration file. If the filename is NULL,
197  * the value of the environment variable $NOTMUCH_CONFIG will be used.
198  * If $NOTMUCH_CONFIG is unset, the default configuration file
199  * ($HOME/.notmuch-config) will be used.
200  *
201  * If any error occurs, (out of memory, or a permission-denied error,
202  * etc.), this function will print a message to stderr and return
203  * NULL.
204  *
205  * FILE NOT FOUND: When the specified configuration file (whether from
206  * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
207  * exist, the behavior of this function depends on the 'is_new_ret'
208  * variable.
209  *
210  *      If is_new_ret is NULL, then a "file not found" message will be
211  *      printed to stderr and NULL will be returned.
212
213  *      If is_new_ret is non-NULL then a default configuration will be
214  *      returned and *is_new_ret will be set to 1 on return so that
215  *      the caller can recognize this case.
216  *
217  *      These default configuration settings are determined as
218  *      follows:
219  *
220  *              database_path:          $HOME/mail
221  *
222  *              user_name:              $NAME variable if set, otherwise
223  *                                      read from /etc/passwd
224  *
225  *              user_primary_mail:      $EMAIL variable if set, otherwise
226  *                                      constructed from the username and
227  *                                      hostname of the current machine.
228  *
229  *              user_other_email:       Not set.
230  *
231  *      The default configuration also contains comments to guide the
232  *      user in editing the file directly.
233  */
234 notmuch_config_t *
235 notmuch_config_open (void *ctx,
236                      const char *filename,
237                      notmuch_bool_t create_new)
238 {
239     GError *error = NULL;
240     size_t tmp;
241     char *notmuch_config_env = NULL;
242     int file_had_database_group;
243     int file_had_new_group;
244     int file_had_user_group;
245     int file_had_maildir_group;
246     int file_had_search_group;
247
248     notmuch_config_t *config = talloc (ctx, notmuch_config_t);
249     if (config == NULL) {
250         fprintf (stderr, "Out of memory.\n");
251         return NULL;
252     }
253     
254     talloc_set_destructor (config, notmuch_config_destructor);
255
256     if (filename) {
257         config->filename = talloc_strdup (config, filename);
258     } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) {
259         config->filename = talloc_strdup (config, notmuch_config_env);
260     } else {
261         config->filename = talloc_asprintf (config, "%s/.notmuch-config",
262                                             getenv ("HOME"));
263     }
264
265     config->key_file = g_key_file_new ();
266
267     config->is_new = FALSE;
268     config->database_path = NULL;
269     config->user_name = NULL;
270     config->user_primary_email = NULL;
271     config->user_other_email = NULL;
272     config->user_other_email_length = 0;
273     config->new_tags = NULL;
274     config->new_tags_length = 0;
275     config->new_ignore = NULL;
276     config->new_ignore_length = 0;
277     config->maildir_synchronize_flags = TRUE;
278     config->search_exclude_tags = NULL;
279     config->search_exclude_tags_length = 0;
280
281     if (! g_key_file_load_from_file (config->key_file,
282                                      config->filename,
283                                      G_KEY_FILE_KEEP_COMMENTS,
284                                      &error))
285     {
286         /* If create_new is true, then the caller is prepared for a
287          * default configuration file in the case of FILE NOT
288          * FOUND. Otherwise, any read failure is an error.
289          */
290         if (create_new &&
291             error->domain == G_FILE_ERROR &&
292             error->code == G_FILE_ERROR_NOENT)
293         {
294             g_error_free (error);
295             config->is_new = TRUE;
296         }
297         else
298         {
299             fprintf (stderr, "Error reading configuration file %s: %s\n",
300                      config->filename, error->message);
301             talloc_free (config);
302             g_error_free (error);
303             return NULL;
304         }
305     }
306
307     /* Whenever we know of configuration sections that don't appear in
308      * the configuration file, we add some comments to help the user
309      * understand what can be done.
310      *
311      * It would be convenient to just add those comments now, but
312      * apparently g_key_file will clear any comments when keys are
313      * added later that create the groups. So we have to check for the
314      * groups now, but add the comments only after setting all of our
315      * values.
316      */
317     file_had_database_group = g_key_file_has_group (config->key_file,
318                                                     "database");
319     file_had_new_group = g_key_file_has_group (config->key_file, "new");
320     file_had_user_group = g_key_file_has_group (config->key_file, "user");
321     file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir");
322     file_had_search_group = g_key_file_has_group (config->key_file, "search");
323
324
325     if (notmuch_config_get_database_path (config) == NULL) {
326         char *path = talloc_asprintf (config, "%s/mail",
327                                       getenv ("HOME"));
328         notmuch_config_set_database_path (config, path);
329         talloc_free (path);
330     }
331
332     if (notmuch_config_get_user_name (config) == NULL) {
333         char *name = getenv ("NAME");
334         if (! name)
335             name = get_name_from_passwd_file (config);
336         notmuch_config_set_user_name (config, name);
337         talloc_free (name);
338     }
339
340     if (notmuch_config_get_user_primary_email (config) == NULL) {
341         char *email = getenv ("EMAIL");
342         if (email) {
343             notmuch_config_set_user_primary_email (config, email);
344         } else {
345             char hostname[256];
346             struct hostent *hostent;
347             const char *domainname;
348
349             char *username = get_username_from_passwd_file (config);
350
351             gethostname (hostname, 256);
352             hostname[255] = '\0';
353
354             hostent = gethostbyname (hostname);
355             if (hostent && (domainname = strchr (hostent->h_name, '.')))
356                 domainname += 1;
357             else
358                 domainname = "(none)";
359
360             email = talloc_asprintf (config, "%s@%s.%s",
361                                      username, hostname, domainname);
362
363             notmuch_config_set_user_primary_email (config, email);
364
365             talloc_free (username);
366             talloc_free (email);
367         }
368     }
369
370     if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
371         const char *tags[] = { "unread", "inbox" };
372         notmuch_config_set_new_tags (config, tags, 2);
373     }
374
375     if (notmuch_config_get_new_ignore (config, &tmp) == NULL) {
376         notmuch_config_set_new_ignore (config, NULL, 0);
377     }
378
379     if (notmuch_config_get_search_exclude_tags (config, &tmp) == NULL) {
380         if (config->is_new) {
381             const char *tags[] = { "deleted", "spam" };
382             notmuch_config_set_search_exclude_tags (config, tags, 2);
383         } else {
384             notmuch_config_set_search_exclude_tags (config, NULL, 0);
385         }
386     }
387
388     error = NULL;
389     config->maildir_synchronize_flags =
390         g_key_file_get_boolean (config->key_file,
391                                 "maildir", "synchronize_flags", &error);
392     if (error) {
393         notmuch_config_set_maildir_synchronize_flags (config, TRUE);
394         g_error_free (error);
395     }
396
397     /* Whenever we know of configuration sections that don't appear in
398      * the configuration file, we add some comments to help the user
399      * understand what can be done. */
400     if (config->is_new)
401         g_key_file_set_comment (config->key_file, NULL, NULL,
402                                 toplevel_config_comment, NULL);
403
404     if (! file_had_database_group)
405         g_key_file_set_comment (config->key_file, "database", NULL,
406                                 database_config_comment, NULL);
407
408     if (! file_had_new_group)
409         g_key_file_set_comment (config->key_file, "new", NULL,
410                                 new_config_comment, NULL);
411
412     if (! file_had_user_group)
413         g_key_file_set_comment (config->key_file, "user", NULL,
414                                 user_config_comment, NULL);
415
416     if (! file_had_maildir_group)
417         g_key_file_set_comment (config->key_file, "maildir", NULL,
418                                 maildir_config_comment, NULL);
419
420     if (! file_had_search_group)
421         g_key_file_set_comment (config->key_file, "search", NULL,
422                                 search_config_comment, NULL);
423
424     return config;
425 }
426
427 /* Close the given notmuch_config_t object, freeing all resources.
428  * 
429  * Note: Any changes made to the configuration are *not* saved by this
430  * function. To save changes, call notmuch_config_save before
431  * notmuch_config_close.
432 */
433 void
434 notmuch_config_close (notmuch_config_t *config)
435 {
436     talloc_free (config);
437 }
438
439 /* Save any changes made to the notmuch configuration.
440  *
441  * Any comments originally in the file will be preserved.
442  *
443  * Returns 0 if successful, and 1 in case of any error, (after
444  * printing a description of the error to stderr).
445  */
446 int
447 notmuch_config_save (notmuch_config_t *config)
448 {
449     size_t length;
450     char *data, *filename;
451     GError *error = NULL;
452
453     data = g_key_file_to_data (config->key_file, &length, NULL);
454     if (data == NULL) {
455         fprintf (stderr, "Out of memory.\n");
456         return 1;
457     }
458
459     /* Try not to overwrite symlinks. */
460     filename = canonicalize_file_name (config->filename);
461     if (! filename) {
462         if (errno == ENOENT) {
463             filename = strdup (config->filename);
464             if (! filename) {
465                 fprintf (stderr, "Out of memory.\n");
466                 g_free (data);
467                 return 1;
468             }
469         } else {
470             fprintf (stderr, "Error canonicalizing %s: %s\n", config->filename,
471                      strerror (errno));
472             g_free (data);
473             return 1;
474         }
475     }
476
477     if (! g_file_set_contents (filename, data, length, &error)) {
478         if (strcmp (filename, config->filename) != 0) {
479             fprintf (stderr, "Error saving configuration to %s (-> %s): %s\n",
480                      config->filename, filename, error->message);
481         } else {
482             fprintf (stderr, "Error saving configuration to %s: %s\n",
483                      filename, error->message);
484         }
485         g_error_free (error);
486         free (filename);
487         g_free (data);
488         return 1;
489     }
490
491     free (filename);
492     g_free (data);
493     return 0;
494 }
495
496 notmuch_bool_t
497 notmuch_config_is_new (notmuch_config_t *config)
498 {
499     return config->is_new;
500 }
501
502 static const char *
503 _config_get (notmuch_config_t *config, char **field,
504              const char *group, const char *key)
505 {
506     /* read from config file and cache value, if not cached already */
507     if (*field == NULL) {
508         char *value;
509         value = g_key_file_get_string (config->key_file, group, key, NULL);
510         if (value) {
511             *field = talloc_strdup (config, value);
512             free (value);
513         }
514     }
515     return *field;
516 }
517
518 static void
519 _config_set (notmuch_config_t *config, char **field,
520              const char *group, const char *key, const char *value)
521 {
522     g_key_file_set_string (config->key_file, group, key, value);
523
524     /* drop the cached value */
525     talloc_free (*field);
526     *field = NULL;
527 }
528
529 static const char **
530 _config_get_list (notmuch_config_t *config,
531                   const char *section, const char *key,
532                   const char ***outlist, size_t *list_length, size_t *ret_length)
533 {
534     assert(outlist);
535
536     /* read from config file and cache value, if not cached already */
537     if (*outlist == NULL) {
538
539         char **inlist = g_key_file_get_string_list (config->key_file,
540                                              section, key, list_length, NULL);
541         if (inlist) {
542             unsigned int i;
543
544             *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
545
546             for (i = 0; i < *list_length; i++)
547                 (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
548
549             (*outlist)[i] = NULL;
550
551             g_strfreev (inlist);
552         }
553     }
554
555     if (ret_length)
556         *ret_length = *list_length;
557
558     return *outlist;
559 }
560
561 static void
562 _config_set_list (notmuch_config_t *config,
563                   const char *group, const char *name,
564                   const char *list[],
565                   size_t length, const char ***config_var )
566 {
567     g_key_file_set_string_list (config->key_file, group, name, list, length);
568
569     /* drop the cached value */
570     talloc_free (*config_var);
571     *config_var = NULL;
572 }
573
574 const char *
575 notmuch_config_get_database_path (notmuch_config_t *config)
576 {
577     return _config_get (config, &config->database_path, "database", "path");
578 }
579
580 void
581 notmuch_config_set_database_path (notmuch_config_t *config,
582                                   const char *database_path)
583 {
584     _config_set (config, &config->database_path, "database", "path", database_path);
585 }
586
587 const char *
588 notmuch_config_get_user_name (notmuch_config_t *config)
589 {
590     return _config_get (config, &config->user_name, "user", "name");
591 }
592
593 void
594 notmuch_config_set_user_name (notmuch_config_t *config,
595                               const char *user_name)
596 {
597     _config_set (config, &config->user_name, "user", "name", user_name);
598 }
599
600 const char *
601 notmuch_config_get_user_primary_email (notmuch_config_t *config)
602 {
603     return _config_get (config, &config->user_primary_email, "user", "primary_email");
604 }
605
606 void
607 notmuch_config_set_user_primary_email (notmuch_config_t *config,
608                                        const char *primary_email)
609 {
610     _config_set (config, &config->user_primary_email, "user", "primary_email", primary_email);
611 }
612
613 const char **
614 notmuch_config_get_user_other_email (notmuch_config_t *config,   size_t *length)
615 {
616     return _config_get_list (config, "user", "other_email",
617                              &(config->user_other_email),
618                              &(config->user_other_email_length), length);
619 }
620
621 const char **
622 notmuch_config_get_new_tags (notmuch_config_t *config,   size_t *length)
623 {
624     return _config_get_list (config, "new", "tags",
625                              &(config->new_tags),
626                              &(config->new_tags_length), length);
627 }
628
629 const char **
630 notmuch_config_get_new_ignore (notmuch_config_t *config, size_t *length)
631 {
632     return _config_get_list (config, "new", "ignore",
633                              &(config->new_ignore),
634                              &(config->new_ignore_length), length);
635 }
636
637 void
638 notmuch_config_set_user_other_email (notmuch_config_t *config,
639                                      const char *list[],
640                                      size_t length)
641 {
642     _config_set_list (config, "user", "other_email", list, length,
643                      &(config->user_other_email));
644 }
645
646 void
647 notmuch_config_set_new_tags (notmuch_config_t *config,
648                                      const char *list[],
649                                      size_t length)
650 {
651     _config_set_list (config, "new", "tags", list, length,
652                      &(config->new_tags));
653 }
654
655 void
656 notmuch_config_set_new_ignore (notmuch_config_t *config,
657                                const char *list[],
658                                size_t length)
659 {
660     _config_set_list (config, "new", "ignore", list, length,
661                      &(config->new_ignore));
662 }
663
664 const char **
665 notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length)
666 {
667     return _config_get_list (config, "search", "exclude_tags",
668                              &(config->search_exclude_tags),
669                              &(config->search_exclude_tags_length), length);
670 }
671
672 void
673 notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
674                                       const char *list[],
675                                       size_t length)
676 {
677     _config_set_list (config, "search", "exclude_tags", list, length,
678                       &(config->search_exclude_tags));
679 }
680
681 /* Given a configuration item of the form <group>.<key> return the
682  * component group and key. If any error occurs, print a message on
683  * stderr and return 1. Otherwise, return 0.
684  *
685  * Note: This function modifies the original 'item' string.
686  */
687 static int
688 _item_split (char *item, char **group, char **key)
689 {
690     char *period;
691
692     *group = item;
693
694     period = strchr (item, '.');
695     if (period == NULL || *(period+1) == '\0') {
696         fprintf (stderr,
697                  "Invalid configuration name: %s\n"
698                  "(Should be of the form <section>.<item>)\n", item);
699         return 1;
700     }
701
702     *period = '\0';
703     *key = period + 1;
704
705     return 0;
706 }
707
708 static int
709 notmuch_config_command_get (notmuch_config_t *config, char *item)
710 {
711     if (strcmp(item, "database.path") == 0) {
712         printf ("%s\n", notmuch_config_get_database_path (config));
713     } else if (strcmp(item, "user.name") == 0) {
714         printf ("%s\n", notmuch_config_get_user_name (config));
715     } else if (strcmp(item, "user.primary_email") == 0) {
716         printf ("%s\n", notmuch_config_get_user_primary_email (config));
717     } else if (strcmp(item, "user.other_email") == 0) {
718         const char **other_email;
719         size_t i, length;
720         
721         other_email = notmuch_config_get_user_other_email (config, &length);
722         for (i = 0; i < length; i++)
723             printf ("%s\n", other_email[i]);
724     } else if (strcmp(item, "new.tags") == 0) {
725         const char **tags;
726         size_t i, length;
727
728         tags = notmuch_config_get_new_tags (config, &length);
729         for (i = 0; i < length; i++)
730             printf ("%s\n", tags[i]);
731     } else {
732         char **value;
733         size_t i, length;
734         char *group, *key;
735
736         if (_item_split (item, &group, &key))
737             return 1;
738
739         value = g_key_file_get_string_list (config->key_file,
740                                             group, key,
741                                             &length, NULL);
742         if (value == NULL) {
743             fprintf (stderr, "Unknown configuration item: %s.%s\n",
744                      group, key);
745             return 1;
746         }
747
748         for (i = 0; i < length; i++)
749             printf ("%s\n", value[i]);
750
751         g_strfreev (value);
752     }
753
754     return 0;
755 }
756
757 static int
758 notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[])
759 {
760     char *group, *key;
761
762     if (_item_split (item, &group, &key))
763         return 1;
764
765     /* With only the name of an item, we clear it from the
766      * configuration file.
767      *
768      * With a single value, we set it as a string.
769      *
770      * With multiple values, we set them as a string list.
771      */
772     switch (argc) {
773     case 0:
774         g_key_file_remove_key (config->key_file, group, key, NULL);
775         break;
776     case 1:
777         g_key_file_set_string (config->key_file, group, key, argv[0]);
778         break;
779     default:
780         g_key_file_set_string_list (config->key_file, group, key,
781                                     (const gchar **) argv, argc);
782         break;
783     }
784
785     return notmuch_config_save (config);
786 }
787
788 static int
789 notmuch_config_command_list (notmuch_config_t *config)
790 {
791     char **groups;
792     size_t g, groups_length;
793
794     groups = g_key_file_get_groups (config->key_file, &groups_length);
795     if (groups == NULL)
796         return 1;
797
798     for (g = 0; g < groups_length; g++) {
799         char **keys;
800         size_t k, keys_length;
801
802         keys = g_key_file_get_keys (config->key_file,
803                                     groups[g], &keys_length, NULL);
804         if (keys == NULL)
805             continue;
806
807         for (k = 0; k < keys_length; k++) {
808             char *value;
809
810             value = g_key_file_get_string (config->key_file,
811                                            groups[g], keys[k], NULL);
812             if (value != NULL) {
813                 printf ("%s.%s=%s\n", groups[g], keys[k], value);
814                 free (value);
815             }
816         }
817
818         g_strfreev (keys);
819     }
820
821     g_strfreev (groups);
822
823     return 0;
824 }
825
826 int
827 notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
828 {
829     int ret;
830
831     argc--; argv++; /* skip subcommand argument */
832
833     if (argc < 1) {
834         fprintf (stderr, "Error: notmuch config requires at least one argument.\n");
835         return EXIT_FAILURE;
836     }
837
838     if (strcmp (argv[0], "get") == 0) {
839         if (argc != 2) {
840             fprintf (stderr, "Error: notmuch config get requires exactly "
841                      "one argument.\n");
842             return EXIT_FAILURE;
843         }
844         ret = notmuch_config_command_get (config, argv[1]);
845     } else if (strcmp (argv[0], "set") == 0) {
846         if (argc < 2) {
847             fprintf (stderr, "Error: notmuch config set requires at least "
848                      "one argument.\n");
849             return EXIT_FAILURE;
850         }
851         ret = notmuch_config_command_set (config, argv[1], argc - 2, argv + 2);
852     } else if (strcmp (argv[0], "list") == 0) {
853         ret = notmuch_config_command_list (config);
854     } else {
855         fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
856                  argv[0]);
857         return EXIT_FAILURE;
858     }
859
860     return ret ? EXIT_FAILURE : EXIT_SUCCESS;
861
862 }
863
864 notmuch_bool_t
865 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
866 {
867     return config->maildir_synchronize_flags;
868 }
869
870 void
871 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
872                                               notmuch_bool_t synchronize_flags)
873 {
874     g_key_file_set_boolean (config->key_file,
875                             "maildir", "synchronize_flags", synchronize_flags);
876     config->maildir_synchronize_flags = synchronize_flags;
877 }