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