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