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