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