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