NeoMutt  2022-04-29-247-gc6aae8
Teaching an old dog new tricks
DOXYGEN
db.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <limits.h>
31#include <notmuch.h>
32#include <stdbool.h>
33#include <stdio.h>
34#include <sys/stat.h>
35#include "private.h"
36#include "mutt/lib.h"
37#include "config/lib.h"
38#include "email/lib.h"
39#include "core/lib.h"
40#include "lib.h"
41#include "adata.h"
42#include "mdata.h"
43#include "mutt_logging.h"
44
54const char *nm_db_get_filename(struct Mailbox *m)
55{
56 struct NmMboxData *mdata = nm_mdata_get(m);
57 const char *db_filename = NULL;
58
59 const char *const c_nm_default_url = cs_subset_string(NeoMutt->sub, "nm_default_url");
60 if (mdata && mdata->db_url && mdata->db_url->path)
61 db_filename = mdata->db_url->path;
62 else
63 db_filename = c_nm_default_url;
64
65 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
66 if (!db_filename && !c_folder)
67 return NULL;
68
69 if (!db_filename)
70 db_filename = c_folder;
71
72 if (nm_path_probe(db_filename, NULL) == MUTT_NOTMUCH)
73 db_filename += NmUrlProtocolLen;
74
75 mutt_debug(LL_DEBUG2, "nm: db filename '%s'\n", db_filename);
76 return db_filename;
77}
78
84static const char *get_nm_config_file(void)
85{
86 const char *config_to_use = NULL;
87 const char *nm_config_file = cs_subset_path(NeoMutt->sub, "nm_config_file");
88
89 // Workaround the configuration system mapping "" to NULL.
90 if (nm_config_file == NULL)
91 {
92 config_to_use = "";
93 }
94 else if (!mutt_strn_equal(nm_config_file, "auto", 4))
95 {
96 config_to_use = nm_config_file;
97 }
98
99 return config_to_use;
100}
101
109notmuch_database_t *nm_db_do_open(const char *filename, bool writable, bool verbose)
110{
111 notmuch_database_t *db = NULL;
112 int ct = 0;
113 notmuch_status_t st = NOTMUCH_STATUS_SUCCESS;
114 char *msg = NULL;
115
116 const short c_nm_open_timeout = cs_subset_number(NeoMutt->sub, "nm_open_timeout");
117 mutt_debug(LL_DEBUG1, "nm: db open '%s' %s (timeout %d)\n", filename,
118 writable ? "[WRITE]" : "[READ]", c_nm_open_timeout);
119
120 const notmuch_database_mode_t mode = writable ? NOTMUCH_DATABASE_MODE_READ_WRITE :
121 NOTMUCH_DATABASE_MODE_READ_ONLY;
122
123 do
124 {
125#if LIBNOTMUCH_CHECK_VERSION(5, 4, 0)
126 // notmuch 0.32-0.32.2 didn't bump libnotmuch version to 5.4.
127 const char *config_file = get_nm_config_file();
128 const char *const config_profile = cs_subset_string(NeoMutt->sub, "nm_config_profile");
129
130 st = notmuch_database_open_with_config(filename, mode, config_file,
131 config_profile, &db, &msg);
132
133 // Attempt opening database without configuration file. Don't if the user specified no config.
134 if (st == NOTMUCH_STATUS_NO_CONFIG && !mutt_str_equal(config_file, ""))
135 {
136 mutt_debug(LL_DEBUG1, "nm: Could not find notmuch configuration file: %s\n", config_file);
137 mutt_debug(LL_DEBUG1, "nm: Attempting to open notmuch db without configuration file.\n");
138
139 FREE(&msg);
140
141 st = notmuch_database_open_with_config(filename, mode, "", NULL, &db, &msg);
142 }
143#elif LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
144 st = notmuch_database_open_verbose(filename, mode, &db, &msg);
145#elif defined(NOTMUCH_API_3)
146 st = notmuch_database_open(filename, mode, &db);
147#else
148 db = notmuch_database_open(filename, mode);
149#endif
150 if ((st == NOTMUCH_STATUS_FILE_ERROR) || db || !c_nm_open_timeout ||
151 ((ct / 2) > c_nm_open_timeout))
152 {
153 break;
154 }
155
156 if (verbose && ct && ((ct % 2) == 0))
157 mutt_error(_("Waiting for notmuch DB... (%d sec)"), ct / 2);
158 mutt_date_sleep_ms(500); /* Half a second */
159 ct++;
160 } while (true);
161
162 if (st != NOTMUCH_STATUS_SUCCESS)
163 {
164 db = NULL;
165 }
166
167 if (verbose)
168 {
169 if (!db)
170 {
171 if (msg)
172 {
173 mutt_error(msg);
174 }
175 else
176 {
177 mutt_error(_("Can't open notmuch database: %s: %s"), filename,
178 st ? notmuch_status_to_string(st) : _("unknown reason"));
179 }
180 }
181 else if (ct > 1)
182 {
184 }
185 }
186
187 FREE(&msg);
188
189 return db;
190}
191
198notmuch_database_t *nm_db_get(struct Mailbox *m, bool writable)
199{
200 struct NmAccountData *adata = nm_adata_get(m);
201
202 if (!adata)
203 return NULL;
204
205 // Use an existing open db if we have one.
206 if (adata->db)
207 return adata->db;
208
209 const char *db_filename = nm_db_get_filename(m);
210 if (db_filename)
211 adata->db = nm_db_do_open(db_filename, writable, true);
212
213 return adata->db;
214}
215
222int nm_db_release(struct Mailbox *m)
223{
224 struct NmAccountData *adata = nm_adata_get(m);
225 if (!adata || !adata->db || nm_db_is_longrun(m))
226 return -1;
227
228 mutt_debug(LL_DEBUG1, "nm: db close\n");
229 nm_db_free(adata->db);
230 adata->db = NULL;
231 adata->longrun = false;
232 return 0;
233}
234
239void nm_db_free(notmuch_database_t *db)
240{
241#ifdef NOTMUCH_API_3
242 notmuch_database_destroy(db);
243#else
244 notmuch_database_close(db);
245#endif
246}
247
256{
257 struct NmAccountData *adata = nm_adata_get(m);
258 if (!adata || !adata->db)
259 return -1;
260
261 if (adata->trans)
262 return 0;
263
264 mutt_debug(LL_DEBUG2, "nm: db trans start\n");
265 if (notmuch_database_begin_atomic(adata->db))
266 return -1;
267 adata->trans = true;
268 return 1;
269}
270
278{
279 struct NmAccountData *adata = nm_adata_get(m);
280 if (!adata || !adata->db)
281 return -1;
282
283 if (!adata->trans)
284 return 0;
285
286 mutt_debug(LL_DEBUG2, "nm: db trans end\n");
287 adata->trans = false;
288 if (notmuch_database_end_atomic(adata->db))
289 return -1;
290
291 return 0;
292}
293
304int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
305{
306 if (!m || !mtime)
307 return -1;
308
309 struct stat st = { 0 };
310 char path[PATH_MAX] = { 0 };
311 const char *db_filename = nm_db_get_filename(m);
312
313 mutt_debug(LL_DEBUG2, "nm: checking database mtime '%s'\n", db_filename);
314
315 // See if the path we were given has a Xapian directory.
316 // After notmuch 0.32, a .notmuch folder isn't guaranteed.
317 snprintf(path, sizeof(path), "%s/xapian", db_filename);
318 if (stat(path, &st) == 0)
319 {
320 *mtime = st.st_mtime;
321 return 0;
322 }
323
324 // Otherwise, check for a .notmuch directory.
325 snprintf(path, sizeof(path), "%s/.notmuch/xapian", db_filename);
326
327 if (stat(path, &st) != 0)
328 return -1;
329
330 *mtime = st.st_mtime;
331 return 0;
332}
333
340{
341 struct NmAccountData *adata = nm_adata_get(m);
342 if (!adata)
343 return false;
344
345 return adata->longrun;
346}
347
353void nm_db_longrun_init(struct Mailbox *m, bool writable)
354{
355 struct NmAccountData *adata = nm_adata_get(m);
356
357 if (!(adata && nm_db_get(m, writable)))
358 return;
359
360 adata->longrun = true;
361 mutt_debug(LL_DEBUG2, "nm: long run initialized\n");
362}
363
369{
370 struct NmAccountData *adata = nm_adata_get(m);
371
372 if (adata)
373 {
374 adata->longrun = false; /* to force nm_db_release() released DB */
375 if (nm_db_release(m) == 0)
376 mutt_debug(LL_DEBUG2, "nm: long run deinitialized\n");
377 else
378 adata->longrun = true;
379 }
380}
381
387{
388 struct NmAccountData *adata = nm_adata_get(m);
389 if (!adata || !adata->db)
390 return;
391
392 mutt_debug(LL_DEBUG1, "nm: ERROR: db is open, closing\n");
393 nm_db_release(m);
394}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
void mutt_date_sleep_ms(size_t ms)
Sleep for milliseconds.
Definition: date.c:705
Structs that make up an email.
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
enum MailboxType nm_path_probe(const char *path, const struct stat *st)
Is this a Notmuch Mailbox? - Implements MxOps::path_probe() -.
Definition: notmuch.c:2433
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:496
#define PATH_MAX
Definition: mutt.h:40
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
NeoMutt Logging.
struct NmAccountData * nm_adata_get(struct Mailbox *m)
Get the Notmuch Account data.
Definition: adata.c:68
notmuch_database_t * nm_db_get(struct Mailbox *m, bool writable)
Get the Notmuch database.
Definition: db.c:198
int nm_db_trans_begin(struct Mailbox *m)
Start a Notmuch database transaction.
Definition: db.c:255
notmuch_database_t * nm_db_do_open(const char *filename, bool writable, bool verbose)
Open a Notmuch database.
Definition: db.c:109
static const char * get_nm_config_file(void)
Gets the configuration file.
Definition: db.c:84
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition: db.c:368
const char * nm_db_get_filename(struct Mailbox *m)
Get the filename of the Notmuch database.
Definition: db.c:54
int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
Get the database modification time.
Definition: db.c:304
int nm_db_release(struct Mailbox *m)
Close the Notmuch database.
Definition: db.c:222
bool nm_db_is_longrun(struct Mailbox *m)
Is Notmuch in the middle of a long-running transaction.
Definition: db.c:339
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition: db.c:353
void nm_db_debug_check(struct Mailbox *m)
Check if the database is open.
Definition: db.c:386
void nm_db_free(notmuch_database_t *db)
Decoupled way to close a Notmuch database.
Definition: db.c:239
int nm_db_trans_end(struct Mailbox *m)
End a database transaction.
Definition: db.c:277
struct NmMboxData * nm_mdata_get(struct Mailbox *m)
Get the Notmuch Mailbox data.
Definition: mdata.c:97
Notmuch-specific Mailbox data.
const int NmUrlProtocolLen
Definition: notmuch.c:94
Pop-specific Account data.
GUI display the mailboxes in a side panel.
Key value store.
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
A mailbox.
Definition: mailbox.h:79
void * mdata
Driver specific data.
Definition: mailbox.h:132
bool verbose
Display status messages?
Definition: mailbox.h:114
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Notmuch-specific Account data -.
Definition: adata.h:35
notmuch_database_t * db
Connection to Notmuch database.
Definition: adata.h:36
Notmuch-specific Mailbox data -.
Definition: mdata.h:34