]> git.notmuchmail.org Git - notmuch/blob - lib/open.cc
lib/open: look in MAILDIR for database, as documented.
[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         *database_path = getenv ("MAILDIR");
225     }
226
227     if (! *database_path) {
228         notmuch_status_t status;
229
230         *database_path = talloc_asprintf (ctx, "%s/mail", getenv ("HOME"));
231         status = _db_dir_exists (*database_path, message);
232         if (status) {
233             *database_path = NULL;
234         }
235     }
236
237     if (*database_path == NULL) {
238         *message = strdup ("Error: could not locate database.\n");
239         return NOTMUCH_STATUS_NO_DATABASE;
240     }
241
242     if (*database_path[0] != '/') {
243         *message = strdup ("Error: Database path must be absolute.\n");
244         return NOTMUCH_STATUS_PATH_ERROR;
245     }
246     return NOTMUCH_STATUS_SUCCESS;
247 }
248
249 notmuch_database_t *
250 _alloc_notmuch ()
251 {
252     notmuch_database_t *notmuch;
253
254     notmuch = talloc_zero (NULL, notmuch_database_t);
255     if (! notmuch)
256         return NULL;
257
258     notmuch->exception_reported = false;
259     notmuch->status_string = NULL;
260     notmuch->writable_xapian_db = NULL;
261     notmuch->config_path = NULL;
262     notmuch->atomic_nesting = 0;
263     notmuch->view = 1;
264     return notmuch;
265 }
266
267 static notmuch_status_t
268 _trial_open (const char *xapian_path, char **message_ptr)
269 {
270     try {
271         Xapian::Database db (xapian_path);
272     } catch (const Xapian::DatabaseOpeningError &error) {
273         IGNORE_RESULT (asprintf (message_ptr,
274                                  "Cannot open Xapian database at %s: %s\n",
275                                  xapian_path,
276                                  error.get_msg ().c_str ()));
277         return NOTMUCH_STATUS_PATH_ERROR;
278     } catch (const Xapian::Error &error) {
279         IGNORE_RESULT (asprintf (message_ptr,
280                                  "A Xapian exception occurred opening database: %s\n",
281                                  error.get_msg ().c_str ()));
282         return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
283     }
284     return NOTMUCH_STATUS_SUCCESS;
285 }
286
287 notmuch_status_t
288 _notmuch_choose_xapian_path (void *ctx, const char *database_path,
289                              const char **xapian_path, char **message_ptr)
290 {
291     notmuch_status_t status;
292     const char *trial_path, *notmuch_path;
293
294     status = _db_dir_exists (database_path, message_ptr);
295     if (status)
296         goto DONE;
297
298     trial_path = talloc_asprintf (ctx, "%s/xapian", database_path);
299     status = _trial_open (trial_path, message_ptr);
300     if (status != NOTMUCH_STATUS_PATH_ERROR)
301         goto DONE;
302
303     if (*message_ptr)
304         free (*message_ptr);
305
306     notmuch_path = talloc_asprintf (ctx, "%s/.notmuch", database_path);
307     status = _db_dir_exists (notmuch_path, message_ptr);
308     if (status)
309         goto DONE;
310
311     trial_path = talloc_asprintf (ctx, "%s/xapian", notmuch_path);
312     status = _trial_open (trial_path, message_ptr);
313
314   DONE:
315     if (status == NOTMUCH_STATUS_SUCCESS)
316         *xapian_path = trial_path;
317     return status;
318 }
319
320 static void
321 _set_database_path (notmuch_database_t *notmuch,
322                     const char *database_path)
323 {
324     char *path = talloc_strdup (notmuch, database_path);
325
326     strip_trailing (path, '/');
327
328     _notmuch_config_cache (notmuch, NOTMUCH_CONFIG_DATABASE_PATH, path);
329 }
330
331 static void
332 _init_libs ()
333 {
334
335     static int initialized = 0;
336
337     /* Initialize the GLib type system and threads */
338 #if ! GLIB_CHECK_VERSION (2, 35, 1)
339     g_type_init ();
340 #endif
341
342     /* Initialize gmime */
343     if (! initialized) {
344         g_mime_init ();
345         initialized = 1;
346     }
347 }
348
349 static void
350 _load_database_state (notmuch_database_t *notmuch)
351 {
352     std::string last_thread_id;
353     std::string last_mod;
354
355     notmuch->last_doc_id = notmuch->xapian_db->get_lastdocid ();
356     last_thread_id = notmuch->xapian_db->get_metadata ("last_thread_id");
357     if (last_thread_id.empty ()) {
358         notmuch->last_thread_id = 0;
359     } else {
360         const char *str;
361         char *end;
362
363         str = last_thread_id.c_str ();
364         notmuch->last_thread_id = strtoull (str, &end, 16);
365         if (*end != '\0')
366             INTERNAL_ERROR ("Malformed database last_thread_id: %s", str);
367     }
368
369     /* Get current highest revision number. */
370     last_mod = notmuch->xapian_db->get_value_upper_bound (
371         NOTMUCH_VALUE_LAST_MOD);
372     if (last_mod.empty ())
373         notmuch->revision = 0;
374     else
375         notmuch->revision = Xapian::sortable_unserialise (last_mod);
376     notmuch->uuid = talloc_strdup (
377         notmuch, notmuch->xapian_db->get_uuid ().c_str ());
378 }
379
380 static notmuch_status_t
381 _finish_open (notmuch_database_t *notmuch,
382               const char *profile,
383               notmuch_database_mode_t mode,
384               GKeyFile *key_file,
385               char **message_ptr)
386 {
387     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
388     char *incompat_features;
389     char *message = NULL;
390     unsigned int version;
391     const char *database_path = notmuch_database_get_path (notmuch);
392
393     try {
394
395         if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
396             notmuch->writable_xapian_db = new Xapian::WritableDatabase (notmuch->xapian_path,
397                                                                         DB_ACTION);
398             notmuch->xapian_db = notmuch->writable_xapian_db;
399         } else {
400             notmuch->xapian_db = new Xapian::Database (notmuch->xapian_path);
401         }
402
403         /* Check version.  As of database version 3, we represent
404          * changes in terms of features, so assume a version bump
405          * means a dramatically incompatible change. */
406         version = notmuch_database_get_version (notmuch);
407         if (version > NOTMUCH_DATABASE_VERSION) {
408             IGNORE_RESULT (asprintf (&message,
409                                      "Error: Notmuch database at %s\n"
410                                      "       has a newer database format version (%u) than supported by this\n"
411                                      "       version of notmuch (%u).\n",
412                                      database_path, version, NOTMUCH_DATABASE_VERSION));
413             notmuch_database_destroy (notmuch);
414             notmuch = NULL;
415             status = NOTMUCH_STATUS_FILE_ERROR;
416             goto DONE;
417         }
418
419         /* Check features. */
420         incompat_features = NULL;
421         notmuch->features = _notmuch_database_parse_features (
422             notmuch, notmuch->xapian_db->get_metadata ("features").c_str (),
423             version, mode == NOTMUCH_DATABASE_MODE_READ_WRITE ? 'w' : 'r',
424             &incompat_features);
425         if (incompat_features) {
426             IGNORE_RESULT (asprintf (&message,
427                                      "Error: Notmuch database at %s\n"
428                                      "       requires features (%s)\n"
429                                      "       not supported by this version of notmuch.\n",
430                                      database_path, incompat_features));
431             notmuch_database_destroy (notmuch);
432             notmuch = NULL;
433             status = NOTMUCH_STATUS_FILE_ERROR;
434             goto DONE;
435         }
436
437         _load_database_state (notmuch);
438
439         notmuch->query_parser = new Xapian::QueryParser;
440         notmuch->term_gen = new Xapian::TermGenerator;
441         notmuch->term_gen->set_stemmer (Xapian::Stem ("english"));
442         notmuch->value_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
443         notmuch->date_range_processor = new ParseTimeRangeProcessor (NOTMUCH_VALUE_TIMESTAMP,
444                                                                      "date:");
445         notmuch->last_mod_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_LAST_MOD,
446                                                                               "lastmod:");
447         notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
448         notmuch->query_parser->set_database (*notmuch->xapian_db);
449         notmuch->query_parser->set_stemmer (Xapian::Stem ("english"));
450         notmuch->query_parser->set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
451         notmuch->query_parser->add_rangeprocessor (notmuch->value_range_processor);
452         notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
453         notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor);
454
455         /* Configuration information is needed to set up query parser */
456         status = _notmuch_config_load_from_database (notmuch);
457         if (status)
458             goto DONE;
459
460         if (key_file)
461             status = _notmuch_config_load_from_file (notmuch, key_file);
462         if (status)
463             goto DONE;
464
465         status = _choose_dir (notmuch, profile,
466                               NOTMUCH_CONFIG_HOOK_DIR,
467                               "XDG_CONFIG_HOME",
468                               ".config",
469                               "hooks",
470                               &message);
471         if (status)
472             goto DONE;
473
474         status = _choose_dir (notmuch, profile,
475                               NOTMUCH_CONFIG_BACKUP_DIR,
476                               "XDG_DATA_HOME",
477                               ".local/share",
478                               "backups",
479                               &message);
480         if (status)
481             goto DONE;
482         status = _notmuch_config_load_defaults (notmuch);
483         if (status)
484             goto DONE;
485
486         status = _notmuch_database_setup_standard_query_fields (notmuch);
487         if (status)
488             goto DONE;
489
490         status = _notmuch_database_setup_user_query_fields (notmuch);
491         if (status)
492             goto DONE;
493
494     } catch (const Xapian::Error &error) {
495         IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
496                                  error.get_msg ().c_str ()));
497         notmuch_database_destroy (notmuch);
498         notmuch = NULL;
499         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
500     }
501   DONE:
502     if (message_ptr)
503         *message_ptr = message;
504     return status;
505 }
506
507 notmuch_status_t
508 notmuch_database_open_with_config (const char *database_path,
509                                    notmuch_database_mode_t mode,
510                                    const char *config_path,
511                                    const char *profile,
512                                    notmuch_database_t **database,
513                                    char **status_string)
514 {
515     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
516     void *local = talloc_new (NULL);
517     notmuch_database_t *notmuch = NULL;
518     char *message = NULL;
519     GKeyFile *key_file = NULL;
520     bool split = false;
521
522     _init_libs ();
523
524     notmuch = _alloc_notmuch ();
525     if (! notmuch) {
526         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
527         goto DONE;
528     }
529
530     status = _load_key_file (notmuch, config_path, profile, &key_file);
531     if (status) {
532         message = strdup ("Error: cannot load config file.\n");
533         goto DONE;
534     }
535
536     if ((status = _choose_database_path (local, profile, key_file,
537                                          &database_path, &split,
538                                          &message)))
539         goto DONE;
540
541     status = _db_dir_exists (database_path, &message);
542     if (status)
543         goto DONE;
544
545     _set_database_path (notmuch, database_path);
546
547     status = _notmuch_choose_xapian_path (notmuch, database_path,
548                                           &notmuch->xapian_path, &message);
549     if (status)
550         goto DONE;
551
552     status = _finish_open (notmuch, profile, mode, key_file, &message);
553
554   DONE:
555     talloc_free (local);
556
557     if (key_file)
558         g_key_file_free (key_file);
559
560     if (message) {
561         if (status_string)
562             *status_string = message;
563         else
564             free (message);
565     }
566
567     if (database)
568         *database = notmuch;
569     else
570         talloc_free (notmuch);
571
572     if (notmuch)
573         notmuch->open = true;
574
575     return status;
576 }
577
578 notmuch_status_t
579 notmuch_database_create (const char *path, notmuch_database_t **database)
580 {
581     char *status_string = NULL;
582     notmuch_status_t status;
583
584     status = notmuch_database_create_verbose (path, database,
585                                               &status_string);
586
587     if (status_string) {
588         fputs (status_string, stderr);
589         free (status_string);
590     }
591
592     return status;
593 }
594
595 notmuch_status_t
596 notmuch_database_create_verbose (const char *path,
597                                  notmuch_database_t **database,
598                                  char **status_string)
599 {
600     return notmuch_database_create_with_config (path, "", NULL, database, status_string);
601 }
602
603 notmuch_status_t
604 notmuch_database_create_with_config (const char *database_path,
605                                      const char *config_path,
606                                      const char *profile,
607                                      notmuch_database_t **database,
608                                      char **status_string)
609 {
610     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
611     notmuch_database_t *notmuch = NULL;
612     const char *notmuch_path = NULL;
613     char *message = NULL;
614     GKeyFile *key_file = NULL;
615     void *local = talloc_new (NULL);
616     int err;
617     bool split = false;
618
619     _init_libs ();
620
621     notmuch = _alloc_notmuch ();
622     if (! notmuch) {
623         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
624         goto DONE;
625     }
626
627     status = _load_key_file (notmuch, config_path, profile, &key_file);
628     if (status) {
629         message = strdup ("Error: cannot load config file.\n");
630         goto DONE;
631     }
632
633     if ((status = _choose_database_path (local, profile, key_file,
634                                          &database_path, &split, &message)))
635         goto DONE;
636
637     status = _db_dir_exists (database_path, &message);
638     if (status)
639         goto DONE;
640
641     _set_database_path (notmuch, database_path);
642
643     if (key_file && ! split) {
644         char *mail_root = notmuch_canonicalize_file_name (
645             g_key_file_get_value (key_file, "database", "mail_root", NULL));
646         char *db_path = notmuch_canonicalize_file_name (database_path);
647
648         split = (mail_root && (0 != strcmp (mail_root, db_path)));
649
650         free (mail_root);
651         free (db_path);
652     }
653
654     if (split) {
655         notmuch_path = database_path;
656     } else {
657         if (! (notmuch_path = talloc_asprintf (local, "%s/%s", database_path, ".notmuch"))) {
658             status = NOTMUCH_STATUS_OUT_OF_MEMORY;
659             goto DONE;
660         }
661
662         err = mkdir (notmuch_path, 0755);
663         if (err) {
664             if (errno == EEXIST) {
665                 status = NOTMUCH_STATUS_DATABASE_EXISTS;
666                 talloc_free (notmuch);
667                 notmuch = NULL;
668             } else {
669                 IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n",
670                                          notmuch_path, strerror (errno)));
671                 status = NOTMUCH_STATUS_FILE_ERROR;
672             }
673             goto DONE;
674         }
675     }
676
677     if (! (notmuch->xapian_path = talloc_asprintf (notmuch, "%s/%s", notmuch_path, "xapian"))) {
678         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
679         goto DONE;
680     }
681
682     status = _trial_open (notmuch->xapian_path, &message);
683     if (status == NOTMUCH_STATUS_SUCCESS) {
684         notmuch_database_destroy (notmuch);
685         notmuch = NULL;
686         status = NOTMUCH_STATUS_DATABASE_EXISTS;
687         goto DONE;
688     }
689
690     if (message)
691         free (message);
692
693     status = _finish_open (notmuch,
694                            profile,
695                            NOTMUCH_DATABASE_MODE_READ_WRITE,
696                            key_file,
697                            &message);
698     if (status)
699         goto DONE;
700
701     /* Upgrade doesn't add these feature to existing databases, but
702      * new databases have them. */
703     notmuch->features |= NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES;
704     notmuch->features |= NOTMUCH_FEATURE_INDEXED_MIMETYPES;
705     notmuch->features |= NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY;
706
707     status = notmuch_database_upgrade (notmuch, NULL, NULL);
708     if (status) {
709         notmuch_database_close (notmuch);
710         notmuch = NULL;
711     }
712
713   DONE:
714     talloc_free (local);
715
716     if (key_file)
717         g_key_file_free (key_file);
718
719     if (message) {
720         if (status_string)
721             *status_string = message;
722         else
723             free (message);
724     }
725     if (database)
726         *database = notmuch;
727     else
728         talloc_free (notmuch);
729     return status;
730 }
731
732 notmuch_status_t
733 notmuch_database_reopen (notmuch_database_t *notmuch,
734                          notmuch_database_mode_t new_mode)
735 {
736     notmuch_database_mode_t cur_mode = _notmuch_database_mode (notmuch);
737
738     if (notmuch->xapian_db == NULL) {
739         _notmuch_database_log (notmuch, "Cannot reopen closed or nonexistent database\n");
740         return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
741     }
742
743     try {
744         if (cur_mode == new_mode &&
745             new_mode == NOTMUCH_DATABASE_MODE_READ_ONLY) {
746             notmuch->xapian_db->reopen ();
747         } else {
748             notmuch->xapian_db->close ();
749
750             delete notmuch->xapian_db;
751             notmuch->xapian_db = NULL;
752             /* no need to free the same object twice */
753             notmuch->writable_xapian_db = NULL;
754
755             if (new_mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
756                 notmuch->writable_xapian_db = new Xapian::WritableDatabase (notmuch->xapian_path,
757                                                                             DB_ACTION);
758                 notmuch->xapian_db = notmuch->writable_xapian_db;
759             } else {
760                 notmuch->xapian_db = new Xapian::Database (notmuch->xapian_path,
761                                                            DB_ACTION);
762             }
763         }
764
765         _load_database_state (notmuch);
766     } catch (const Xapian::Error &error) {
767         if (! notmuch->exception_reported) {
768             _notmuch_database_log (notmuch, "Error: A Xapian exception reopening database: %s\n",
769                                    error.get_msg ().c_str ());
770             notmuch->exception_reported = true;
771         }
772         return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
773     }
774
775     notmuch->view++;
776     notmuch->open = true;
777     return NOTMUCH_STATUS_SUCCESS;
778 }
779
780 notmuch_status_t
781 _maybe_load_config_from_database (notmuch_database_t *notmuch,
782                                   GKeyFile *key_file,
783                                   const char *database_path,
784                                   const char *profile)
785 {
786     char *message; /* ignored */
787
788     if (_db_dir_exists (database_path, &message))
789         return NOTMUCH_STATUS_NO_DATABASE;
790
791     _set_database_path (notmuch, database_path);
792
793     if (_notmuch_choose_xapian_path (notmuch, database_path, &notmuch->xapian_path, &message))
794         return NOTMUCH_STATUS_NO_DATABASE;
795
796     (void) _finish_open (notmuch, profile, NOTMUCH_DATABASE_MODE_READ_ONLY, key_file, &message);
797
798     return NOTMUCH_STATUS_SUCCESS;
799 }
800
801 notmuch_status_t
802 notmuch_database_load_config (const char *database_path,
803                               const char *config_path,
804                               const char *profile,
805                               notmuch_database_t **database,
806                               char **status_string)
807 {
808     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS, warning = NOTMUCH_STATUS_SUCCESS;
809     void *local = talloc_new (NULL);
810     notmuch_database_t *notmuch = NULL;
811     char *message = NULL;
812     GKeyFile *key_file = NULL;
813     bool split = false;
814
815     _init_libs ();
816
817     notmuch = _alloc_notmuch ();
818     if (! notmuch) {
819         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
820         goto DONE;
821     }
822
823     status = _load_key_file (notmuch, config_path, profile, &key_file);
824     switch (status) {
825     case NOTMUCH_STATUS_SUCCESS:
826         break;
827     case NOTMUCH_STATUS_NO_CONFIG:
828         warning = status;
829         break;
830     default:
831         message = strdup ("Error: cannot load config file.\n");
832         goto DONE;
833     }
834
835     status = _choose_database_path (local, profile, key_file,
836                                     &database_path, &split, &message);
837     switch (status) {
838     case NOTMUCH_STATUS_NO_DATABASE:
839     case NOTMUCH_STATUS_SUCCESS:
840         if (! warning)
841             warning = status;
842         break;
843     default:
844         goto DONE;
845     }
846
847
848     if (database_path) {
849         status = _maybe_load_config_from_database (notmuch, key_file, database_path, profile);
850         switch (status) {
851         case NOTMUCH_STATUS_NO_DATABASE:
852         case NOTMUCH_STATUS_SUCCESS:
853             if (! warning)
854                 warning = status;
855             break;
856         default:
857             goto DONE;
858         }
859     }
860
861     if (key_file) {
862         status = _notmuch_config_load_from_file (notmuch, key_file);
863         if (status)
864             goto DONE;
865     }
866     status = _notmuch_config_load_defaults (notmuch);
867     if (status)
868         goto DONE;
869
870   DONE:
871     talloc_free (local);
872
873     if (status_string)
874         *status_string = message;
875
876     if (database)
877         *database = notmuch;
878
879     if (status)
880         return status;
881     else
882         return warning;
883 }