NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
db.c
Go to the documentation of this file.
1
25
31
32#include "config.h"
33#include <limits.h>
34#include <notmuch.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include <sys/stat.h>
38#include <time.h>
39#include "private.h"
40#include "mutt/lib.h"
41#include "config/lib.h"
42#include "email/lib.h"
43#include "core/lib.h"
44#include "lib.h"
45#include "adata.h"
46#include "mdata.h"
47#include "mutt_logging.h"
48
63const char *nm_db_get_filename(struct Mailbox *m)
64{
65 struct NmMboxData *mdata = nm_mdata_get(m);
66 const char *db_filename = NULL;
67
68 const char *const c_nm_default_url = cs_subset_string(NeoMutt->sub, "nm_default_url");
69 if (mdata && mdata->db_url && mdata->db_url->path)
70 db_filename = mdata->db_url->path;
71 else
72 db_filename = c_nm_default_url;
73
74 if (!db_filename)
75 return NULL;
76
77 if (nm_path_probe(db_filename, NULL) == MUTT_NOTMUCH)
78 db_filename += NmUrlProtocolLen;
79
80 mutt_debug(LL_DEBUG2, "nm: db filename '%s'\n", db_filename);
81 return db_filename;
82}
83
84#if LIBNOTMUCH_CHECK_VERSION(5, 4, 0)
90static const char *get_nm_config_file(void)
91{
92 const char *config_to_use = NULL;
93 const char *c_nm_config_file = cs_subset_path(NeoMutt->sub, "nm_config_file");
94
95 // Workaround the configuration system mapping "" to NULL.
96 if (!c_nm_config_file)
97 {
98 config_to_use = "";
99 }
100 else if (!mutt_strn_equal(c_nm_config_file, "auto", 4))
101 {
102 config_to_use = c_nm_config_file;
103 }
104
105 return config_to_use;
106}
107#endif
108
116notmuch_database_t *nm_db_do_open(const char *filename, bool writable, bool verbose)
117{
118 notmuch_database_t *db = NULL;
119 int ct = 0;
120 notmuch_status_t st = NOTMUCH_STATUS_SUCCESS;
121 char *msg = NULL;
122
123 const short c_nm_open_timeout = cs_subset_number(NeoMutt->sub, "nm_open_timeout");
124 mutt_debug(LL_DEBUG1, "nm: db open '%s' %s (timeout %d)\n", filename ? filename : "(auto)",
125 writable ? "[WRITE]" : "[READ]", c_nm_open_timeout);
126
127 const notmuch_database_mode_t mode = writable ? NOTMUCH_DATABASE_MODE_READ_WRITE :
128 NOTMUCH_DATABASE_MODE_READ_ONLY;
129
130 do
131 {
132#if LIBNOTMUCH_CHECK_VERSION(5, 4, 0)
133 // notmuch 0.32-0.32.2 didn't bump libnotmuch version to 5.4.
134 const char *config_file = get_nm_config_file();
135 const char *const c_nm_config_profile = cs_subset_string(NeoMutt->sub, "nm_config_profile");
136
137 FREE(&msg);
138 st = notmuch_database_open_with_config(filename, mode, config_file,
139 c_nm_config_profile, &db, &msg);
140
141 // Attempt opening database without configuration file. Don't if the user specified no config.
142 if ((st == NOTMUCH_STATUS_NO_CONFIG) && !mutt_str_equal(config_file, ""))
143 {
144 mutt_debug(LL_DEBUG1, "nm: Could not find notmuch configuration file: %s\n", config_file);
145 mutt_debug(LL_DEBUG1, "nm: Attempting to open notmuch db without configuration file\n");
146
147 FREE(&msg);
148
149 st = notmuch_database_open_with_config(filename, mode, "", NULL, &db, &msg);
150 }
151 else if ((st == NOTMUCH_STATUS_NO_CONFIG) && !config_file)
152 {
153 FREE(&msg);
154 }
155#elif LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
156 st = notmuch_database_open_verbose(filename, mode, &db, &msg);
157#elif defined(NOTMUCH_API_3)
158 st = notmuch_database_open(filename, mode, &db);
159#else
160 db = notmuch_database_open(filename, mode);
161#endif
162 if ((st == NOTMUCH_STATUS_FILE_ERROR) || db || !c_nm_open_timeout ||
163 ((ct / 2) > c_nm_open_timeout))
164 {
165 break;
166 }
167
168 if (verbose && ct && ((ct % 2) == 0))
169 mutt_error(_("Waiting for notmuch DB... (%d sec)"), ct / 2);
170 mutt_date_sleep_ms(500); /* Half a second */
171 ct++;
172 } while (true);
173
174 if (st != NOTMUCH_STATUS_SUCCESS)
175 {
176 db = NULL;
177 }
178
179 if (verbose)
180 {
181 if (!db)
182 {
183 if (msg)
184 {
185 mutt_error("%s", msg);
186 }
187 else
188 {
189 mutt_error(_("Can't open notmuch database: %s: %s"), filename,
190 st ? notmuch_status_to_string(st) : _("unknown reason"));
191 }
192 }
193 else if (ct > 1)
194 {
196 }
197 }
198
199 FREE(&msg);
200
201 return db;
202}
203
210notmuch_database_t *nm_db_get(struct Mailbox *m, bool writable)
211{
212 struct NmAccountData *adata = nm_adata_get(m);
213
214 if (!adata)
215 return NULL;
216
217 // Use an existing open db if we have one.
218 if (adata->db)
219 return adata->db;
220
221 const char *db_filename = nm_db_get_filename(m);
222#if LIBNOTMUCH_CHECK_VERSION(5, 4, 0)
223 // On libnotmuch >= 5.4, NULL db_filename lets notmuch resolve the
224 // database path from its config file or environment variables.
225 adata->db = nm_db_do_open(db_filename, writable, true);
226#else
227 if (!db_filename)
228 {
229 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
230 db_filename = c_folder;
231 }
232 if (db_filename)
233 adata->db = nm_db_do_open(db_filename, writable, true);
234#endif
235
236 return adata->db;
237}
238
245int nm_db_release(struct Mailbox *m)
246{
247 struct NmAccountData *adata = nm_adata_get(m);
248 if (!adata || !adata->db || nm_db_is_longrun(m))
249 return -1;
250
251 mutt_debug(LL_DEBUG1, "nm: db close\n");
252 nm_db_free(adata->db);
253 adata->db = NULL;
254 adata->longrun = false;
255 return 0;
256}
257
262void nm_db_free(notmuch_database_t *db)
263{
264#ifdef NOTMUCH_API_3
265 notmuch_database_destroy(db);
266#else
267 notmuch_database_close(db);
268#endif
269}
270
279{
280 struct NmAccountData *adata = nm_adata_get(m);
281 if (!adata || !adata->db)
282 return -1;
283
284 if (adata->trans)
285 return 0;
286
287 mutt_debug(LL_DEBUG2, "nm: db trans start\n");
288 if (notmuch_database_begin_atomic(adata->db))
289 return -1;
290 adata->trans = true;
291 return 1;
292}
293
301{
302 struct NmAccountData *adata = nm_adata_get(m);
303 if (!adata || !adata->db)
304 return -1;
305
306 if (!adata->trans)
307 return 0;
308
309 mutt_debug(LL_DEBUG2, "nm: db trans end\n");
310 adata->trans = false;
311 if (notmuch_database_end_atomic(adata->db))
312 return -1;
313
314 return 0;
315}
316
327int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
328{
329 if (!m || !mtime)
330 return -1;
331
332 struct stat st = { 0 };
333 char path[PATH_MAX] = { 0 };
334 const char *db_filename = nm_db_get_filename(m);
335 if (!db_filename)
336 return -1;
337
338 mutt_debug(LL_DEBUG2, "nm: checking database mtime '%s'\n", db_filename);
339
340 // See if the path we were given has a Xapian directory.
341 // After notmuch 0.32, a .notmuch folder isn't guaranteed.
342 snprintf(path, sizeof(path), "%s/xapian", db_filename);
343 if (stat(path, &st) == 0)
344 {
345 *mtime = st.st_mtime;
346 return 0;
347 }
348
349 // Otherwise, check for a .notmuch directory.
350 snprintf(path, sizeof(path), "%s/.notmuch/xapian", db_filename);
351
352 if (stat(path, &st) != 0)
353 return -1;
354
355 *mtime = st.st_mtime;
356 return 0;
357}
358
365{
366 struct NmAccountData *adata = nm_adata_get(m);
367 if (!adata)
368 return false;
369
370 return adata->longrun;
371}
372
378void nm_db_longrun_init(struct Mailbox *m, bool writable)
379{
380 struct NmAccountData *adata = nm_adata_get(m);
381
382 if (!(adata && nm_db_get(m, writable)))
383 return;
384
385 adata->longrun = true;
386 mutt_debug(LL_DEBUG2, "nm: long run initialized\n");
387}
388
394{
395 struct NmAccountData *adata = nm_adata_get(m);
396
397 if (adata)
398 {
399 adata->longrun = false; /* to force nm_db_release() released DB */
400 if (nm_db_release(m) == 0)
401 mutt_debug(LL_DEBUG2, "nm: long run deinitialized\n");
402 else
403 adata->longrun = true;
404 }
405}
406
412{
413 struct NmAccountData *adata = nm_adata_get(m);
414 if (!adata || !adata->db)
415 return;
416
417 mutt_debug(LL_DEBUG1, "nm: ERROR: db is open, closing\n");
418 nm_db_release(m);
419}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition helpers.c:168
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition mailbox.h:50
Structs that make up an email.
#define mutt_error(...)
Definition logging2.h:94
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
enum MailboxType nm_path_probe(const char *path, const struct stat *st)
Is this a Notmuch Mailbox?
Definition notmuch.c:2531
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
void mutt_date_sleep_ms(size_t ms)
Sleep for milliseconds.
Definition date.c:984
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:665
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:429
#define PATH_MAX
Definition mutt.h:49
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
struct NmAccountData * nm_adata_get(struct Mailbox *m)
Get the Notmuch Account data.
Definition adata.c:69
Notmuch-specific Account data.
notmuch_database_t * nm_db_get(struct Mailbox *m, bool writable)
Get the Notmuch database.
Definition db.c:210
int nm_db_trans_begin(struct Mailbox *m)
Start a Notmuch database transaction.
Definition db.c:278
notmuch_database_t * nm_db_do_open(const char *filename, bool writable, bool verbose)
Open a Notmuch database.
Definition db.c:116
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition db.c:393
const char * nm_db_get_filename(struct Mailbox *m)
Get the filename of the Notmuch database.
Definition db.c:63
int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
Get the database modification time.
Definition db.c:327
int nm_db_release(struct Mailbox *m)
Close the Notmuch database.
Definition db.c:245
bool nm_db_is_longrun(struct Mailbox *m)
Is Notmuch in the middle of a long-running transaction.
Definition db.c:364
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition db.c:378
void nm_db_debug_check(struct Mailbox *m)
Check if the database is open.
Definition db.c:411
void nm_db_free(notmuch_database_t *db)
Decoupled way to close a Notmuch database.
Definition db.c:262
int nm_db_trans_end(struct Mailbox *m)
End a database transaction.
Definition db.c:300
Notmuch virtual mailbox type.
struct NmMboxData * nm_mdata_get(struct Mailbox *m)
Get the Notmuch Mailbox data.
Definition mdata.c:96
Notmuch-specific Mailbox data.
Notmuch private types.
const int NmUrlProtocolLen
Length of NmUrlProtocol string.
Definition notmuch.c:104
void * adata
Private data (for Mailbox backends)
Definition account.h:42
A mailbox.
Definition mailbox.h:81
void * mdata
Driver specific data.
Definition mailbox.h:134
bool verbose
Display status messages?
Definition mailbox.h:119
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
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:35