NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
bcache.c File Reference

Body Caching (local copies of email bodies) More...

#include "config.h"
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "lib.h"
#include "muttlib.h"
+ Include dependency graph for bcache.c:

Go to the source code of this file.

Data Structures

struct  BodyCache
 Local cache of email bodies. More...
 

Functions

static int bcache_path (struct ConnAccount *account, const char *mailbox, struct BodyCache *bcache)
 Create the cache path for a given account/mailbox.
 
static int mutt_bcache_move (struct BodyCache *bcache, const char *id, const char *newid)
 Change the id of a message in the cache.
 
struct BodyCachemutt_bcache_open (struct ConnAccount *account, const char *mailbox)
 Open an Email-Body Cache.
 
void mutt_bcache_close (struct BodyCache **ptr)
 Close an Email-Body Cache.
 
FILE * mutt_bcache_get (struct BodyCache *bcache, const char *id)
 Open a file in the Body Cache.
 
FILE * mutt_bcache_put (struct BodyCache *bcache, const char *id)
 Create a file in the Body Cache.
 
int mutt_bcache_commit (struct BodyCache *bcache, const char *id)
 Move a temporary file into the Body Cache.
 
int mutt_bcache_del (struct BodyCache *bcache, const char *id)
 Delete a file from the Body Cache.
 
int mutt_bcache_exists (struct BodyCache *bcache, const char *id)
 Check if a file exists in the Body Cache.
 
int mutt_bcache_list (struct BodyCache *bcache, bcache_list_t want_id, void *data)
 Find matching entries in the Body Cache.
 

Detailed Description

Body Caching (local copies of email bodies)

Authors
  • Richard Russon
  • Pietro Cerutti

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file bcache.c.

Function Documentation

◆ bcache_path()

static int bcache_path ( struct ConnAccount * account,
const char * mailbox,
struct BodyCache * bcache )
static

Create the cache path for a given account/mailbox.

Parameters
accountAccount info
mailboxMailbox name
bcacheBody cache
Return values
0Success
-1Failure

Definition at line 61 of file bcache.c.

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}
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
#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 _(a)
Definition message.h:28
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
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
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
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bcache_move()

static int mutt_bcache_move ( struct BodyCache * bcache,
const char * id,
const char * newid )
static

Change the id of a message in the cache.

Parameters
bcacheBody cache
idPer-mailbox unique identifier for the message
newidNew id for the message
Return values
0Success
-1Error

Definition at line 133 of file bcache.c.

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}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bcache_open()

struct BodyCache * mutt_bcache_open ( struct ConnAccount * account,
const char * mailbox )

Open an Email-Body Cache.

Parameters
accountcurrent mailbox' account (required)
mailboxpath to the mailbox of the account (optional)
Return values
NULLFailure

The driver using it is responsible for ensuring that hierarchies are separated by '/' (if it knows of such a concepts like mailboxes or hierarchies)

Definition at line 162 of file bcache.c.

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}
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
void mutt_bcache_close(struct BodyCache **ptr)
Close an Email-Body Cache.
Definition bcache.c:183
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
Local cache of email bodies.
Definition bcache.c:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bcache_close()

void mutt_bcache_close ( struct BodyCache ** ptr)

Close an Email-Body Cache.

Parameters
[out]ptrBody cache

Free all memory of bcache and finally FREE() it, too.

Definition at line 183 of file bcache.c.

184{
185 if (!ptr || !*ptr)
186 return;
187
188 struct BodyCache *bcache = *ptr;
189 FREE(&bcache->path);
190
191 FREE(ptr);
192}
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
+ Here is the caller graph for this function:

◆ mutt_bcache_get()

FILE * mutt_bcache_get ( struct BodyCache * bcache,
const char * id )

Open a file in the Body Cache.

Parameters
bcacheBody Cache from mutt_bcache_open()
idPer-mailbox unique identifier for the message
Return values
ptrSuccess
NULLFailure

Definition at line 201 of file bcache.c.

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}
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bcache_put()

FILE * mutt_bcache_put ( struct BodyCache * bcache,
const char * id )

Create a file in the Body Cache.

Parameters
bcacheBody Cache from mutt_bcache_open()
idPer-mailbox unique identifier for the message
Return values
ptrSuccess
NULLFailure

The returned FILE* is in a temporary location. Use mutt_bcache_commit to put it into place

Definition at line 228 of file bcache.c.

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}
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition file.c:844
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bcache_commit()

int mutt_bcache_commit ( struct BodyCache * bcache,
const char * id )

Move a temporary file into the Body Cache.

Parameters
bcacheBody Cache from mutt_bcache_open()
idPer-mailbox unique identifier for the message
Return values
0Success
-1Failure

Definition at line 270 of file bcache.c.

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}
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bcache_del()

int mutt_bcache_del ( struct BodyCache * bcache,
const char * id )

Delete a file from the Body Cache.

Parameters
bcacheBody Cache from mutt_bcache_open()
idPer-mailbox unique identifier for the message
Return values
0Success
-1Failure

Definition at line 290 of file bcache.c.

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}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bcache_exists()

int mutt_bcache_exists ( struct BodyCache * bcache,
const char * id )

Check if a file exists in the Body Cache.

Parameters
bcacheBody Cache from mutt_bcache_open()
idPer-mailbox unique identifier for the message
Return values
0Success
-1Failure

Definition at line 313 of file bcache.c.

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}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_bcache_list()

int mutt_bcache_list ( struct BodyCache * bcache,
bcache_list_t want_id,
void * data )

Find matching entries in the Body Cache.

Parameters
bcacheBody Cache from mutt_bcache_open()
want_idCallback function called for each match
dataData to pass to the callback function
Return values
-1Failure
>=0count of matching items

This more or less "examines" the cache and calls a function with each id it finds if given.

The optional callback function gets the id of a message, the very same body cache handle mutt_bcache_list() is called with (to, perhaps, perform further operations on the bcache), and a data cookie which is just passed as-is. If the return value of the callback is non-zero, the listing is aborted and continued otherwise. The callback is optional so that this function can be used to count the items in the cache (see below for return value).

Definition at line 355 of file bcache.c.

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}
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
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
+ Here is the call graph for this function:
+ Here is the caller graph for this function: