NeoMutt  2021-10-29-225-gb9986f
Teaching an old dog new tricks
DOXYGEN
bcache.c
Go to the documentation of this file.
1 
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 "lib.h"
42 #include "mutt_account.h"
43 #include "muttlib.h"
44 
45 struct ConnAccount;
46 
50 struct BodyCache
51 {
52  char *path;
53 };
54 
63 static int bcache_path(struct ConnAccount *account, const char *mailbox, struct BodyCache *bcache)
64 {
65  char host[256];
66  struct Url url = { 0 };
67 
68  const char *const c_message_cachedir =
69  cs_subset_path(NeoMutt->sub, "message_cachedir");
70  if (!account || !c_message_cachedir || !bcache)
71  return -1;
72 
73  struct stat st = { 0 };
74  if (!((stat(c_message_cachedir, &st) == 0) && S_ISDIR(st.st_mode)))
75  {
76  mutt_error(_("Cache disabled, $message_cachedir isn't a directory: %s"), c_message_cachedir);
77  return -1;
78  }
79 
80  /* make up a Url we can turn into a string */
81  mutt_account_tourl(account, &url);
82  /* mutt_account_tourl() just sets up some pointers;
83  * if this ever changes, we have a memleak here */
84  url.path = NULL;
85  if (url_tostring(&url, host, sizeof(host), U_PATH) < 0)
86  {
87  mutt_debug(LL_DEBUG1, "URL to string failed\n");
88  return -1;
89  }
90 
91  struct Buffer *path = mutt_buffer_pool_get();
92  struct Buffer *dst = mutt_buffer_pool_get();
93  mutt_encode_path(path, NONULL(mailbox));
94 
95  mutt_buffer_printf(dst, "%s/%s%s", c_message_cachedir, host, mutt_buffer_string(path));
96  if (*(dst->dptr - 1) != '/')
97  mutt_buffer_addch(dst, '/');
98 
99  mutt_debug(LL_DEBUG3, "path: '%s'\n", mutt_buffer_string(dst));
100  bcache->path = mutt_buffer_strdup(dst);
101 
104  return 0;
105 }
106 
115 static int mutt_bcache_move(struct BodyCache *bcache, const char *id, const char *newid)
116 {
117  if (!bcache || !id || (*id == '\0') || !newid || (*newid == '\0'))
118  return -1;
119 
120  struct Buffer *path = mutt_buffer_pool_get();
121  struct Buffer *newpath = mutt_buffer_pool_get();
122 
123  mutt_buffer_printf(path, "%s%s", bcache->path, id);
124  mutt_buffer_printf(newpath, "%s%s", bcache->path, newid);
125 
126  mutt_debug(LL_DEBUG3, "bcache: mv: '%s' '%s'\n", mutt_buffer_string(path),
127  mutt_buffer_string(newpath));
128 
129  int rc = rename(mutt_buffer_string(path), mutt_buffer_string(newpath));
131  mutt_buffer_pool_release(&newpath);
132  return rc;
133 }
134 
145 struct BodyCache *mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
146 {
147  if (!account)
148  return NULL;
149 
150  struct BodyCache *bcache = mutt_mem_calloc(1, sizeof(struct BodyCache));
151  if (bcache_path(account, mailbox, bcache) < 0)
152  {
153  mutt_bcache_close(&bcache);
154  return NULL;
155  }
156 
157  return bcache;
158 }
159 
166 void mutt_bcache_close(struct BodyCache **bcache)
167 {
168  if (!bcache || !*bcache)
169  return;
170  FREE(&(*bcache)->path);
171  FREE(bcache);
172 }
173 
181 FILE *mutt_bcache_get(struct BodyCache *bcache, const char *id)
182 {
183  if (!id || (*id == '\0') || !bcache)
184  return NULL;
185 
186  struct Buffer *path = mutt_buffer_pool_get();
187  mutt_buffer_addstr(path, bcache->path);
188  mutt_buffer_addstr(path, id);
189 
190  FILE *fp = mutt_file_fopen(mutt_buffer_string(path), "r");
191 
192  mutt_debug(LL_DEBUG3, "bcache: get: '%s': %s\n", mutt_buffer_string(path),
193  fp ? "yes" : "no");
194 
196  return fp;
197 }
198 
209 FILE *mutt_bcache_put(struct BodyCache *bcache, const char *id)
210 {
211  if (!id || (*id == '\0') || !bcache)
212  return NULL;
213 
214  struct Buffer *path = mutt_buffer_pool_get();
215  mutt_buffer_printf(path, "%s%s%s", bcache->path, id, ".tmp");
216 
217  struct stat st = { 0 };
218  if (stat(bcache->path, &st) == 0)
219  {
220  if (!S_ISDIR(st.st_mode))
221  {
222  mutt_error(_("Message cache isn't a directory: %s"), bcache->path);
223  return NULL;
224  }
225  }
226  else
227  {
228  if (mutt_file_mkdir(bcache->path, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
229  {
230  mutt_error(_("Can't create %s: %s"), bcache->path, strerror(errno));
231  return NULL;
232  }
233  }
234 
235  mutt_debug(LL_DEBUG3, "bcache: put: '%s'\n", path);
236 
237  FILE *fp = mutt_file_fopen(mutt_buffer_string(path), "w+");
239  return fp;
240 }
241 
249 int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
250 {
251  struct Buffer *tmpid = mutt_buffer_pool_get();
252  mutt_buffer_printf(tmpid, "%s.tmp", id);
253 
254  int rc = mutt_bcache_move(bcache, mutt_buffer_string(tmpid), id);
255  mutt_buffer_pool_release(&tmpid);
256  return rc;
257 }
258 
266 int mutt_bcache_del(struct BodyCache *bcache, const char *id)
267 {
268  if (!id || (*id == '\0') || !bcache)
269  return -1;
270 
271  struct Buffer *path = mutt_buffer_pool_get();
272  mutt_buffer_addstr(path, bcache->path);
273  mutt_buffer_addstr(path, id);
274 
275  mutt_debug(LL_DEBUG3, "bcache: del: '%s'\n", mutt_buffer_string(path));
276 
277  int rc = unlink(mutt_buffer_string(path));
279  return rc;
280 }
281 
289 int mutt_bcache_exists(struct BodyCache *bcache, const char *id)
290 {
291  if (!id || (*id == '\0') || !bcache)
292  return -1;
293 
294  struct Buffer *path = mutt_buffer_pool_get();
295  mutt_buffer_addstr(path, bcache->path);
296  mutt_buffer_addstr(path, id);
297 
298  int rc = 0;
299  struct stat st = { 0 };
300  if (stat(mutt_buffer_string(path), &st) < 0)
301  rc = -1;
302  else
303  rc = (S_ISREG(st.st_mode) && (st.st_size != 0)) ? 0 : -1;
304 
305  mutt_debug(LL_DEBUG3, "bcache: exists: '%s': %s\n", mutt_buffer_string(path),
306  (rc == 0) ? "yes" : "no");
307 
309  return rc;
310 }
311 
331 int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
332 {
333  DIR *d = NULL;
334  struct dirent *de = NULL;
335  int rc = -1;
336 
337  if (!bcache || !(d = opendir(bcache->path)))
338  goto out;
339 
340  rc = 0;
341 
342  mutt_debug(LL_DEBUG3, "bcache: list: dir: '%s'\n", bcache->path);
343 
344  while ((de = readdir(d)))
345  {
346  if (mutt_str_startswith(de->d_name, ".") || mutt_str_startswith(de->d_name, ".."))
347  {
348  continue;
349  }
350 
351  mutt_debug(LL_DEBUG3, "bcache: list: dir: '%s', id :'%s'\n", bcache->path, de->d_name);
352 
353  if (want_id && (want_id(de->d_name, bcache, data) != 0))
354  goto out;
355 
356  rc++;
357  }
358 
359 out:
360  if (d)
361  {
362  if (closedir(d) < 0)
363  rc = -1;
364  }
365  mutt_debug(LL_DEBUG3, "bcache: list: did %d entries\n", rc);
366  return rc;
367 }
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:289
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:145
int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
Move a temporary file into the Body Cache.
Definition: bcache.c:249
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:63
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:331
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:266
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition: bcache.c:209
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition: bcache.c:181
void mutt_bcache_close(struct BodyCache **bcache)
Close an Email-Body Cache.
Definition: bcache.c:166
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:115
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:432
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Convenience wrapper for the config headers.
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:905
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:593
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:40
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:158
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
ConnAccount object used by POP and IMAP.
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:1491
Some miscellaneous functions.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
Key value store.
#define NONULL(x)
Definition: string2.h:37
Local cache of email bodies.
Definition: bcache.c:51
char * path
On-disk path to the file.
Definition: bcache.c:52
String manipulation buffer.
Definition: buffer.h:34
char * dptr
Current read/write position.
Definition: buffer.h:36
Login details for a remote server.
Definition: connaccount.h:53
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
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(struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:418
#define U_PATH
Definition: url.h:50