NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
bcache.c
Go to the documentation of this file.
1
23
29
30#include "config.h"
31#include <dirent.h>
32#include <errno.h>
33#include <stdio.h>
34#include <string.h>
35#include <sys/stat.h>
36#include <unistd.h>
37#include "mutt/lib.h"
38#include "config/lib.h"
39#include "email/lib.h"
40#include "core/lib.h"
41#include "conn/lib.h"
42#include "lib.h"
43#include "muttlib.h"
44
49{
50 char *path;
51};
52
61static int bcache_path(struct ConnAccount *account, const char *mailbox, struct BodyCache *bcache)
62{
63 const char *const c_message_cache_dir = cs_subset_path(NeoMutt->sub, "message_cache_dir");
64 if (!account || !c_message_cache_dir || !bcache)
65 return -1;
66
67 struct stat st = { 0 };
68 if ((stat(c_message_cache_dir, &st) == 0) && !S_ISDIR(st.st_mode))
69 {
70 mutt_error(_("Cache disabled, $message_cache_dir isn't a directory: %s"), c_message_cache_dir);
71 return -1;
72 }
73
74 struct Url url = { 0 };
75 struct Buffer *host = buf_pool_get();
76
77 /* make up a Url we can turn into a string */
78 account_to_url(account, &url);
79 /* account_to_url() just sets up some pointers;
80 * if this ever changes, we have a memleak here */
81 url.path = NULL;
82 if (url_tostring(&url, host->data, host->dsize, U_PATH) < 0)
83 {
84 mutt_debug(LL_DEBUG1, "URL to string failed\n");
85 buf_pool_release(&host);
86 return -1;
87 }
88 buf_fix_dptr(host);
89
90 /* A server-supplied mailbox name with '..' components would let the cache
91 * path escape $message_cache_dir. Replace them with '__' so the path stays
92 * inside the cache directory. imap_hcache_open() rejects these names for
93 * the header cache. */
94 for (const char *p = mailbox; p && *p;)
95 {
96 const char *slash = strchr(p, '/');
97 const size_t len = slash ? (size_t) (slash - p) : strlen(p);
98 if ((len == 2) && (p[0] == '.') && (p[1] == '.'))
99 buf_addstr(host, "__");
100 else
101 buf_addstr_n(host, p, len);
102 if (!slash)
103 break;
104 buf_addch(host, '/');
105 p = slash + 1;
106 }
107
108 struct Buffer *path = buf_pool_get();
109 struct Buffer *dst = buf_pool_get();
110 mutt_encode_path(path, buf_string(host));
111
112 buf_printf(dst, "%s/%s", c_message_cache_dir, buf_string(path));
113 if (*(dst->dptr - 1) != '/')
114 buf_addch(dst, '/');
115
116 mutt_debug(LL_DEBUG3, "path: '%s'\n", buf_string(dst));
117 bcache->path = buf_strdup(dst);
118
119 buf_pool_release(&host);
120 buf_pool_release(&path);
121 buf_pool_release(&dst);
122 return 0;
123}
124
133static int mutt_bcache_move(struct BodyCache *bcache, const char *id, const char *newid)
134{
135 if (!bcache || !id || (*id == '\0') || !newid || (*newid == '\0'))
136 return -1;
137
138 struct Buffer *path = buf_pool_get();
139 struct Buffer *newpath = buf_pool_get();
140
141 buf_printf(path, "%s%s", bcache->path, id);
142 buf_printf(newpath, "%s%s", bcache->path, newid);
143
144 mutt_debug(LL_DEBUG3, "bcache: mv: '%s' '%s'\n", buf_string(path), buf_string(newpath));
145
146 int rc = rename(buf_string(path), buf_string(newpath));
147 buf_pool_release(&path);
148 buf_pool_release(&newpath);
149 return rc;
150}
151
162struct BodyCache *mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
163{
164 if (!account)
165 return NULL;
166
167 struct BodyCache *bcache = MUTT_MEM_CALLOC(1, struct BodyCache);
168 if (bcache_path(account, mailbox, bcache) < 0)
169 {
170 mutt_bcache_close(&bcache);
171 return NULL;
172 }
173
174 return bcache;
175}
176
183void mutt_bcache_close(struct BodyCache **ptr)
184{
185 if (!ptr || !*ptr)
186 return;
187
188 struct BodyCache *bcache = *ptr;
189 FREE(&bcache->path);
190
191 FREE(ptr);
192}
193
201FILE *mutt_bcache_get(struct BodyCache *bcache, const char *id)
202{
203 if (!id || (*id == '\0') || !bcache)
204 return NULL;
205
206 struct Buffer *path = buf_pool_get();
207 buf_addstr(path, bcache->path);
208 buf_addstr(path, id);
209
210 FILE *fp = mutt_file_fopen(buf_string(path), "r");
211
212 mutt_debug(LL_DEBUG3, "bcache: get: '%s': %s\n", buf_string(path), fp ? "yes" : "no");
213
214 buf_pool_release(&path);
215 return fp;
216}
217
228FILE *mutt_bcache_put(struct BodyCache *bcache, const char *id)
229{
230 if (!id || (*id == '\0') || !bcache)
231 return NULL;
232
233 struct Buffer *path = buf_pool_get();
234 buf_printf(path, "%s%s%s", bcache->path, id, ".tmp");
235
236 struct stat st = { 0 };
237 if (stat(bcache->path, &st) == 0)
238 {
239 if (!S_ISDIR(st.st_mode))
240 {
241 mutt_error(_("Message cache isn't a directory: %s"), bcache->path);
242 buf_pool_release(&path);
243 return NULL;
244 }
245 }
246 else
247 {
248 if (mutt_file_mkdir(bcache->path, S_IRWXU) < 0)
249 {
250 mutt_error(_("Can't create %s: %s"), bcache->path, strerror(errno));
251 buf_pool_release(&path);
252 return NULL;
253 }
254 }
255
256 mutt_debug(LL_DEBUG3, "bcache: put: '%s'\n", buf_string(path));
257
258 FILE *fp = mutt_file_fopen(buf_string(path), "w+");
259 buf_pool_release(&path);
260 return fp;
261}
262
270int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
271{
272 if (!id || (*id == '\0') || !bcache)
273 return -1;
274
275 struct Buffer *tmpid = buf_pool_get();
276 buf_printf(tmpid, "%s.tmp", id);
277
278 int rc = mutt_bcache_move(bcache, buf_string(tmpid), id);
279 buf_pool_release(&tmpid);
280 return rc;
281}
282
290int mutt_bcache_del(struct BodyCache *bcache, const char *id)
291{
292 if (!id || (*id == '\0') || !bcache)
293 return -1;
294
295 struct Buffer *path = buf_pool_get();
296 buf_addstr(path, bcache->path);
297 buf_addstr(path, id);
298
299 mutt_debug(LL_DEBUG3, "bcache: del: '%s'\n", buf_string(path));
300
301 int rc = unlink(buf_string(path));
302 buf_pool_release(&path);
303 return rc;
304}
305
313int mutt_bcache_exists(struct BodyCache *bcache, const char *id)
314{
315 if (!id || (*id == '\0') || !bcache)
316 return -1;
317
318 struct Buffer *path = buf_pool_get();
319 buf_addstr(path, bcache->path);
320 buf_addstr(path, id);
321
322 int rc = 0;
323 struct stat st = { 0 };
324 if (stat(buf_string(path), &st) < 0)
325 rc = -1;
326 else
327 rc = (S_ISREG(st.st_mode) && (st.st_size != 0)) ? 0 : -1;
328
329 mutt_debug(LL_DEBUG3, "bcache: exists: '%s': %s\n", buf_string(path),
330 (rc == 0) ? "yes" : "no");
331
332 buf_pool_release(&path);
333 return rc;
334}
335
355int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
356{
357 DIR *dir = NULL;
358 struct dirent *de = NULL;
359 int rc = -1;
360
361 if (!bcache || !(dir = mutt_file_opendir(bcache->path, MUTT_OPENDIR_NONE)))
362 goto out;
363
364 rc = 0;
365
366 mutt_debug(LL_DEBUG3, "bcache: list: dir: '%s'\n", bcache->path);
367
368 while ((de = readdir(dir)))
369 {
370 if (mutt_str_startswith(de->d_name, ".") || mutt_str_startswith(de->d_name, ".."))
371 {
372 continue;
373 }
374
375 mutt_debug(LL_DEBUG3, "bcache: list: dir: '%s', id :'%s'\n", bcache->path, de->d_name);
376
377 if (want_id && (want_id(de->d_name, bcache, data) != 0))
378 goto out;
379
380 rc++;
381 }
382
383out:
384 if (dir)
385 {
386 if (closedir(dir) < 0)
387 rc = -1;
388 }
389 mutt_debug(LL_DEBUG3, "bcache: list: did %d entries\n", rc);
390 return rc;
391}
Body Caching (local copies of email bodies)
int(* bcache_list_t)(const char *id, struct BodyCache *bcache, void *data)
Definition lib.h:55
int mutt_bcache_exists(struct BodyCache *bcache, const char *id)
Check if a file exists in the Body Cache.
Definition bcache.c:313
int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
Move a temporary file into the Body Cache.
Definition bcache.c:270
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition bcache.c:162
static int bcache_path(struct ConnAccount *account, const char *mailbox, struct BodyCache *bcache)
Create the cache path for a given account/mailbox.
Definition bcache.c:61
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition bcache.c:355
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition bcache.c:201
void mutt_bcache_close(struct BodyCache **ptr)
Close an Email-Body Cache.
Definition bcache.c:183
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition bcache.c:290
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition bcache.c:228
static int mutt_bcache_move(struct BodyCache *bcache, const char *id, const char *newid)
Change the id of a message in the cache.
Definition bcache.c:133
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
size_t buf_addstr_n(struct Buffer *buf, const char *s, size_t len)
Add a string to a Buffer, expanding it if necessary.
Definition buffer.c:109
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:189
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
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.
Connection Library.
Convenience wrapper for the core headers.
Structs that make up an email.
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition file.c:844
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition file.c:535
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition file.h:68
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
#define mutt_error(...)
Definition logging2.h:94
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
@ 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
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
void account_to_url(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition muttlib.c:803
Some miscellaneous functions.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
Local cache of email bodies.
Definition bcache.c:49
char * path
On-disk path to the file.
Definition bcache.c:50
String manipulation buffer.
Definition buffer.h:36
char * dptr
Current read/write position.
Definition buffer.h:38
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
Login details for a remote server.
Definition connaccount.h:59
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition url.h:69
char * path
Path.
Definition url.h:75
int url_tostring(const struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition url.c:426
#define U_PATH
Path is included in URL.
Definition url.h:50