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