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