]> git.notmuchmail.org Git - notmuch/blob - lib/open.cc
lib: autocommit after some number of completed transactions
[notmuch] / lib / open.cc
1 #include <unistd.h>
2 #include <libgen.h>
3
4 #include "database-private.h"
5 #include "parse-time-vrp.h"
6 #include "path-util.h"
7
8 #if HAVE_XAPIAN_DB_RETRY_LOCK
9 #define DB_ACTION (Xapian::DB_CREATE_OR_OPEN | Xapian::DB_RETRY_LOCK)
10 #else
11 #define DB_ACTION Xapian::DB_CREATE_OR_OPEN
12 #endif
13
14 notmuch_status_t
15 notmuch_database_open (const char *path,
16                        notmuch_database_mode_t mode,
17                        notmuch_database_t **database)
18 {
19     char *status_string = NULL;
20     notmuch_status_t status;
21
22     status = notmuch_database_open_verbose (path, mode, database,
23                                             &status_string);
24
25     if (status_string) {
26         fputs (status_string, stderr);
27         free (status_string);
28     }
29
30     return status;
31 }
32
33 notmuch_status_t
34 notmuch_database_open_verbose (const char *path,
35                                notmuch_database_mode_t mode,
36                                notmuch_database_t **database,
37                                char **status_string)
38 {
39     return notmuch_database_open_with_config (path, mode, "", NULL,
40                                               database, status_string);
41 }
42
43 static const char *
44 _xdg_dir (void *ctx,
45           const char *xdg_root_variable,
46           const char *xdg_prefix,
47           const char *profile_name)
48 {
49     const char *xdg_root = getenv (xdg_root_variable);
50
51     if (! xdg_root) {
52         const char *home = getenv ("HOME");
53
54         if (! home) return NULL;
55
56         xdg_root = talloc_asprintf (ctx,
57                                     "%s/%s",
58                                     home,
59                                     xdg_prefix);
60     }
61
62     if (! profile_name)
63         profile_name = getenv ("NOTMUCH_PROFILE");
64
65     if (! profile_name)
66         profile_name = "default";
67
68     return talloc_asprintf (ctx,
69                             "%s/notmuch/%s",
70                             xdg_root,
71                             profile_name);
72 }
73
74 static notmuch_status_t
75 _choose_dir (notmuch_database_t *notmuch,
76              const char *profile,
77              notmuch_config_key_t key,
78              const char *xdg_var,
79              const char *xdg_subdir,
80              const char *subdir,
81              char **message = NULL)
82 {
83     const char *parent;
84     const char *dir;
85     struct stat st;
86     int err;
87
88     dir = notmuch_config_get (notmuch, key);
89
90     if (dir)
91         return NOTMUCH_STATUS_SUCCESS;
92
93     parent = _xdg_dir (notmuch, xdg_var, xdg_subdir, profile);
94     if (! parent)
95         return NOTMUCH_STATUS_PATH_ERROR;
96
97     dir = talloc_asprintf (notmuch, "%s/%s", parent, subdir);
98
99     err = stat (dir, &st);
100     if (err) {
101         if (errno == ENOENT) {
102             char *notmuch_path = dirname (talloc_strdup (notmuch, notmuch->xapian_path));
103             dir = talloc_asprintf (notmuch, "%s/%s", notmuch_path, subdir);
104         } else {
105             IGNORE_RESULT (asprintf (message, "Error: Cannot stat %s: %s.\n",
106                                      dir, strerror (errno)));
107             return NOTMUCH_STATUS_FILE_ERROR;
108         }
109     }
110
111     _notmuch_config_cache (notmuch, key, dir);
112
113     return NOTMUCH_STATUS_SUCCESS;
114 }
115
116 static notmuch_status_t
117 _load_key_file (notmuch_database_t *notmuch,
118                 const char *path,
119                 const char *profile,
120                 GKeyFile **key_file)
121 {
122     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
123
124     if (path && EMPTY_STRING (path))
125         goto DONE;
126
127     if (! path)
128         path = getenv ("NOTMUCH_CONFIG");
129
130     if (path)
131         path = talloc_strdup (notmuch, path);
132     else {
133         const char *dir = _xdg_dir (notmuch, "XDG_CONFIG_HOME", ".config", profile);
134
135         if (dir) {
136             path = talloc_asprintf (notmuch, "%s/config", dir);
137             if (access (path, R_OK) != 0)
138                 path = NULL;
139         }
140     }
141
142     if (! path) {
143         const char *home = getenv ("HOME");
144
145         path = talloc_asprintf (notmuch, "%s/.notmuch-config", home);
146
147         if (! profile)
148             profile = getenv ("NOTMUCH_PROFILE");
149
150         if (profile)
151             path = talloc_asprintf (notmuch, "%s.%s", path, profile);
152     }
153
154     *key_file = g_key_file_new ();
155     if (! g_key_file_load_from_file (*key_file, path, G_KEY_FILE_NONE, NULL)) {
156         status = NOTMUCH_STATUS_NO_CONFIG;
157     }
158
159   DONE:
160     if (path)
161         notmuch->config_path = path;
162
163     return status;
164 }
165
166 static notmuch_status_t
167 _db_dir_exists (const char *database_path, char **message)
168 {
169     struct stat st;
170     int err;
171
172     err = stat (database_path, &st);
173     if (err) {
174         IGNORE_RESULT (asprintf (message, "Error: Cannot open database at %s: %s.\n",
175                                  database_path, strerror (errno)));
176         return NOTMUCH_STATUS_FILE_ERROR;
177     }
178
179     if (! S_ISDIR (st.st_mode)) {
180         IGNORE_RESULT (asprintf (message, "Error: Cannot open database at %s: "
181                                  "Not a directory.\n",
182                                  database_path));
183         return NOTMUCH_STATUS_FILE_ERROR;
184     }
185
186     return NOTMUCH_STATUS_SUCCESS;
187 }
188
189 static notmuch_status_t
190 _choose_database_path (void *ctx,
191                        const char *profile,
192                        GKeyFile *key_file,
193                        const char **database_path,
194                        bool *split,
195                        char **message)
196 {
197     if (! *database_path) {
198         *database_path = getenv ("NOTMUCH_DATABASE");
199     }
200
201     if (! *database_path && key_file) {
202         char *path = g_key_file_get_value (key_file, "database", "path", NULL);
203         if (path) {
204             if (path[0] == '/')
205                 *database_path = talloc_strdup (ctx, path);
206             else
207                 *database_path = talloc_asprintf (ctx, "%s/%s", getenv ("HOME"), path);
208             g_free (path);
209         }
210     }
211     if (! *database_path) {
212         notmuch_status_t status;
213
214         *database_path = _xdg_dir (ctx, "XDG_DATA_HOME", ".local/share", profile);
215         status = _db_dir_exists (*database_path, message);
216         if (status) {
217             *database_path = NULL;
218         } else {
219             *split = true;
220         }
221     }
222
223     if (! *database_path) {
224         notmuch_status_t status;
225
226         *database_path = talloc_asprintf (ctx, "%s/mail", getenv ("HOME"));
227         status = _db_dir_exists (*database_path, message);
228         if (status) {
229             *database_path = NULL;
230         }
231     }
232
233     if (*database_path == NULL) {
234         *message = strdup ("Error: could not locate database.\n");
235         return NOTMUCH_STATUS_NO_DATABASE;
236     }
237
238     if (*database_path[0] != '/') {
239         *message = strdup ("Error: Database path must be absolute.\n");
240         return NOTMUCH_STATUS_PATH_ERROR;
241     }
242     return NOTMUCH_STATUS_SUCCESS;
243 }
244
245 static notmuch_database_t *
246 _alloc_notmuch ()
247 {
248     notmuch_database_t *notmuch;
249
250     notmuch = talloc_zero (NULL, notmuch_database_t);
251     if (! notmuch)
252         return NULL;
253
254     notmuch->exception_reported = false;
255     notmuch->status_string = NULL;
256     notmuch->writable_xapian_db = NULL;
257     notmuch->config_path = NULL;
258     notmuch->atomic_nesting = 0;
259     notmuch->transaction_count = 0;
260     notmuch->transaction_threshold = 0;
261     notmuch->view = 1;
262     return notmuch;
263 }
264
265 static notmuch_status_t
266 _trial_open (const char *xapian_path, char **message_ptr)
267 {
268     try {
269         Xapian::Database db (xapian_path);
270     } catch (const Xapian::DatabaseOpeningError &error) {
271         IGNORE_RESULT (asprintf (message_ptr,
272                                  "Cannot open Xapian database at %s: %s\n",
273                                  xapian_path,
274                                  error.get_msg ().c_str ()));
275         return NOTMUCH_STATUS_PATH_ERROR;
276     } catch (const Xapian::Error &error) {
277         IGNORE_RESULT (asprintf (message_ptr,
278                                  "A Xapian exception occurred opening database: %s\n",
279                                  error.get_msg ().c_str ()));
280         return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
281     }
282     return NOTMUCH_STATUS_SUCCESS;
283 }
284
285 notmuch_status_t
286 _notmuch_choose_xapian_path (void *ctx, const char *database_path,
287                              const char **xapian_path, char **message_ptr)
288 {
289     notmuch_status_t status;
290     const char *trial_path, *notmuch_path;
291
292     status = _db_dir_exists (database_path, message_ptr);
293     if (status)
294         goto DONE;
295
296     trial_path = talloc_asprintf (ctx, "%s/xapian", database_path);
297     status = _trial_open (trial_path, message_ptr);
298     if (status != NOTMUCH_STATUS_PATH_ERROR)
299         goto DONE;
300
301     if (*message_ptr)
302         free (*message_ptr);
303
304     notmuch_path = talloc_asprintf (ctx, "%s/.notmuch", database_path);
305     status = _db_dir_exists (notmuch_path, message_ptr);
306     if (status)
307         goto DONE;
308
309     trial_path = talloc_asprintf (ctx, "%s/xapian", notmuch_path);
310     status = _trial_open (trial_path, message_ptr);
311
312   DONE:
313     if (status == NOTMUCH_STATUS_SUCCESS)
314         *xapian_path = trial_path;
315     return status;
316 }
317
318 static void
319 _set_database_path (notmuch_database_t *notmuch,
320                     const char *database_path)
321 {
322     char *path = talloc_strdup (notmuch, database_path);
323
324     strip_trailing (path, '/');
325
326     _notmuch_config_cache (notmuch, NOTMUCH_CONFIG_DATABASE_PATH, path);
327 }
328
329 static void
330 _load_database_state (notmuch_database_t *notmuch)
331 {
332     std::string last_thread_id;
333     std::string last_mod;
334
335     notmuch->last_doc_id = notmuch->xapian_db->get_lastdocid ();
336     last_thread_id = notmuch->xapian_db->get_metadata ("last_thread_id");
337     if (last_thread_id.empty ()) {
338         notmuch->last_thread_id = 0;
339     } else {
340         const char *str;
341         char *end;
342
343         str = last_thread_id.c_str ();
344         notmuch->last_thread_id = strtoull (str, &end, 16);
345         if (*end != '\0')
346             INTERNAL_ERROR ("Malformed database last_thread_id: %s", str);
347     }
348
349     /* Get current highest revision number. */
350     last_mod = notmuch->xapian_db->get_value_upper_bound (
351         NOTMUCH_VALUE_LAST_MOD);
352     if (last_mod.empty ())
353         notmuch->revision = 0;
354     else
355         notmuch->revision = Xapian::sortable_unserialise (last_mod);
356     notmuch->uuid = talloc_strdup (
357         notmuch, notmuch->xapian_db->get_uuid ().c_str ());
358 }
359
360 static notmuch_status_t
361 _finish_open (notmuch_database_t *notmuch,
362               const char *profile,
363               notmuch_database_mode_t mode,
364               GKeyFile *key_file,
365               char **message_ptr)
366 {
367     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
368     char *incompat_features;
369     char *message = NULL;
370     const char *autocommit_str;
371     char *autocommit_end;
372     unsigned int version;
373     const char *database_path = notmuch_database_get_path (notmuch);
374
375     try {
376
377         if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
378             notmuch->writable_xapian_db = new Xapian::WritableDatabase (notmuch->xapian_path,
379                                                                         DB_ACTION);
380             notmuch->xapian_db = notmuch->writable_xapian_db;
381         } else {
382             notmuch->xapian_db = new Xapian::Database (notmuch->xapian_path);
383         }
384
385         /* Check version.  As of database version 3, we represent
386          * changes in terms of features, so assume a version bump
387          * means a dramatically incompatible change. */
388         version = notmuch_database_get_version (notmuch);
389         if (version > NOTMUCH_DATABASE_VERSION) {
390             IGNORE_RESULT (asprintf (&message,
391                                      "Error: Notmuch database at %s\n"
392                                      "       has a newer database format version (%u) than supported by this\n"
393                                      "       version of notmuch (%u).\n",
394                                      database_path, version, NOTMUCH_DATABASE_VERSION));
395             notmuch_database_destroy (notmuch);
396             notmuch = NULL;
397             status = NOTMUCH_STATUS_FILE_ERROR;
398             goto DONE;
399         }
400
401         /* Check features. */
402         incompat_features = NULL;
403         notmuch->features = _notmuch_database_parse_features (
404             notmuch, notmuch->xapian_db->get_metadata ("features").c_str (),
405             version, mode == NOTMUCH_DATABASE_MODE_READ_WRITE ? 'w' : 'r',
406             &incompat_features);
407         if (incompat_features) {
408             IGNORE_RESULT (asprintf (&message,
409                                      "Error: Notmuch database at %s\n"
410                                      "       requires features (%s)\n"
411                                      "       not supported by this version of notmuch.\n",
412                                      database_path, incompat_features));
413             notmuch_database_destroy (notmuch);
414             notmuch = NULL;
415             status = NOTMUCH_STATUS_FILE_ERROR;
416             goto DONE;
417         }
418
419         _load_database_state (notmuch);
420
421         notmuch->query_parser = new Xapian::QueryParser;
422         notmuch->term_gen = new Xapian::TermGenerator;
423         notmuch->term_gen->set_stemmer (Xapian::Stem ("english"));
424         notmuch->value_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
425         notmuch->date_range_processor = new ParseTimeRangeProcessor (NOTMUCH_VALUE_TIMESTAMP,
426                                                                      "date:");
427         notmuch->last_mod_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_LAST_MOD,
428                                                                               "lastmod:");
429         notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
430         notmuch->query_parser->set_database (*notmuch->xapian_db);
431         notmuch->query_parser->set_stemmer (Xapian::Stem ("english"));
432         notmuch->query_parser->set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
433         notmuch->query_parser->add_rangeprocessor (notmuch->value_range_processor);
434         notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
435         notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor);
436
437         /* Configuration information is needed to set up query parser */
438         status = _notmuch_config_load_from_database (notmuch);
439         if (status)
440             goto DONE;
441
442         if (key_file)
443             status = _notmuch_config_load_from_file (notmuch, key_file);
444         if (status)
445             goto DONE;
446
447         status = _choose_dir (notmuch, profile,
448                               NOTMUCH_CONFIG_HOOK_DIR,
449                               "XDG_CONFIG_HOME",
450                               ".config",
451                               "hooks",
452                               &message);
453         if (status)
454             goto DONE;
455
456         status = _choose_dir (notmuch, profile,
457                               NOTMUCH_CONFIG_BACKUP_DIR,
458                               "XDG_DATA_HOME",
459                               ".local/share",
460                               "backups",
461                               &message);
462         if (status)
463             goto DONE;
464         status = _notmuch_config_load_defaults (notmuch);
465         if (status)
466             goto DONE;
467
468         autocommit_str = notmuch_config_get (notmuch, NOTMUCH_CONFIG_AUTOCOMMIT);
469         if (unlikely (! autocommit_str)) {
470             INTERNAL_ERROR ("missing configuration for autocommit");
471         }
472         notmuch->transaction_threshold = strtoul (autocommit_str, &autocommit_end, 10);
473         if (*autocommit_end != '\0')
474             INTERNAL_ERROR ("Malformed database database.autocommit value: %s", autocommit_str);
475
476         status = _notmuch_database_setup_standard_query_fields (notmuch);
477         if (status)
478             goto DONE;
479
480         status = _notmuch_database_setup_user_query_fields (notmuch);
481         if (status)
482             goto DONE;
483
484     } catch (const Xapian::Error &error) {
485         IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
486                                  error.get_msg ().c_str ()));
487         notmuch_database_destroy (notmuch);
488         notmuch = NULL;
489         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
490     }
491   DONE:
492     if (message_ptr)
493         *message_ptr = message;
494     return status;
495 }
496
497 notmuch_status_t
498 notmuch_database_open_with_config (const char *database_path,
499                                    notmuch_database_mode_t mode,
500                                    const char *config_path,
501                                    const char *profile,
502                                    notmuch_database_t **database,
503                                    char **status_string)
504 {
505     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
506     void *local = talloc_new (NULL);
507     notmuch_database_t *notmuch = NULL;
508     char *message = NULL;
509     GKeyFile *key_file = NULL;
510     bool split = false;
511
512     _notmuch_init ();
513
514     notmuch = _alloc_notmuch ();
515     if (! notmuch) {
516         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
517         goto DONE;
518     }
519
520     status = _load_key_file (notmuch, config_path, profile, &key_file);
521     if (status) {
522         message = strdup ("Error: cannot load config file.\n");
523         goto DONE;
524     }
525
526     if ((status = _choose_database_path (local, profile, key_file,
527                                          &database_path, &split,
528                                          &message)))
529         goto DONE;
530
531     status = _db_dir_exists (database_path, &message);
532     if (status)
533         goto DONE;
534
535     _set_database_path (notmuch, database_path);
536
537     status = _notmuch_choose_xapian_path (notmuch, database_path,
538                                           &notmuch->xapian_path, &message);
539     if (status)
540         goto DONE;
541
542     status = _finish_open (notmuch, profile, mode, key_file, &message);
543
544   DONE:
545     talloc_free (local);
546
547     if (key_file)
548         g_key_file_free (key_file);
549
550     if (message) {
551         if (status_string)
552             *status_string = message;
553         else
554             free (message);
555     }
556
557     if (database)
558         *database = notmuch;
559     else
560         talloc_free (notmuch);
561
562     if (notmuch)
563         notmuch->open = true;
564
565     return status;
566 }
567
568 notmuch_status_t
569 notmuch_database_create (const char *path, notmuch_database_t **database)
570 {
571     char *status_string = NULL;
572     notmuch_status_t status;
573
574     status = notmuch_database_create_verbose (path, database,
575                                               &status_string);
576
577     if (status_string) {
578         fputs (status_string, stderr);
579         free (status_string);
580     }
581
582     return status;
583 }
584
585 notmuch_status_t
586 notmuch_database_create_verbose (const char *path,
587                                  notmuch_database_t **database,
588                                  char **status_string)
589 {
590     return notmuch_database_create_with_config (path, "", NULL, database, status_string);
591 }
592
593 notmuch_status_t
594 notmuch_database_create_with_config (const char *database_path,
595                                      const char *config_path,
596                                      const char *profile,
597                                      notmuch_database_t **database,
598                                      char **status_string)
599 {
600     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
601     notmuch_database_t *notmuch = NULL;
602     const char *notmuch_path = NULL;
603     char *message = NULL;
604     GKeyFile *key_file = NULL;
605     void *local = talloc_new (NULL);
606     int err;
607     bool split = false;
608
609     _notmuch_init ();
610
611     notmuch = _alloc_notmuch ();
612     if (! notmuch) {
613         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
614         goto DONE;
615     }
616
617     status = _load_key_file (notmuch, config_path, profile, &key_file);
618     if (status) {
619         message = strdup ("Error: cannot load config file.\n");
620         goto DONE;
621     }
622
623     if ((status = _choose_database_path (local, profile, key_file,
624                                          &database_path, &split, &message)))
625         goto DONE;
626
627     status = _db_dir_exists (database_path, &message);
628     if (status)
629         goto DONE;
630
631     _set_database_path (notmuch, database_path);
632
633     if (key_file && ! split) {
634         char *mail_root = notmuch_canonicalize_file_name (
635             g_key_file_get_value (key_file, "database", "mail_root", NULL));
636         char *db_path = notmuch_canonicalize_file_name (database_path);
637
638         split = (mail_root && (0 != strcmp (mail_root, db_path)));
639
640         free (mail_root);
641         free (db_path);
642     }
643
644     if (split) {
645         notmuch_path = database_path;
646     } else {
647         if (! (notmuch_path = talloc_asprintf (local, "%s/%s", database_path, ".notmuch"))) {
648             status = NOTMUCH_STATUS_OUT_OF_MEMORY;
649             goto DONE;
650         }
651
652         err = mkdir (notmuch_path, 0755);
653         if (err) {
654             if (errno == EEXIST) {
655                 status = NOTMUCH_STATUS_DATABASE_EXISTS;
656                 talloc_free (notmuch);
657                 notmuch = NULL;
658             } else {
659                 IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n",
660                                          notmuch_path, strerror (errno)));
661                 status = NOTMUCH_STATUS_FILE_ERROR;
662             }
663             goto DONE;
664         }
665     }
666
667     if (! (notmuch->xapian_path = talloc_asprintf (notmuch, "%s/%s", notmuch_path, "xapian"))) {
668         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
669         goto DONE;
670     }
671
672     status = _trial_open (notmuch->xapian_path, &message);
673     if (status == NOTMUCH_STATUS_SUCCESS) {
674         notmuch_database_destroy (notmuch);
675         notmuch = NULL;
676         status = NOTMUCH_STATUS_DATABASE_EXISTS;
677         goto DONE;
678     }
679
680     if (message)
681         free (message);
682
683     status = _finish_open (notmuch,
684                            profile,
685                            NOTMUCH_DATABASE_MODE_READ_WRITE,
686                            key_file,
687                            &message);
688     if (status)
689         goto DONE;
690
691     /* Upgrade doesn't add these feature to existing databases, but
692      * new databases have them. */
693     notmuch->features |= NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES;
694     notmuch->features |= NOTMUCH_FEATURE_INDEXED_MIMETYPES;
695     notmuch->features |= NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY;
696
697     status = notmuch_database_upgrade (notmuch, NULL, NULL);
698     if (status) {
699         notmuch_database_close (notmuch);
700         notmuch = NULL;
701     }
702
703   DONE:
704     talloc_free (local);
705
706     if (key_file)
707         g_key_file_free (key_file);
708
709     if (message) {
710         if (status_string)
711             *status_string = message;
712         else
713             free (message);
714     }
715     if (database)
716         *database = notmuch;
717     else
718         talloc_free (notmuch);
719     return status;
720 }
721
722 notmuch_status_t
723 notmuch_database_reopen (notmuch_database_t *notmuch,
724                          notmuch_database_mode_t new_mode)
725 {
726     notmuch_database_mode_t cur_mode = _notmuch_database_mode (notmuch);
727
728     if (notmuch->xapian_db == NULL) {
729         _notmuch_database_log (notmuch, "Cannot reopen closed or nonexistent database\n");
730         return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
731     }
732
733     try {
734         if (cur_mode == new_mode &&
735             new_mode == NOTMUCH_DATABASE_MODE_READ_ONLY) {
736             notmuch->xapian_db->reopen ();
737         } else {
738             notmuch->xapian_db->close ();
739
740             delete notmuch->xapian_db;
741             notmuch->xapian_db = NULL;
742             /* no need to free the same object twice */
743             notmuch->writable_xapian_db = NULL;
744
745             if (new_mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
746                 notmuch->writable_xapian_db = new Xapian::WritableDatabase (notmuch->xapian_path,
747                                                                             DB_ACTION);
748                 notmuch->xapian_db = notmuch->writable_xapian_db;
749             } else {
750                 notmuch->xapian_db = new Xapian::Database (notmuch->xapian_path,
751                                                            DB_ACTION);
752             }
753         }
754
755         _load_database_state (notmuch);
756     } catch (const Xapian::Error &error) {
757         if (! notmuch->exception_reported) {
758             _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n",
759                                    error.get_msg ().c_str ());
760             notmuch->exception_reported = true;
761         }
762         return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
763     }
764
765     notmuch->view++;
766     notmuch->open = true;
767     return NOTMUCH_STATUS_SUCCESS;
768 }
769
770 static notmuch_status_t
771 _maybe_load_config_from_database (notmuch_database_t *notmuch,
772                                   GKeyFile *key_file,
773                                   const char *database_path,
774                                   const char *profile)
775 {
776     char *message; /* ignored */
777
778     if (_db_dir_exists (database_path, &message))
779         return NOTMUCH_STATUS_NO_DATABASE;
780
781     _set_database_path (notmuch, database_path);
782
783     if (_notmuch_choose_xapian_path (notmuch, database_path, &notmuch->xapian_path, &message))
784         return NOTMUCH_STATUS_NO_DATABASE;
785
786     (void) _finish_open (notmuch, profile, NOTMUCH_DATABASE_MODE_READ_ONLY, key_file, &message);
787
788     return NOTMUCH_STATUS_SUCCESS;
789 }
790
791 notmuch_status_t
792 notmuch_database_load_config (const char *database_path,
793                               const char *config_path,
794                               const char *profile,
795                               notmuch_database_t **database,
796                               char **status_string)
797 {
798     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS, warning = NOTMUCH_STATUS_SUCCESS;
799     void *local = talloc_new (NULL);
800     notmuch_database_t *notmuch = NULL;
801     char *message = NULL;
802     GKeyFile *key_file = NULL;
803     bool split = false;
804
805     _notmuch_init ();
806
807     notmuch = _alloc_notmuch ();
808     if (! notmuch) {
809         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
810         goto DONE;
811     }
812
813     status = _load_key_file (notmuch, config_path, profile, &key_file);
814     switch (status) {
815     case NOTMUCH_STATUS_SUCCESS:
816         break;
817     case NOTMUCH_STATUS_NO_CONFIG:
818         warning = status;
819         break;
820     default:
821         message = strdup ("Error: cannot load config file.\n");
822         goto DONE;
823     }
824
825     status = _choose_database_path (local, profile, key_file,
826                                     &database_path, &split, &message);
827     switch (status) {
828     case NOTMUCH_STATUS_NO_DATABASE:
829     case NOTMUCH_STATUS_SUCCESS:
830         if (! warning)
831             warning = status;
832         break;
833     default:
834         goto DONE;
835     }
836
837
838     if (database_path) {
839         status = _maybe_load_config_from_database (notmuch, key_file, database_path, profile);
840         switch (status) {
841         case NOTMUCH_STATUS_NO_DATABASE:
842         case NOTMUCH_STATUS_SUCCESS:
843             if (! warning)
844                 warning = status;
845             break;
846         default:
847             goto DONE;
848         }
849     }
850
851     if (key_file) {
852         status = _notmuch_config_load_from_file (notmuch, key_file);
853         if (status)
854             goto DONE;
855     }
856     status = _notmuch_config_load_defaults (notmuch);
857     if (status)
858         goto DONE;
859
860   DONE:
861     talloc_free (local);
862
863     if (status_string)
864         *status_string = message;
865
866     if (database)
867         *database = notmuch;
868
869     if (status)
870         return status;
871     else
872         return warning;
873 }