NeoMutt  2024-04-16-36-g75b6fb
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
db.c
Go to the documentation of this file.
1
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 "private.h"
39#include "mutt/lib.h"
40#include "config/lib.h"
41#include "email/lib.h"
42#include "core/lib.h"
43#include "lib.h"
44#include "adata.h"
45#include "mdata.h"
46#include "mutt_logging.h"
47
57const char *nm_db_get_filename(struct Mailbox *m)
58{
59 struct NmMboxData *mdata = nm_mdata_get(m);
60 const char *db_filename = NULL;
61
62 const char *const c_nm_default_url = cs_subset_string(NeoMutt->sub, "nm_default_url");
63 if (mdata && mdata->db_url && mdata->db_url->path)
64 db_filename = mdata->db_url->path;
65 else
66 db_filename = c_nm_default_url;
67
68 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
69 if (!db_filename && !c_folder)
70 return NULL;
71
72 if (!db_filename)
73 db_filename = c_folder;
74
75 if (nm_path_probe(db_filename, NULL) == MUTT_NOTMUCH)
76 db_filename += NmUrlProtocolLen;
77
78 mutt_debug(LL_DEBUG2, "nm: db filename '%s'\n", db_filename);
79 return db_filename;
80}
81
82#if LIBNOTMUCH_CHECK_VERSION(5, 4, 0)
88static const char *get_nm_config_file(void)
89{
90 const char *config_to_use = NULL;
91 const char *c_nm_config_file = cs_subset_path(NeoMutt->sub, "nm_config_file");
92
93 // Workaround the configuration system mapping "" to NULL.
94 if (!c_nm_config_file)
95 {
96 config_to_use = "";
97 }
98 else if (!mutt_strn_equal(c_nm_config_file, "auto", 4))
99 {
100 config_to_use = c_nm_config_file;
101 }
102
103 return config_to_use;
104}
105#endif
106
114notmuch_database_t *nm_db_do_open(const char *filename, bool writable, bool verbose)
115{
116 notmuch_database_t *db = NULL;
117 int ct = 0;
118 notmuch_status_t st = NOTMUCH_STATUS_SUCCESS;
119 char *msg = NULL;
120
121 const short c_nm_open_timeout = cs_subset_number(NeoMutt->sub, "nm_open_timeout");
122 mutt_debug(LL_DEBUG1, "nm: db open '%s' %s (timeout %d)\n", filename,
123 writable ? "[WRITE]" : "[READ]", c_nm_open_timeout);
124
125 const notmuch_database_mode_t mode = writable ? NOTMUCH_DATABASE_MODE_READ_WRITE :
126 NOTMUCH_DATABASE_MODE_READ_ONLY;
127
128 do
129 {
130#if LIBNOTMUCH_CHECK_VERSION(5, 4, 0)
131 // notmuch 0.32-0.32.2 didn't bump libnotmuch version to 5.4.
132 const char *config_file = get_nm_config_file();
133 const char *const c_nm_config_profile = cs_subset_string(NeoMutt->sub, "nm_config_profile");
134
135 FREE(&msg);
136 st = notmuch_database_open_with_config(filename, mode, config_file,
137 c_nm_config_profile, &db, &msg);
138
139 // Attempt opening database without configuration file. Don't if the user specified no config.
140 if ((st == NOTMUCH_STATUS_NO_CONFIG) && !mutt_str_equal(config_file, ""))
141 {
142 mutt_debug(LL_DEBUG1, "nm: Could not find notmuch configuration file: %s\n", config_file);
143 mutt_debug(LL_DEBUG1, "nm: Attempting to open notmuch db without configuration file\n");
144
145 FREE(&msg);
146
147 st = notmuch_database_open_with_config(filename, mode, "", NULL, &db, &msg);
148 }
149 else if ((st == NOTMUCH_STATUS_NO_CONFIG) && !config_file)
150 {
151 FREE(&msg);
152 }
153#elif LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
154 st = notmuch_database_open_verbose(filename, mode, &db, &msg);
155#elif defined(NOTMUCH_API_3)
156 st = notmuch_database_open(filename, mode, &db);
157#else
158 db = notmuch_database_open(filename, mode);
159#endif
160 if ((st == NOTMUCH_STATUS_FILE_ERROR) || db || !c_nm_open_timeout ||
161 ((ct / 2) > c_nm_open_timeout))
162 {
163 break;
164 }
165
166 if (verbose && ct && ((ct % 2) == 0))
167 mutt_error(_("Waiting for notmuch DB... (%d sec)"), ct / 2);
168 mutt_date_sleep_ms(500); /* Half a second */
169 ct++;
170 } while (true);
171
172 if (st != NOTMUCH_STATUS_SUCCESS)
173 {
174 db = NULL;
175 }
176
177 if (verbose)
178 {
179 if (!db)
180 {
181 if (msg)
182 {
183 mutt_error("%s", msg);
184 }
185 else
186 {
187 mutt_error(_("Can't open notmuch database: %s: %s"), filename,
188 st ? notmuch_status_to_string(st) : _("unknown reason"));
189 }
190 }
191 else if (ct > 1)
192 {
194 }
195 }
196
197 FREE(&msg);
198
199 return db;
200}
201
208notmuch_database_t *nm_db_get(struct Mailbox *m, bool writable)
209{
210 struct NmAccountData *adata = nm_adata_get(m);
211
212 if (!adata)
213 return NULL;
214
215 // Use an existing open db if we have one.
216 if (adata->db)
217 return adata->db;
218
219 const char *db_filename = nm_db_get_filename(m);
220 if (db_filename)
221 adata->db = nm_db_do_open(db_filename, writable, true);
222
223 return adata->db;
224}
225
232int nm_db_release(struct Mailbox *m)
233{
234 struct NmAccountData *adata = nm_adata_get(m);
235 if (!adata || !adata->db || nm_db_is_longrun(m))
236 return -1;
237
238 mutt_debug(LL_DEBUG1, "nm: db close\n");
239 nm_db_free(adata->db);
240 adata->db = NULL;
241 adata->longrun = false;
242 return 0;
243}
244
249void nm_db_free(notmuch_database_t *db)
250{
251#ifdef NOTMUCH_API_3
252 notmuch_database_destroy(db);
253#else
254 notmuch_database_close(db);
255#endif
256}
257
266{
267 struct NmAccountData *adata = nm_adata_get(m);
268 if (!adata || !adata->db)
269 return -1;
270
271 if (adata->trans)
272 return 0;
273
274 mutt_debug(LL_DEBUG2, "nm: db trans start\n");
275 if (notmuch_database_begin_atomic(adata->db))
276 return -1;
277 adata->trans = true;
278 return 1;
279}
280
288{
289 struct NmAccountData *adata = nm_adata_get(m);
290 if (!adata || !adata->db)
291 return -1;
292
293 if (!adata->trans)
294 return 0;
295
296 mutt_debug(LL_DEBUG2, "nm: db trans end\n");
297 adata->trans = false;
298 if (notmuch_database_end_atomic(adata->db))
299 return -1;
300
301 return 0;
302}
303
314int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
315{
316 if (!m || !mtime)
317 return -1;
318
319 struct stat st = { 0 };
320 char path[PATH_MAX] = { 0 };
321 const char *db_filename = nm_db_get_filename(m);
322
323 mutt_debug(LL_DEBUG2, "nm: checking database mtime '%s'\n", db_filename);
324
325 // See if the path we were given has a Xapian directory.
326 // After notmuch 0.32, a .notmuch folder isn't guaranteed.
327 snprintf(path, sizeof(path), "%s/xapian", db_filename);
328 if (stat(path, &st) == 0)
329 {
330 *mtime = st.st_mtime;
331 return 0;
332 }
333
334 // Otherwise, check for a .notmuch directory.
335 snprintf(path, sizeof(path), "%s/.notmuch/xapian", db_filename);
336
337 if (stat(path, &st) != 0)
338 return -1;
339
340 *mtime = st.st_mtime;
341 return 0;
342}
343
350{
351 struct NmAccountData *adata = nm_adata_get(m);
352 if (!adata)
353 return false;
354
355 return adata->longrun;
356}
357
363void nm_db_longrun_init(struct Mailbox *m, bool writable)
364{
365 struct NmAccountData *adata = nm_adata_get(m);
366
367 if (!(adata && nm_db_get(m, writable)))
368 return;
369
370 adata->longrun = true;
371 mutt_debug(LL_DEBUG2, "nm: long run initialized\n");
372}
373
379{
380 struct NmAccountData *adata = nm_adata_get(m);
381
382 if (adata)
383 {
384 adata->longrun = false; /* to force nm_db_release() released DB */
385 if (nm_db_release(m) == 0)
386 mutt_debug(LL_DEBUG2, "nm: long run deinitialized\n");
387 else
388 adata->longrun = true;
389 }
390}
391
397{
398 struct NmAccountData *adata = nm_adata_get(m);
399 if (!adata || !adata->db)
400 return;
401
402 mutt_debug(LL_DEBUG1, "nm: ERROR: db is open, closing\n");
403 nm_db_release(m);
404}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:292
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:169
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
Structs that make up an email.
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
enum MailboxType nm_path_probe(const char *path, const struct stat *st)
Is this a Notmuch Mailbox? - Implements MxOps::path_probe() -.
Definition: notmuch.c:2457
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define FREE(x)
Definition: memory.h:45
void mutt_date_sleep_ms(size_t ms)
Sleep for milliseconds.
Definition: date.c:982
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:654
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:419
#define PATH_MAX
Definition: mutt.h:42
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:71
notmuch_database_t * nm_db_get(struct Mailbox *m, bool writable)
Get the Notmuch database.
Definition: db.c:208
int nm_db_trans_begin(struct Mailbox *m)
Start a Notmuch database transaction.
Definition: db.c:265
notmuch_database_t * nm_db_do_open(const char *filename, bool writable, bool verbose)
Open a Notmuch database.
Definition: db.c:114
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition: db.c:378
const char * nm_db_get_filename(struct Mailbox *m)
Get the filename of the Notmuch database.
Definition: db.c:57
int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
Get the database modification time.
Definition: db.c:314
int nm_db_release(struct Mailbox *m)
Close the Notmuch database.
Definition: db.c:232
bool nm_db_is_longrun(struct Mailbox *m)
Is Notmuch in the middle of a long-running transaction.
Definition: db.c:349
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition: db.c:363
void nm_db_debug_check(struct Mailbox *m)
Check if the database is open.
Definition: db.c:396
void nm_db_free(notmuch_database_t *db)
Decoupled way to close a Notmuch database.
Definition: db.c:249
int nm_db_trans_end(struct Mailbox *m)
End a database transaction.
Definition: db.c:287
struct NmMboxData * nm_mdata_get(struct Mailbox *m)
Get the Notmuch Mailbox data.
Definition: mdata.c:96
Notmuch-specific Mailbox data.
const int NmUrlProtocolLen
Length of NmUrlProtocol string.
Definition: notmuch.c:103
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:42
A mailbox.
Definition: mailbox.h:79
void * mdata
Driver specific data.
Definition: mailbox.h:132
bool verbose
Display status messages?
Definition: mailbox.h:117
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
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