]> git.notmuchmail.org Git - notmuch/blob - lib/open.cc
7acaea7bf3502abd685fb708af63bb5863729f5a
[notmuch] / lib / open.cc
1 #include <unistd.h>
2 #include "database-private.h"
3 #include "parse-time-vrp.h"
4
5 #if HAVE_XAPIAN_DB_RETRY_LOCK
6 #define DB_ACTION (Xapian::DB_CREATE_OR_OPEN | Xapian::DB_RETRY_LOCK)
7 #else
8 #define DB_ACTION Xapian::DB_CREATE_OR_OPEN
9 #endif
10
11 notmuch_status_t
12 notmuch_database_open (const char *path,
13                        notmuch_database_mode_t mode,
14                        notmuch_database_t **database)
15 {
16     char *status_string = NULL;
17     notmuch_status_t status;
18
19     status = notmuch_database_open_verbose (path, mode, database,
20                                             &status_string);
21
22     if (status_string) {
23         fputs (status_string, stderr);
24         free (status_string);
25     }
26
27     return status;
28 }
29
30 notmuch_status_t
31 notmuch_database_open_verbose (const char *path,
32                                notmuch_database_mode_t mode,
33                                notmuch_database_t **database,
34                                char **status_string)
35 {
36     return notmuch_database_open_with_config (path, mode, "", NULL,
37                                               database, status_string);
38 }
39
40 notmuch_status_t
41 notmuch_database_open_with_config (const char *database_path,
42                                    notmuch_database_mode_t mode,
43                                    const char *config_path,
44                                    unused(const char *profile),
45                                    notmuch_database_t **database,
46                                    char **status_string)
47 {
48     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
49     void *local = talloc_new (NULL);
50     notmuch_database_t *notmuch = NULL;
51     char *notmuch_path, *xapian_path, *incompat_features;
52     char *configured_database_path = NULL;
53     char *message = NULL;
54     struct stat st;
55     int err;
56     unsigned int version;
57     GKeyFile *key_file = NULL;
58     static int initialized = 0;
59
60     /* XXX TODO: default locations for NULL case, handle profiles */
61     if (config_path != NULL && ! EMPTY_STRING (config_path)) {
62         key_file = g_key_file_new ();
63         if (! g_key_file_load_from_file (key_file, config_path, G_KEY_FILE_NONE, NULL)) {
64             status = NOTMUCH_STATUS_FILE_ERROR;
65             goto DONE;
66         }
67         configured_database_path = g_key_file_get_value (key_file, "database", "path", NULL);
68     }
69
70     if (database_path == NULL)
71         database_path = configured_database_path;
72
73     if (database_path == NULL) {
74         message = strdup ("Error: Cannot open a database for a NULL path.\n");
75         status = NOTMUCH_STATUS_NULL_POINTER;
76         goto DONE;
77     }
78
79     if (database_path[0] != '/') {
80         message = strdup ("Error: Database path must be absolute.\n");
81         status = NOTMUCH_STATUS_PATH_ERROR;
82         goto DONE;
83     }
84
85     if (! (notmuch_path = talloc_asprintf (local, "%s/%s", database_path, ".notmuch"))) {
86         message = strdup ("Out of memory\n");
87         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
88         goto DONE;
89     }
90
91     err = stat (notmuch_path, &st);
92     if (err) {
93         IGNORE_RESULT (asprintf (&message, "Error opening database at %s: %s\n",
94                                  notmuch_path, strerror (errno)));
95         status = NOTMUCH_STATUS_FILE_ERROR;
96         goto DONE;
97     }
98
99     if (! (xapian_path = talloc_asprintf (local, "%s/%s", notmuch_path, "xapian"))) {
100         message = strdup ("Out of memory\n");
101         status = NOTMUCH_STATUS_OUT_OF_MEMORY;
102         goto DONE;
103     }
104
105     /* Initialize the GLib type system and threads */
106 #if ! GLIB_CHECK_VERSION (2, 35, 1)
107     g_type_init ();
108 #endif
109
110     /* Initialize gmime */
111     if (! initialized) {
112         g_mime_init ();
113         initialized = 1;
114     }
115
116     notmuch = talloc_zero (NULL, notmuch_database_t);
117     notmuch->exception_reported = false;
118     notmuch->status_string = NULL;
119     notmuch->path = talloc_strdup (notmuch, database_path);
120
121     strip_trailing (notmuch->path, '/');
122
123     notmuch->writable_xapian_db = NULL;
124     notmuch->atomic_nesting = 0;
125     notmuch->view = 1;
126     try {
127         std::string last_thread_id;
128         std::string last_mod;
129
130         if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) {
131             notmuch->writable_xapian_db = new Xapian::WritableDatabase (xapian_path,
132                                                                         DB_ACTION);
133             notmuch->xapian_db = notmuch->writable_xapian_db;
134         } else {
135             notmuch->xapian_db = new Xapian::Database (xapian_path);
136         }
137
138         /* Check version.  As of database version 3, we represent
139          * changes in terms of features, so assume a version bump
140          * means a dramatically incompatible change. */
141         version = notmuch_database_get_version (notmuch);
142         if (version > NOTMUCH_DATABASE_VERSION) {
143             IGNORE_RESULT (asprintf (&message,
144                                      "Error: Notmuch database at %s\n"
145                                      "       has a newer database format version (%u) than supported by this\n"
146                                      "       version of notmuch (%u).\n",
147                                      notmuch_path, version, NOTMUCH_DATABASE_VERSION));
148             notmuch_database_destroy (notmuch);
149             notmuch = NULL;
150             status = NOTMUCH_STATUS_FILE_ERROR;
151             goto DONE;
152         }
153
154         /* Check features. */
155         incompat_features = NULL;
156         notmuch->features = _notmuch_database_parse_features (
157             local, notmuch->xapian_db->get_metadata ("features").c_str (),
158             version, mode == NOTMUCH_DATABASE_MODE_READ_WRITE ? 'w' : 'r',
159             &incompat_features);
160         if (incompat_features) {
161             IGNORE_RESULT (asprintf (&message,
162                                      "Error: Notmuch database at %s\n"
163                                      "       requires features (%s)\n"
164                                      "       not supported by this version of notmuch.\n",
165                                      notmuch_path, incompat_features));
166             notmuch_database_destroy (notmuch);
167             notmuch = NULL;
168             status = NOTMUCH_STATUS_FILE_ERROR;
169             goto DONE;
170         }
171
172         notmuch->last_doc_id = notmuch->xapian_db->get_lastdocid ();
173         last_thread_id = notmuch->xapian_db->get_metadata ("last_thread_id");
174         if (last_thread_id.empty ()) {
175             notmuch->last_thread_id = 0;
176         } else {
177             const char *str;
178             char *end;
179
180             str = last_thread_id.c_str ();
181             notmuch->last_thread_id = strtoull (str, &end, 16);
182             if (*end != '\0')
183                 INTERNAL_ERROR ("Malformed database last_thread_id: %s", str);
184         }
185
186         /* Get current highest revision number. */
187         last_mod = notmuch->xapian_db->get_value_upper_bound (
188             NOTMUCH_VALUE_LAST_MOD);
189         if (last_mod.empty ())
190             notmuch->revision = 0;
191         else
192             notmuch->revision = Xapian::sortable_unserialise (last_mod);
193         notmuch->uuid = talloc_strdup (
194             notmuch, notmuch->xapian_db->get_uuid ().c_str ());
195
196         notmuch->query_parser = new Xapian::QueryParser;
197         notmuch->term_gen = new Xapian::TermGenerator;
198         notmuch->term_gen->set_stemmer (Xapian::Stem ("english"));
199         notmuch->value_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_TIMESTAMP);
200         notmuch->date_range_processor = new ParseTimeRangeProcessor (NOTMUCH_VALUE_TIMESTAMP, "date:");
201         notmuch->last_mod_range_processor = new Xapian::NumberRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:");
202         notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
203         notmuch->query_parser->set_database (*notmuch->xapian_db);
204         notmuch->query_parser->set_stemmer (Xapian::Stem ("english"));
205         notmuch->query_parser->set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
206         notmuch->query_parser->add_rangeprocessor (notmuch->value_range_processor);
207         notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
208         notmuch->query_parser->add_rangeprocessor (notmuch->last_mod_range_processor);
209
210         /* Configuration information is needed to set up query parser */
211         status = _notmuch_config_load_from_database (notmuch);
212         if (status)
213             goto DONE;
214
215         if (key_file)
216             status = _notmuch_config_load_from_file (notmuch, key_file);
217         if (status)
218             goto DONE;
219
220         status = _notmuch_database_setup_standard_query_fields (notmuch);
221         if (status)
222             goto DONE;
223
224         status = _notmuch_database_setup_user_query_fields (notmuch);
225         if (status)
226             goto DONE;
227
228     } catch (const Xapian::Error &error) {
229         IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
230                                  error.get_msg ().c_str ()));
231         notmuch_database_destroy (notmuch);
232         notmuch = NULL;
233         status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
234     }
235
236   DONE:
237     talloc_free (local);
238
239     if (message) {
240         if (status_string)
241             *status_string = message;
242         else
243             free (message);
244     }
245
246     if (database)
247         *database = notmuch;
248     else
249         talloc_free (notmuch);
250
251     if (notmuch)
252         notmuch->open = true;
253
254     return status;
255 }