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