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