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