]> git.notmuchmail.org Git - notmuch/blob - notmuch-config.c
lib: Improve documentation of _notmuch_message_create_for_message_id
[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:          $MAILDIR, otherwise $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 = getenv ("MAILDIR");
327         if (! path)
328             path = talloc_asprintf (config, "%s/mail",
329                                     getenv ("HOME"));
330         notmuch_config_set_database_path (config, path);
331         talloc_free (path);
332     }
333
334     if (notmuch_config_get_user_name (config) == NULL) {
335         char *name = getenv ("NAME");
336         if (! name)
337             name = get_name_from_passwd_file (config);
338         notmuch_config_set_user_name (config, name);
339         talloc_free (name);
340     }
341
342     if (notmuch_config_get_user_primary_email (config) == NULL) {
343         char *email = getenv ("EMAIL");
344         if (email) {
345             notmuch_config_set_user_primary_email (config, email);
346         } else {
347             char hostname[256];
348             struct hostent *hostent;
349             const char *domainname;
350
351             char *username = get_username_from_passwd_file (config);
352
353             gethostname (hostname, 256);
354             hostname[255] = '\0';
355
356             hostent = gethostbyname (hostname);
357             if (hostent && (domainname = strchr (hostent->h_name, '.')))
358                 domainname += 1;
359             else
360                 domainname = "(none)";
361
362             email = talloc_asprintf (config, "%s@%s.%s",
363                                      username, hostname, domainname);
364
365             notmuch_config_set_user_primary_email (config, email);
366
367             talloc_free (username);
368             talloc_free (email);
369         }
370     }
371
372     if (notmuch_config_get_new_tags (config, &tmp) == NULL) {
373         const char *tags[] = { "unread", "inbox" };
374         notmuch_config_set_new_tags (config, tags, 2);
375     }
376
377     if (notmuch_config_get_new_ignore (config, &tmp) == NULL) {
378         notmuch_config_set_new_ignore (config, NULL, 0);
379     }
380
381     if (notmuch_config_get_search_exclude_tags (config, &tmp) == NULL) {
382         if (config->is_new) {
383             const char *tags[] = { "deleted", "spam" };
384             notmuch_config_set_search_exclude_tags (config, tags, 2);
385         } else {
386             notmuch_config_set_search_exclude_tags (config, NULL, 0);
387         }
388     }
389
390     error = NULL;
391     config->maildir_synchronize_flags =
392         g_key_file_get_boolean (config->key_file,
393                                 "maildir", "synchronize_flags", &error);
394     if (error) {
395         notmuch_config_set_maildir_synchronize_flags (config, TRUE);
396         g_error_free (error);
397     }
398
399     /* Whenever we know of configuration sections that don't appear in
400      * the configuration file, we add some comments to help the user
401      * understand what can be done. */
402     if (config->is_new)
403         g_key_file_set_comment (config->key_file, NULL, NULL,
404                                 toplevel_config_comment, NULL);
405
406     if (! file_had_database_group)
407         g_key_file_set_comment (config->key_file, "database", NULL,
408                                 database_config_comment, NULL);
409
410     if (! file_had_new_group)
411         g_key_file_set_comment (config->key_file, "new", NULL,
412                                 new_config_comment, NULL);
413
414     if (! file_had_user_group)
415         g_key_file_set_comment (config->key_file, "user", NULL,
416                                 user_config_comment, NULL);
417
418     if (! file_had_maildir_group)
419         g_key_file_set_comment (config->key_file, "maildir", NULL,
420                                 maildir_config_comment, NULL);
421
422     if (! file_had_search_group)
423         g_key_file_set_comment (config->key_file, "search", NULL,
424                                 search_config_comment, NULL);
425
426     return config;
427 }
428
429 /* Close the given notmuch_config_t object, freeing all resources.
430  * 
431  * Note: Any changes made to the configuration are *not* saved by this
432  * function. To save changes, call notmuch_config_save before
433  * notmuch_config_close.
434 */
435 void
436 notmuch_config_close (notmuch_config_t *config)
437 {
438     talloc_free (config);
439 }
440
441 /* Save any changes made to the notmuch configuration.
442  *
443  * Any comments originally in the file will be preserved.
444  *
445  * Returns 0 if successful, and 1 in case of any error, (after
446  * printing a description of the error to stderr).
447  */
448 int
449 notmuch_config_save (notmuch_config_t *config)
450 {
451     size_t length;
452     char *data, *filename;
453     GError *error = NULL;
454
455     data = g_key_file_to_data (config->key_file, &length, NULL);
456     if (data == NULL) {
457         fprintf (stderr, "Out of memory.\n");
458         return 1;
459     }
460
461     /* Try not to overwrite symlinks. */
462     filename = canonicalize_file_name (config->filename);
463     if (! filename) {
464         if (errno == ENOENT) {
465             filename = strdup (config->filename);
466             if (! filename) {
467                 fprintf (stderr, "Out of memory.\n");
468                 g_free (data);
469                 return 1;
470             }
471         } else {
472             fprintf (stderr, "Error canonicalizing %s: %s\n", config->filename,
473                      strerror (errno));
474             g_free (data);
475             return 1;
476         }
477     }
478
479     if (! g_file_set_contents (filename, data, length, &error)) {
480         if (strcmp (filename, config->filename) != 0) {
481             fprintf (stderr, "Error saving configuration to %s (-> %s): %s\n",
482                      config->filename, filename, error->message);
483         } else {
484             fprintf (stderr, "Error saving configuration to %s: %s\n",
485                      filename, error->message);
486         }
487         g_error_free (error);
488         free (filename);
489         g_free (data);
490         return 1;
491     }
492
493     free (filename);
494     g_free (data);
495     return 0;
496 }
497
498 notmuch_bool_t
499 notmuch_config_is_new (notmuch_config_t *config)
500 {
501     return config->is_new;
502 }
503
504 static const char *
505 _config_get (notmuch_config_t *config, char **field,
506              const char *group, const char *key)
507 {
508     /* read from config file and cache value, if not cached already */
509     if (*field == NULL) {
510         char *value;
511         value = g_key_file_get_string (config->key_file, group, key, NULL);
512         if (value) {
513             *field = talloc_strdup (config, value);
514             free (value);
515         }
516     }
517     return *field;
518 }
519
520 static void
521 _config_set (notmuch_config_t *config, char **field,
522              const char *group, const char *key, const char *value)
523 {
524     g_key_file_set_string (config->key_file, group, key, value);
525
526     /* drop the cached value */
527     talloc_free (*field);
528     *field = NULL;
529 }
530
531 static const char **
532 _config_get_list (notmuch_config_t *config,
533                   const char *section, const char *key,
534                   const char ***outlist, size_t *list_length, size_t *ret_length)
535 {
536     assert(outlist);
537
538     /* read from config file and cache value, if not cached already */
539     if (*outlist == NULL) {
540
541         char **inlist = g_key_file_get_string_list (config->key_file,
542                                              section, key, list_length, NULL);
543         if (inlist) {
544             unsigned int i;
545
546             *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
547
548             for (i = 0; i < *list_length; i++)
549                 (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
550
551             (*outlist)[i] = NULL;
552
553             g_strfreev (inlist);
554         }
555     }
556
557     if (ret_length)
558         *ret_length = *list_length;
559
560     return *outlist;
561 }
562
563 static void
564 _config_set_list (notmuch_config_t *config,
565                   const char *group, const char *name,
566                   const char *list[],
567                   size_t length, const char ***config_var )
568 {
569     g_key_file_set_string_list (config->key_file, group, name, list, length);
570
571     /* drop the cached value */
572     talloc_free (*config_var);
573     *config_var = NULL;
574 }
575
576 const char *
577 notmuch_config_get_database_path (notmuch_config_t *config)
578 {
579     return _config_get (config, &config->database_path, "database", "path");
580 }
581
582 void
583 notmuch_config_set_database_path (notmuch_config_t *config,
584                                   const char *database_path)
585 {
586     _config_set (config, &config->database_path, "database", "path", database_path);
587 }
588
589 const char *
590 notmuch_config_get_user_name (notmuch_config_t *config)
591 {
592     return _config_get (config, &config->user_name, "user", "name");
593 }
594
595 void
596 notmuch_config_set_user_name (notmuch_config_t *config,
597                               const char *user_name)
598 {
599     _config_set (config, &config->user_name, "user", "name", user_name);
600 }
601
602 const char *
603 notmuch_config_get_user_primary_email (notmuch_config_t *config)
604 {
605     return _config_get (config, &config->user_primary_email, "user", "primary_email");
606 }
607
608 void
609 notmuch_config_set_user_primary_email (notmuch_config_t *config,
610                                        const char *primary_email)
611 {
612     _config_set (config, &config->user_primary_email, "user", "primary_email", primary_email);
613 }
614
615 const char **
616 notmuch_config_get_user_other_email (notmuch_config_t *config,   size_t *length)
617 {
618     return _config_get_list (config, "user", "other_email",
619                              &(config->user_other_email),
620                              &(config->user_other_email_length), length);
621 }
622
623 const char **
624 notmuch_config_get_new_tags (notmuch_config_t *config,   size_t *length)
625 {
626     return _config_get_list (config, "new", "tags",
627                              &(config->new_tags),
628                              &(config->new_tags_length), length);
629 }
630
631 const char **
632 notmuch_config_get_new_ignore (notmuch_config_t *config, size_t *length)
633 {
634     return _config_get_list (config, "new", "ignore",
635                              &(config->new_ignore),
636                              &(config->new_ignore_length), length);
637 }
638
639 void
640 notmuch_config_set_user_other_email (notmuch_config_t *config,
641                                      const char *list[],
642                                      size_t length)
643 {
644     _config_set_list (config, "user", "other_email", list, length,
645                      &(config->user_other_email));
646 }
647
648 void
649 notmuch_config_set_new_tags (notmuch_config_t *config,
650                                      const char *list[],
651                                      size_t length)
652 {
653     _config_set_list (config, "new", "tags", list, length,
654                      &(config->new_tags));
655 }
656
657 void
658 notmuch_config_set_new_ignore (notmuch_config_t *config,
659                                const char *list[],
660                                size_t length)
661 {
662     _config_set_list (config, "new", "ignore", list, length,
663                      &(config->new_ignore));
664 }
665
666 const char **
667 notmuch_config_get_search_exclude_tags (notmuch_config_t *config, size_t *length)
668 {
669     return _config_get_list (config, "search", "exclude_tags",
670                              &(config->search_exclude_tags),
671                              &(config->search_exclude_tags_length), length);
672 }
673
674 void
675 notmuch_config_set_search_exclude_tags (notmuch_config_t *config,
676                                       const char *list[],
677                                       size_t length)
678 {
679     _config_set_list (config, "search", "exclude_tags", list, length,
680                       &(config->search_exclude_tags));
681 }
682
683 /* Given a configuration item of the form <group>.<key> return the
684  * component group and key. If any error occurs, print a message on
685  * stderr and return 1. Otherwise, return 0.
686  *
687  * Note: This function modifies the original 'item' string.
688  */
689 static int
690 _item_split (char *item, char **group, char **key)
691 {
692     char *period;
693
694     *group = item;
695
696     period = strchr (item, '.');
697     if (period == NULL || *(period+1) == '\0') {
698         fprintf (stderr,
699                  "Invalid configuration name: %s\n"
700                  "(Should be of the form <section>.<item>)\n", item);
701         return 1;
702     }
703
704     *period = '\0';
705     *key = period + 1;
706
707     return 0;
708 }
709
710 static int
711 notmuch_config_command_get (notmuch_config_t *config, char *item)
712 {
713     if (strcmp(item, "database.path") == 0) {
714         printf ("%s\n", notmuch_config_get_database_path (config));
715     } else if (strcmp(item, "user.name") == 0) {
716         printf ("%s\n", notmuch_config_get_user_name (config));
717     } else if (strcmp(item, "user.primary_email") == 0) {
718         printf ("%s\n", notmuch_config_get_user_primary_email (config));
719     } else if (strcmp(item, "user.other_email") == 0) {
720         const char **other_email;
721         size_t i, length;
722         
723         other_email = notmuch_config_get_user_other_email (config, &length);
724         for (i = 0; i < length; i++)
725             printf ("%s\n", other_email[i]);
726     } else if (strcmp(item, "new.tags") == 0) {
727         const char **tags;
728         size_t i, length;
729
730         tags = notmuch_config_get_new_tags (config, &length);
731         for (i = 0; i < length; i++)
732             printf ("%s\n", tags[i]);
733     } else {
734         char **value;
735         size_t i, length;
736         char *group, *key;
737
738         if (_item_split (item, &group, &key))
739             return 1;
740
741         value = g_key_file_get_string_list (config->key_file,
742                                             group, key,
743                                             &length, NULL);
744         if (value == NULL) {
745             fprintf (stderr, "Unknown configuration item: %s.%s\n",
746                      group, key);
747             return 1;
748         }
749
750         for (i = 0; i < length; i++)
751             printf ("%s\n", value[i]);
752
753         g_strfreev (value);
754     }
755
756     return 0;
757 }
758
759 static int
760 notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[])
761 {
762     char *group, *key;
763
764     if (_item_split (item, &group, &key))
765         return 1;
766
767     /* With only the name of an item, we clear it from the
768      * configuration file.
769      *
770      * With a single value, we set it as a string.
771      *
772      * With multiple values, we set them as a string list.
773      */
774     switch (argc) {
775     case 0:
776         g_key_file_remove_key (config->key_file, group, key, NULL);
777         break;
778     case 1:
779         g_key_file_set_string (config->key_file, group, key, argv[0]);
780         break;
781     default:
782         g_key_file_set_string_list (config->key_file, group, key,
783                                     (const gchar **) argv, argc);
784         break;
785     }
786
787     return notmuch_config_save (config);
788 }
789
790 static int
791 notmuch_config_command_list (notmuch_config_t *config)
792 {
793     char **groups;
794     size_t g, groups_length;
795
796     groups = g_key_file_get_groups (config->key_file, &groups_length);
797     if (groups == NULL)
798         return 1;
799
800     for (g = 0; g < groups_length; g++) {
801         char **keys;
802         size_t k, keys_length;
803
804         keys = g_key_file_get_keys (config->key_file,
805                                     groups[g], &keys_length, NULL);
806         if (keys == NULL)
807             continue;
808
809         for (k = 0; k < keys_length; k++) {
810             char *value;
811
812             value = g_key_file_get_string (config->key_file,
813                                            groups[g], keys[k], NULL);
814             if (value != NULL) {
815                 printf ("%s.%s=%s\n", groups[g], keys[k], value);
816                 free (value);
817             }
818         }
819
820         g_strfreev (keys);
821     }
822
823     g_strfreev (groups);
824
825     return 0;
826 }
827
828 int
829 notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
830 {
831     int ret;
832
833     argc--; argv++; /* skip subcommand argument */
834
835     if (argc < 1) {
836         fprintf (stderr, "Error: notmuch config requires at least one argument.\n");
837         return EXIT_FAILURE;
838     }
839
840     if (strcmp (argv[0], "get") == 0) {
841         if (argc != 2) {
842             fprintf (stderr, "Error: notmuch config get requires exactly "
843                      "one argument.\n");
844             return EXIT_FAILURE;
845         }
846         ret = notmuch_config_command_get (config, argv[1]);
847     } else if (strcmp (argv[0], "set") == 0) {
848         if (argc < 2) {
849             fprintf (stderr, "Error: notmuch config set requires at least "
850                      "one argument.\n");
851             return EXIT_FAILURE;
852         }
853         ret = notmuch_config_command_set (config, argv[1], argc - 2, argv + 2);
854     } else if (strcmp (argv[0], "list") == 0) {
855         ret = notmuch_config_command_list (config);
856     } else {
857         fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
858                  argv[0]);
859         return EXIT_FAILURE;
860     }
861
862     return ret ? EXIT_FAILURE : EXIT_SUCCESS;
863
864 }
865
866 notmuch_bool_t
867 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
868 {
869     return config->maildir_synchronize_flags;
870 }
871
872 void
873 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
874                                               notmuch_bool_t synchronize_flags)
875 {
876     g_key_file_set_boolean (config->key_file,
877                             "maildir", "synchronize_flags", synchronize_flags);
878     config->maildir_synchronize_flags = synchronize_flags;
879 }