]> git.notmuchmail.org Git - notmuch/blob - notmuch-config.c
config: Better formatting for search section comment
[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     "\tauto_exclude_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 **auto_exclude_tags;
110     size_t auto_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->auto_exclude_tags = NULL;
269     config->auto_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_auto_exclude_tags (config, &tmp) == NULL) {
365         const char *tags[] = { "deleted", "spam" };
366         notmuch_config_set_auto_exclude_tags (config, tags, 2);
367     }
368
369     error = NULL;
370     config->maildir_synchronize_flags =
371         g_key_file_get_boolean (config->key_file,
372                                 "maildir", "synchronize_flags", &error);
373     if (error) {
374         notmuch_config_set_maildir_synchronize_flags (config, TRUE);
375         g_error_free (error);
376     }
377
378     /* Whenever we know of configuration sections that don't appear in
379      * the configuration file, we add some comments to help the user
380      * understand what can be done. */
381     if (is_new)
382     {
383         g_key_file_set_comment (config->key_file, NULL, NULL,
384                                 toplevel_config_comment, NULL);
385     }
386
387     if (! file_had_database_group)
388     {
389         g_key_file_set_comment (config->key_file, "database", NULL,
390                                 database_config_comment, NULL);
391     }
392
393     if (! file_had_new_group)
394     {
395         g_key_file_set_comment (config->key_file, "new", NULL,
396                                 new_config_comment, NULL);
397     }
398
399     if (! file_had_user_group)
400     {
401         g_key_file_set_comment (config->key_file, "user", NULL,
402                                 user_config_comment, NULL);
403     }
404
405     if (! file_had_maildir_group)
406     {
407         g_key_file_set_comment (config->key_file, "maildir", NULL,
408                                 maildir_config_comment, NULL);
409     }
410
411     if (! file_had_search_group) {
412         g_key_file_set_comment (config->key_file, "search", NULL,
413                                 search_config_comment, NULL);
414     }
415
416     if (is_new_ret)
417         *is_new_ret = is_new;
418
419     return config;
420 }
421
422 /* Close the given notmuch_config_t object, freeing all resources.
423  * 
424  * Note: Any changes made to the configuration are *not* saved by this
425  * function. To save changes, call notmuch_config_save before
426  * notmuch_config_close.
427 */
428 void
429 notmuch_config_close (notmuch_config_t *config)
430 {
431     talloc_free (config);
432 }
433
434 /* Save any changes made to the notmuch configuration.
435  *
436  * Any comments originally in the file will be preserved.
437  *
438  * Returns 0 if successful, and 1 in case of any error, (after
439  * printing a description of the error to stderr).
440  */
441 int
442 notmuch_config_save (notmuch_config_t *config)
443 {
444     size_t length;
445     char *data;
446     GError *error = NULL;
447
448     data = g_key_file_to_data (config->key_file, &length, NULL);
449     if (data == NULL) {
450         fprintf (stderr, "Out of memory.\n");
451         return 1;
452     }
453
454     if (! g_file_set_contents (config->filename, data, length, &error)) {
455         fprintf (stderr, "Error saving configuration to %s: %s\n",
456                  config->filename, error->message);
457         g_error_free (error);
458         g_free (data);
459         return 1;
460     }
461
462     g_free (data);
463     return 0;
464 }
465
466 const char *
467 notmuch_config_get_database_path (notmuch_config_t *config)
468 {
469     char *path;
470
471     if (config->database_path == NULL) {
472         path = g_key_file_get_string (config->key_file,
473                                       "database", "path", NULL);
474         if (path) {
475             config->database_path = talloc_strdup (config, path);
476             free (path);
477         }
478     }
479
480     return config->database_path;
481 }
482
483 void
484 notmuch_config_set_database_path (notmuch_config_t *config,
485                                   const char *database_path)
486 {
487     g_key_file_set_string (config->key_file,
488                            "database", "path", database_path);
489
490     talloc_free (config->database_path);
491     config->database_path = NULL;
492 }
493
494 const char *
495 notmuch_config_get_user_name (notmuch_config_t *config)
496 {
497     char *name;
498
499     if (config->user_name == NULL) {
500         name = g_key_file_get_string (config->key_file,
501                                       "user", "name", NULL);
502         if (name) {
503             config->user_name = talloc_strdup (config, name);
504             free (name);
505         }
506     }
507
508     return config->user_name;
509 }
510
511 void
512 notmuch_config_set_user_name (notmuch_config_t *config,
513                               const char *user_name)
514 {
515     g_key_file_set_string (config->key_file,
516                            "user", "name", user_name);
517
518     talloc_free (config->user_name);
519     config->user_name = NULL;
520 }
521
522 const char *
523 notmuch_config_get_user_primary_email (notmuch_config_t *config)
524 {
525     char *email;
526
527     if (config->user_primary_email == NULL) {
528         email = g_key_file_get_string (config->key_file,
529                                        "user", "primary_email", NULL);
530         if (email) {
531             config->user_primary_email = talloc_strdup (config, email);
532             free (email);
533         }
534     }
535
536     return config->user_primary_email;
537 }
538
539 void
540 notmuch_config_set_user_primary_email (notmuch_config_t *config,
541                                        const char *primary_email)
542 {
543     g_key_file_set_string (config->key_file,
544                            "user", "primary_email", primary_email);
545
546     talloc_free (config->user_primary_email);
547     config->user_primary_email = NULL;
548 }
549
550 static const char **
551 _config_get_list (notmuch_config_t *config,
552                   const char *section, const char *key,
553                   const char ***outlist, size_t *list_length, size_t *ret_length)
554 {
555     assert(outlist);
556
557     if (*outlist == NULL) {
558
559         char **inlist = g_key_file_get_string_list (config->key_file,
560                                              section, key, list_length, NULL);
561         if (inlist) {
562             unsigned int i;
563
564             *outlist = talloc_size (config, sizeof (char *) * (*list_length + 1));
565
566             for (i = 0; i < *list_length; i++)
567                 (*outlist)[i] = talloc_strdup (*outlist, inlist[i]);
568
569             (*outlist)[i] = NULL;
570
571             g_strfreev (inlist);
572         }
573     }
574
575     if (ret_length)
576         *ret_length = *list_length;
577
578     return *outlist;
579 }
580
581 const char **
582 notmuch_config_get_user_other_email (notmuch_config_t *config,   size_t *length)
583 {
584     return _config_get_list (config, "user", "other_email",
585                              &(config->user_other_email),
586                              &(config->user_other_email_length), length);
587 }
588
589 const char **
590 notmuch_config_get_new_tags (notmuch_config_t *config,   size_t *length)
591 {
592     return _config_get_list (config, "new", "tags",
593                              &(config->new_tags),
594                              &(config->new_tags_length), length);
595 }
596
597 static void
598 _config_set_list (notmuch_config_t *config,
599                   const char *group, const char *name,
600                   const char *list[],
601                   size_t length, const char ***config_var )
602 {
603     g_key_file_set_string_list (config->key_file, group, name, list, length);
604     talloc_free (*config_var);
605     *config_var = NULL;
606 }
607
608 void
609 notmuch_config_set_user_other_email (notmuch_config_t *config,
610                                      const char *list[],
611                                      size_t length)
612 {
613     _config_set_list (config, "user", "other_email", list, length,
614                      &(config->user_other_email));
615 }
616
617 void
618 notmuch_config_set_new_tags (notmuch_config_t *config,
619                                      const char *list[],
620                                      size_t length)
621 {
622     _config_set_list (config, "new", "tags", list, length,
623                      &(config->new_tags));
624 }
625
626 const char **
627 notmuch_config_get_auto_exclude_tags (notmuch_config_t *config, size_t *length)
628 {
629     return _config_get_list (config, "search", "auto_exclude_tags",
630                              &(config->auto_exclude_tags),
631                              &(config->auto_exclude_tags_length), length);
632 }
633
634 void
635 notmuch_config_set_auto_exclude_tags (notmuch_config_t *config,
636                                       const char *list[],
637                                       size_t length)
638 {
639     _config_set_list (config, "search", "auto_exclude_tags", list, length,
640                       &(config->auto_exclude_tags));
641 }
642
643 /* Given a configuration item of the form <group>.<key> return the
644  * component group and key. If any error occurs, print a message on
645  * stderr and return 1. Otherwise, return 0.
646  *
647  * Note: This function modifies the original 'item' string.
648  */
649 static int
650 _item_split (char *item, char **group, char **key)
651 {
652     char *period;
653
654     *group = item;
655
656     period = index (item, '.');
657     if (period == NULL || *(period+1) == '\0') {
658         fprintf (stderr,
659                  "Invalid configuration name: %s\n"
660                  "(Should be of the form <section>.<item>)\n", item);
661         return 1;
662     }
663
664     *period = '\0';
665     *key = period + 1;
666
667     return 0;
668 }
669
670 static int
671 notmuch_config_command_get (void *ctx, char *item)
672 {
673     notmuch_config_t *config;
674
675     config = notmuch_config_open (ctx, NULL, NULL);
676     if (config == NULL)
677         return 1;
678
679     if (strcmp(item, "database.path") == 0) {
680         printf ("%s\n", notmuch_config_get_database_path (config));
681     } else if (strcmp(item, "user.name") == 0) {
682         printf ("%s\n", notmuch_config_get_user_name (config));
683     } else if (strcmp(item, "user.primary_email") == 0) {
684         printf ("%s\n", notmuch_config_get_user_primary_email (config));
685     } else if (strcmp(item, "user.other_email") == 0) {
686         const char **other_email;
687         size_t i, length;
688         
689         other_email = notmuch_config_get_user_other_email (config, &length);
690         for (i = 0; i < length; i++)
691             printf ("%s\n", other_email[i]);
692     } else if (strcmp(item, "new.tags") == 0) {
693         const char **tags;
694         size_t i, length;
695
696         tags = notmuch_config_get_new_tags (config, &length);
697         for (i = 0; i < length; i++)
698             printf ("%s\n", tags[i]);
699     } else {
700         char **value;
701         size_t i, length;
702         char *group, *key;
703
704         if (_item_split (item, &group, &key))
705             return 1;
706
707         value = g_key_file_get_string_list (config->key_file,
708                                             group, key,
709                                             &length, NULL);
710         if (value == NULL) {
711             fprintf (stderr, "Unknown configuration item: %s.%s\n",
712                      group, key);
713             return 1;
714         }
715
716         for (i = 0; i < length; i++)
717             printf ("%s\n", value[i]);
718
719         free (value);
720     }
721
722     notmuch_config_close (config);
723
724     return 0;
725 }
726
727 static int
728 notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
729 {
730     notmuch_config_t *config;
731     char *group, *key;
732     int ret;
733
734     if (_item_split (item, &group, &key))
735         return 1;
736
737     config = notmuch_config_open (ctx, NULL, NULL);
738     if (config == NULL)
739         return 1;
740
741     /* With only the name of an item, we clear it from the
742      * configuration file.
743      *
744      * With a single value, we set it as a string.
745      *
746      * With multiple values, we set them as a string list.
747      */
748     switch (argc) {
749     case 0:
750         g_key_file_remove_key (config->key_file, group, key, NULL);
751         break;
752     case 1:
753         g_key_file_set_string (config->key_file, group, key, argv[0]);
754         break;
755     default:
756         g_key_file_set_string_list (config->key_file, group, key,
757                                     (const gchar **) argv, argc);
758         break;
759     }
760
761     ret = notmuch_config_save (config);
762     notmuch_config_close (config);
763
764     return ret;
765 }
766
767 int
768 notmuch_config_command (void *ctx, int argc, char *argv[])
769 {
770     argc--; argv++; /* skip subcommand argument */
771
772     if (argc < 2) {
773         fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
774         return 1;
775     }
776
777     if (strcmp (argv[0], "get") == 0)
778         return notmuch_config_command_get (ctx, argv[1]);
779     else if (strcmp (argv[0], "set") == 0)
780         return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
781
782     fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
783              argv[0]);
784     return 1;
785 }
786
787 notmuch_bool_t
788 notmuch_config_get_maildir_synchronize_flags (notmuch_config_t *config)
789 {
790     return config->maildir_synchronize_flags;
791 }
792
793 void
794 notmuch_config_set_maildir_synchronize_flags (notmuch_config_t *config,
795                                               notmuch_bool_t synchronize_flags)
796 {
797     g_key_file_set_boolean (config->key_file,
798                             "maildir", "synchronize_flags", synchronize_flags);
799     config->maildir_synchronize_flags = synchronize_flags;
800 }