NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
maildir.c File Reference

Maildir local mailbox type. More...

#include "config.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <utime.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "lib.h"
#include "progress/lib.h"
#include "copy.h"
#include "edata.h"
#include "globals.h"
#include "mdata.h"
#include "mdemail.h"
#include "mx.h"
#include "sort.h"
#include "monitor.h"
#include "hcache/lib.h"
#include "notmuch/lib.h"
+ Include dependency graph for maildir.c:

Go to the source code of this file.

Macros

#define MMC_NO_DIRS   0
 No directories changed.
 
#define MMC_NEW_DIR   (1 << 0)
 'new' directory changed
 
#define MMC_CUR_DIR   (1 << 1)
 'cur' directory changed
 

Functions

struct Emailmaildir_email_new (void)
 Create a Maildir Email.
 
static void maildir_check_dir (struct Mailbox *m, const char *dir_name, bool check_new, bool check_stats)
 Check for new mail / mail counts.
 
static int maildir_sort_flags (const void *a, const void *b, void *sdata)
 Compare two flag characters - Implements sort_t -.
 
void maildir_gen_flags (char *dest, size_t destlen, struct Email *e)
 Generate the Maildir flags for an email.
 
static int maildir_commit_message (struct Mailbox *m, struct Message *msg, struct Email *e)
 Commit a message to a maildir folder.
 
static int maildir_rewrite_message (struct Mailbox *m, struct Email *e)
 Sync a message in an MH folder.
 
static int maildir_sync_message (struct Mailbox *m, struct Email *e)
 Sync an email to a Maildir folder.
 
static void maildir_update_mtime (struct Mailbox *m)
 Update our record of the Maildir modification time.
 
static int maildir_sort_inode (const void *a, const void *b, void *sdata)
 Compare two Maildirs by inode number - Implements sort_t -.
 
static int maildir_parse_dir (struct Mailbox *m, struct MdEmailArray *mda, const char *subdir, struct Progress *progress)
 Read a Maildir mailbox.
 
static size_t maildir_hcache_keylen (const char *fn)
 Calculate the length of the Maildir path.
 
static void maildir_delayed_parsing (struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
 This function does the second parsing pass.
 
static int maildir_read_dir (struct Mailbox *m, const char *subdir)
 Read a Maildir style mailbox.
 
static void maildir_canon_filename (struct Buffer *dest, const char *src)
 Generate the canonical filename for a Maildir folder.
 
static FILE * maildir_open_find_message_dir (const char *folder, const char *unique, const char *subfolder, char **newname)
 Find a message in a maildir folder.
 
void maildir_parse_flags (struct Email *e, const char *path)
 Parse Maildir file flags.
 
bool maildir_parse_stream (enum MailboxType type, FILE *fp, const char *fname, bool is_old, struct Email *e)
 Parse a Maildir message.
 
bool maildir_parse_message (enum MailboxType type, const char *fname, bool is_old, struct Email *e)
 Actually parse a maildir message.
 
bool maildir_sync_mailbox_message (struct Mailbox *m, struct Email *e, struct HeaderCache *hc)
 Save changes to the mailbox.
 
FILE * maildir_open_find_message (const char *folder, const char *msg, char **newname)
 Find a message by name.
 
int maildir_check_empty (struct Buffer *path)
 Is the mailbox empty.
 
static bool maildir_ac_owns_path (struct Account *a, const char *path)
 Check whether an Account own a Mailbox path - Implements MxOps::ac_owns_path() -.
 
static bool maildir_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -.
 
static enum MxOpenReturns maildir_mbox_open (struct Mailbox *m)
 Open a Mailbox - Implements MxOps::mbox_open() -.
 
static bool maildir_mbox_open_append (struct Mailbox *m, OpenMailboxFlags flags)
 Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
 
static enum MxStatus maildir_check (struct Mailbox *m)
 Check for new mail.
 
static enum MxStatus maildir_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -.
 
static enum MxStatus maildir_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -.
 
static enum MxStatus maildir_mbox_sync (struct Mailbox *m)
 Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
 
static enum MxStatus maildir_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -.
 
static bool maildir_msg_open (struct Mailbox *m, struct Message *msg, struct Email *e)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -.
 
bool maildir_msg_open_new (struct Mailbox *m, struct Message *msg, const struct Email *e)
 Open a new message in a Mailbox - Implements MxOps::msg_open_new() -.
 
static int maildir_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -.
 
static int maildir_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -.
 
static int maildir_msg_save_hcache (struct Mailbox *m, struct Email *e)
 Save message to the header cache - Implements MxOps::msg_save_hcache() -.
 
static int maildir_path_canon (struct Buffer *path)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
 
static int maildir_path_parent (struct Buffer *path)
 Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
 
static int maildir_path_pretty (struct Buffer *path, const char *folder)
 Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
 
static enum MailboxType maildir_path_probe (const char *path, const struct stat *st)
 Is this a Maildir Mailbox? - Implements MxOps::path_probe() -.
 

Variables

const struct MxOps MxMaildirOps
 Maildir Mailbox - Implements MxOps -.
 

Detailed Description

Maildir local mailbox type.

Authors
  • Michael R. Elkins
  • Thomas Roessler
  • Michael R. Elkins
  • Richard Russon

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 maildir.c.

Macro Definition Documentation

◆ MMC_NO_DIRS

#define MMC_NO_DIRS   0

No directories changed.

Definition at line 73 of file maildir.c.

◆ MMC_NEW_DIR

#define MMC_NEW_DIR   (1 << 0)

'new' directory changed

Definition at line 74 of file maildir.c.

◆ MMC_CUR_DIR

#define MMC_CUR_DIR   (1 << 1)

'cur' directory changed

Definition at line 75 of file maildir.c.

Function Documentation

◆ maildir_email_new()

struct Email * maildir_email_new ( void  )

Create a Maildir Email.

Return values
ptrNewly created Email

Create a new Email and attach MaildirEmailData.

Note
This should be freed using email_free()

Definition at line 85 of file maildir.c.

86{
87 struct Email *e = email_new();
90
91 return e;
92}
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
void maildir_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
struct MaildirEmailData * maildir_edata_new(void)
Create a new MaildirEmailData object.
Definition: edata.c:53
The envelope/body of an email.
Definition: email.h:37
void * edata
Driver-specific data.
Definition: email.h:72
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:86
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_check_dir()

static void maildir_check_dir ( struct Mailbox m,
const char *  dir_name,
bool  check_new,
bool  check_stats 
)
static

Check for new mail / mail counts.

Parameters
mMailbox to check
dir_namePath to Mailbox
check_newif true, check for new mail
check_statsif true, count total, new, and flagged messages

Checks the specified maildir subdir (cur or new) for new mail or mail counts.

Definition at line 103 of file maildir.c.

105{
106 DIR *dir = NULL;
107 struct dirent *de = NULL;
108 char *p = NULL;
109 struct stat st = { 0 };
110
111 struct Buffer *path = buf_pool_get();
112 struct Buffer *msgpath = buf_pool_get();
113 buf_printf(path, "%s/%s", mailbox_path(m), dir_name);
114
115 /* when $mail_check_recent is set, if the new/ directory hasn't been modified since
116 * the user last exited the mailbox, then we know there is no recent mail. */
117 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
118 if (check_new && c_mail_check_recent)
119 {
120 if ((stat(buf_string(path), &st) == 0) &&
122 {
123 check_new = false;
124 }
125 }
126
127 if (!(check_new || check_stats))
128 goto cleanup;
129
131 if (!dir)
132 {
133 m->type = MUTT_UNKNOWN;
134 goto cleanup;
135 }
136
137 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
138
139 char delimiter_version[8] = { 0 };
140 snprintf(delimiter_version, sizeof(delimiter_version), "%c2,", c_maildir_field_delimiter);
141 while ((de = readdir(dir)))
142 {
143 if (*de->d_name == '.')
144 continue;
145
146 p = strstr(de->d_name, delimiter_version);
147 if (p && strchr(p + 3, 'T'))
148 continue;
149
150 if (check_stats)
151 {
152 m->msg_count++;
153 if (p && strchr(p + 3, 'F'))
154 m->msg_flagged++;
155 }
156 if (!p || !strchr(p + 3, 'S'))
157 {
158 if (check_stats)
159 m->msg_unread++;
160 if (check_new)
161 {
162 if (c_mail_check_recent)
163 {
164 buf_printf(msgpath, "%s/%s", buf_string(path), de->d_name);
165 /* ensure this message was received since leaving this m */
166 if ((stat(buf_string(msgpath), &st) == 0) &&
168 {
169 continue;
170 }
171 }
172 m->has_new = true;
173 if (check_stats)
174 {
175 m->msg_new++;
176 }
177 else
178 {
179 break;
180 }
181 }
182 }
183 }
184
185 closedir(dir);
186
187cleanup:
188 buf_pool_release(&path);
189 buf_pool_release(&msgpath);
190}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
const char * cc_maildir_field_delimiter(void)
Get the cached value of $maildir_field_delimiter.
Definition: config_cache.c:130
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:616
int mutt_file_stat_timespec_compare(struct stat *st, enum MuttStatType type, struct timespec *b)
Compare stat info with a time value.
Definition: file.c:1660
@ MUTT_OPENDIR_CREATE
Create the directory if it doesn't exist.
Definition: file.h:74
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition: file.h:65
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:44
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
String manipulation buffer.
Definition: buffer.h:34
bool has_new
Mailbox has new mail.
Definition: mailbox.h:85
int msg_new
Number of new messages.
Definition: mailbox.h:92
int msg_count
Total number of messages.
Definition: mailbox.h:88
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:90
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:104
int msg_unread
Number of unread messages.
Definition: mailbox.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_gen_flags()

void maildir_gen_flags ( char *  dest,
size_t  destlen,
struct Email e 
)

Generate the Maildir flags for an email.

Parameters
destBuffer for the result
destlenLength of buffer
eEmail

Definition at line 206 of file maildir.c.

207{
208 *dest = '\0';
209
210 const char *flags = NULL;
211
213 if (edata)
214 flags = edata->maildir_flags;
215
216 /* The maildir specification requires that all files in the cur
217 * subdirectory have the :unique string appended, regardless of whether
218 * or not there are any flags. If .old is set, we know that this message
219 * will end up in the cur directory, so we include it in the following
220 * test even though there is no associated flag. */
221
222 if (e->flagged || e->replied || e->read || e->deleted || e->old || flags)
223 {
224 char tmp[1024] = { 0 };
225 snprintf(tmp, sizeof(tmp), "%s%s%s%s%s", e->flagged ? "F" : "", e->replied ? "R" : "",
226 e->read ? "S" : "", e->deleted ? "T" : "", NONULL(flags));
227 if (flags)
228 mutt_qsort_r(tmp, strlen(tmp), 1, maildir_sort_flags, NULL);
229
230 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
231 snprintf(dest, destlen, "%c2,%s", c_maildir_field_delimiter, tmp);
232 }
233}
static int maildir_sort_flags(const void *a, const void *b, void *sdata)
Compare two flag characters - Implements sort_t -.
Definition: maildir.c:195
struct MaildirEmailData * maildir_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
void mutt_qsort_r(void *base, size_t nmemb, size_t size, sort_t compar, void *sdata)
Sort an array, where the comparator has access to opaque data rather than requiring global variables.
Definition: qsort_r.c:66
#define NONULL(x)
Definition: string2.h:37
bool read
Email is read.
Definition: email.h:48
bool old
Email is seen, but unread.
Definition: email.h:47
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
bool deleted
Email is deleted.
Definition: email.h:76
Maildir-specific Email data -.
Definition: edata.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_commit_message()

static int maildir_commit_message ( struct Mailbox m,
struct Message msg,
struct Email e 
)
static

Commit a message to a maildir folder.

Parameters
mMailbox
msgMessage to commit
eEmail
Return values
0Success
-1Failure

msg->path contains the file name of a file in tmp/. We take the flags from this file's name.

m is the mail folder we commit to.

e is a header structure to which we write the message's new file name. This is used in the mh and maildir folder sync routines. When this routine is invoked from mx_msg_commit(), e is NULL.

msg->path looks like this:

tmp/{cur,new}.neomutt-HOSTNAME-PID-COUNTER:flags

See also maildir_msg_open_new().

Definition at line 259 of file maildir.c.

260{
261 char subdir[4] = { 0 };
262 char suffix[16] = { 0 };
263 int rc = 0;
264
265 if (mutt_file_fsync_close(&msg->fp))
266 {
267 mutt_perror(_("Could not flush message to disk"));
268 return -1;
269 }
270
271 /* extract the subdir */
272 char *s = strrchr(msg->path, '/') + 1;
273 mutt_str_copy(subdir, s, 4);
274
275 /* extract the flags */
276 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
277 s = strchr(s, c_maildir_field_delimiter);
278 if (s)
279 mutt_str_copy(suffix, s, sizeof(suffix));
280 else
281 suffix[0] = '\0';
282
283 /* construct a new file name. */
284 struct Buffer *path = buf_pool_get();
285 struct Buffer *full = buf_pool_get();
286 while (true)
287 {
288 buf_printf(path, "%s/%lld.R%" PRIu64 ".%s%s", subdir, (long long) mutt_date_now(),
289 mutt_rand64(), NONULL(ShortHostname), suffix);
290 buf_printf(full, "%s/%s", mailbox_path(m), buf_string(path));
291
292 mutt_debug(LL_DEBUG2, "renaming %s to %s\n", msg->path, buf_string(full));
293
294 if (mutt_file_safe_rename(msg->path, buf_string(full)) == 0)
295 {
296 /* Adjust the mtime on the file to match the time at which this
297 * message was received. Currently this is only set when copying
298 * messages between mailboxes, so we test to ensure that it is
299 * actually set. */
300 if (msg->received != 0)
301 {
302 struct utimbuf ut = { 0 };
303 int rc_utime;
304
305 ut.actime = msg->received;
306 ut.modtime = msg->received;
307 do
308 {
309 rc_utime = utime(buf_string(full), &ut);
310 } while ((rc_utime == -1) && (errno == EINTR));
311 if (rc_utime == -1)
312 {
313 mutt_perror(_("maildir_commit_message(): unable to set time on file"));
314 rc = -1;
315 goto cleanup;
316 }
317 }
318
319#ifdef USE_NOTMUCH
320 if (m->type == MUTT_NOTMUCH)
321 nm_update_filename(m, e->path, buf_string(full), e);
322#endif
323 if (e)
324 mutt_str_replace(&e->path, buf_string(path));
326 FREE(&msg->path);
327
328 goto cleanup;
329 }
330 else if (errno != EEXIST)
331 {
332 mutt_perror("%s", mailbox_path(m));
333 rc = -1;
334 goto cleanup;
335 }
336 }
337
338cleanup:
339 buf_pool_release(&path);
340 buf_pool_release(&full);
341
342 return rc;
343}
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:346
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:168
char * ShortHostname
Short version of the hostname.
Definition: globals.c:40
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
#define FREE(x)
Definition: memory.h:45
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:446
#define _(a)
Definition: message.h:28
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:653
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
int nm_update_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Change the filename.
Definition: notmuch.c:1749
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:134
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
FILE * fp
pointer to the message data
Definition: message.h:35
char * path
path to temp file
Definition: message.h:36
char * committed_path
the final path generated by mx_msg_commit()
Definition: message.h:37
time_t received
Time at which this message was received.
Definition: message.h:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_rewrite_message()

static int maildir_rewrite_message ( struct Mailbox m,
struct Email e 
)
static

Sync a message in an MH folder.

Parameters
mMailbox
eEmail
Return values
0Success
-1Error

Definition at line 352 of file maildir.c.

353{
354 if (!m || !e)
355 return -1;
356
357 bool restore = true;
358
359 long old_body_offset = e->body->offset;
360 long old_body_length = e->body->length;
361 long old_hdr_lines = e->lines;
362
363 struct Message *src = mx_msg_open(m, e);
364 struct Message *dest = mx_msg_open_new(m, e, MUTT_MSG_NO_FLAGS);
365 if (!src || !dest)
366 return -1;
367
368 int rc = mutt_copy_message(dest->fp, e, src, MUTT_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN, 0);
369 if (rc == 0)
370 {
371 char oldpath[PATH_MAX] = { 0 };
372 char partpath[PATH_MAX] = { 0 };
373 snprintf(oldpath, sizeof(oldpath), "%s/%s", mailbox_path(m), e->path);
374 mutt_str_copy(partpath, e->path, sizeof(partpath));
375
376 rc = maildir_commit_message(m, dest, e);
377
378 if (rc == 0)
379 {
380 unlink(oldpath);
381 restore = false;
382 }
383 }
384 mx_msg_close(m, &src);
385 mx_msg_close(m, &dest);
386
387 if ((rc == -1) && restore)
388 {
389 e->body->offset = old_body_offset;
390 e->body->length = old_body_length;
391 e->lines = old_hdr_lines;
392 }
393
395 return rc;
396}
int mutt_copy_message(FILE *fp_out, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:884
#define MUTT_CM_UPDATE
Update structs on sync.
Definition: copy.h:40
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:52
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:62
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
static int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
Commit a message to a maildir folder.
Definition: maildir.c:259
#define PATH_MAX
Definition: mutt.h:41
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition: mx.c:1206
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition: mx.c:1160
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1066
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:39
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
int lines
How many lines in the body of this message?
Definition: email.h:60
struct Body * body
List of MIME parts.
Definition: email.h:67
A local copy of an email.
Definition: message.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_sync_message()

static int maildir_sync_message ( struct Mailbox m,
struct Email e 
)
static

Sync an email to a Maildir folder.

Parameters
mMailbox
eEmail
Return values
0Success
-1Error

Definition at line 405 of file maildir.c.

406{
407 if (!m || !e)
408 return -1;
409
410 struct Buffer *newpath = NULL;
411 struct Buffer *partpath = NULL;
412 struct Buffer *fullpath = NULL;
413 struct Buffer *oldpath = NULL;
414 char suffix[16] = { 0 };
415 int rc = 0;
416
417 if (e->attach_del || e->env->changed)
418 {
419 /* when doing attachment deletion/rethreading, fall back to the MH case. */
420 if (maildir_rewrite_message(m, e) != 0)
421 return -1;
422 e->env->changed = false;
423 }
424 else
425 {
426 /* we just have to rename the file. */
427
428 char *p = strrchr(e->path, '/');
429 if (!p)
430 {
431 mutt_debug(LL_DEBUG1, "%s: unable to find subdir!\n", e->path);
432 return -1;
433 }
434 p++;
435 newpath = buf_pool_get();
436 partpath = buf_pool_get();
437 fullpath = buf_pool_get();
438 oldpath = buf_pool_get();
439
440 buf_strcpy(newpath, p);
441
442 /* kill the previous flags */
443 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
444 p = strchr(newpath->data, c_maildir_field_delimiter);
445 if (p)
446 {
447 *p = '\0';
448 newpath->dptr = p; /* fix buffer up, just to be safe */
449 }
450
451 maildir_gen_flags(suffix, sizeof(suffix), e);
452
453 buf_printf(partpath, "%s/%s%s", (e->read || e->old) ? "cur" : "new",
454 buf_string(newpath), suffix);
455 buf_printf(fullpath, "%s/%s", mailbox_path(m), buf_string(partpath));
456 buf_printf(oldpath, "%s/%s", mailbox_path(m), e->path);
457
458 if (mutt_str_equal(buf_string(fullpath), buf_string(oldpath)))
459 {
460 /* message hasn't really changed */
461 goto cleanup;
462 }
463
464 /* record that the message is possibly marked as trashed on disk */
465 e->trash = e->deleted;
466
467 struct stat st = { 0 };
468 if (stat(buf_string(oldpath), &st) == -1)
469 {
470 mutt_debug(LL_DEBUG1, "File already removed (just continuing)");
471 goto cleanup;
472 }
473
474 if (rename(buf_string(oldpath), buf_string(fullpath)) != 0)
475 {
476 mutt_perror("rename");
477 rc = -1;
478 goto cleanup;
479 }
480 mutt_str_replace(&e->path, buf_string(partpath));
481 }
482
483cleanup:
484 buf_pool_release(&newpath);
485 buf_pool_release(&partpath);
486 buf_pool_release(&fullpath);
487 buf_pool_release(&oldpath);
488
489 return rc;
490}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
static int maildir_rewrite_message(struct Mailbox *m, struct Email *e)
Sync a message in an MH folder.
Definition: maildir.c:352
void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
Generate the Maildir flags for an email.
Definition: maildir.c:206
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
char * dptr
Current read/write position.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:35
struct Envelope * env
Envelope information.
Definition: email.h:66
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
bool trash
Message is marked as trashed on disk (used by the maildir_trash option)
Definition: email.h:51
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:92
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_update_mtime()

static void maildir_update_mtime ( struct Mailbox m)
static

Update our record of the Maildir modification time.

Parameters
mMailbox

Definition at line 496 of file maildir.c.

497{
498 char buf[PATH_MAX] = { 0 };
499 struct stat st = { 0 };
501
502 snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "cur");
503 if (stat(buf, &st) == 0)
505
506 snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "new");
507 if (stat(buf, &st) == 0)
509}
void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *st, enum MuttStatType type)
Read the stat() time into a time value.
Definition: file.c:1620
struct MaildirMboxData * maildir_mdata_get(struct Mailbox *m)
Get the private data for this Mailbox.
Definition: mdata.c:60
void * mdata
Driver specific data.
Definition: mailbox.h:133
Maildir-specific Mailbox data -.
Definition: mdata.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_dir()

static int maildir_parse_dir ( struct Mailbox m,
struct MdEmailArray *  mda,
const char *  subdir,
struct Progress *  progress 
)
static

Read a Maildir mailbox.

Parameters
[in]mMailbox
[out]mdaArray for results
[in]subdirSubdirectory, e.g. 'new'
[in]progressProgress bar
Return values
0Success
-1Error
-2Aborted

Definition at line 532 of file maildir.c.

534{
535 struct dirent *de = NULL;
536 int rc = 0;
537 bool is_old = false;
538 struct MdEmail *entry = NULL;
539 struct Email *e = NULL;
540
541 struct Buffer *buf = buf_pool_get();
542
543 buf_printf(buf, "%s/%s", mailbox_path(m), subdir);
544 is_old = mutt_str_equal("cur", subdir);
545
547 if (!dir)
548 {
549 rc = -1;
550 goto cleanup;
551 }
552
553 while (((de = readdir(dir))) && !SigInt)
554 {
555 if (*de->d_name == '.')
556 continue;
557
558 mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
559
560 e = maildir_email_new();
561 e->old = is_old;
562 maildir_parse_flags(e, de->d_name);
563
564 progress_update(progress, ARRAY_SIZE(mda) + 1, -1);
565
566 buf_printf(buf, "%s/%s", subdir, de->d_name);
567 e->path = buf_strdup(buf);
568
569 entry = maildir_entry_new();
570 entry->email = e;
571 entry->inode = de->d_ino;
572 ARRAY_ADD(mda, entry);
573 }
574
575 closedir(dir);
576
577 if (SigInt)
578 {
579 SigInt = false;
580 return -2; /* action aborted */
581 }
582
583 ARRAY_SORT(mda, maildir_sort_inode, NULL);
584
585cleanup:
586 buf_pool_release(&buf);
587
588 return rc;
589}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:278
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:155
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:86
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:542
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:59
static int maildir_sort_inode(const void *a, const void *b, void *sdata)
Compare two Maildirs by inode number - Implements sort_t -.
Definition: maildir.c:514
struct Email * maildir_email_new(void)
Create a Maildir Email.
Definition: maildir.c:85
void maildir_parse_flags(struct Email *e, const char *path)
Parse Maildir file flags.
Definition: maildir.c:840
struct MdEmail * maildir_entry_new(void)
Create a new Maildir entry.
Definition: mdemail.c:39
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
A Maildir Email helper.
Definition: mdemail.h:34
struct Email * email
Definition: mdemail.h:35
ino_t inode
Definition: mdemail.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_hcache_keylen()

static size_t maildir_hcache_keylen ( const char *  fn)
static

Calculate the length of the Maildir path.

Parameters
fnFile name
Return values
numLength in bytes
Note
This length excludes the flags, which will vary

Definition at line 599 of file maildir.c.

600{
601 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
602 const char *p = strrchr(fn, c_maildir_field_delimiter);
603 return p ? (size_t) (p - fn) : mutt_str_len(fn);
604}
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_delayed_parsing()

static void maildir_delayed_parsing ( struct Mailbox m,
struct MdEmailArray *  mda,
struct Progress *  progress 
)
static

This function does the second parsing pass.

Parameters
[in]mMailbox
[out]mdaMaildir array to parse
[in]progressProgress bar

Definition at line 613 of file maildir.c.

615{
616 char fn[PATH_MAX] = { 0 };
617
618#ifdef USE_HCACHE
619 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
620 struct HeaderCache *hc = hcache_open(c_header_cache, mailbox_path(m), NULL);
621 const bool c_maildir_header_cache_verify = cs_subset_bool(NeoMutt->sub, "maildir_header_cache_verify");
622#endif
623
624 struct MdEmail *md = NULL;
625 struct MdEmail **mdp = NULL;
626 ARRAY_FOREACH(mdp, mda)
627 {
628 md = *mdp;
629 if (!md || !md->email || md->header_parsed)
630 continue;
631
632 progress_update(progress, ARRAY_FOREACH_IDX, -1);
633
634 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
635
636#ifdef USE_HCACHE
637 struct stat st_lastchanged = { 0 };
638 int rc = 0;
639
640 const char *key = md->email->path + 3;
641 size_t keylen = maildir_hcache_keylen(key);
642 struct HCacheEntry hce = { 0 };
643
644 if (hc)
645 {
646 hce = hcache_fetch(hc, key, keylen, 0);
647 }
648
649 if (hce.email && c_maildir_header_cache_verify)
650 {
651 rc = stat(fn, &st_lastchanged);
652 }
653
654 if (hce.email && (rc == 0) && (st_lastchanged.st_mtime <= hce.uidvalidity))
655 {
658 hce.email->old = md->email->old;
659 hce.email->path = mutt_str_dup(md->email->path);
660 email_free(&md->email);
661 md->email = hce.email;
662 maildir_parse_flags(md->email, fn);
663 }
664 else
665#endif
666 {
667 if (maildir_parse_message(m->type, fn, md->email->old, md->email))
668 {
669 md->header_parsed = true;
670#ifdef USE_HCACHE
671 key = md->email->path + 3;
672 keylen = maildir_hcache_keylen(key);
673 hcache_store(hc, key, keylen, md->email, 0);
674#endif
675 }
676 else
677 {
678 email_free(&md->email);
679 }
680 }
681 }
682#ifdef USE_HCACHE
683 hcache_close(&hc);
684#endif
685}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:211
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:169
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
int hcache_store(struct HeaderCache *hc, const char *key, size_t keylen, struct Email *e, uint32_t uidvalidity)
Multiplexor for StoreOps::store.
Definition: hcache.c:686
struct HCacheEntry hcache_fetch(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:583
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:494
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition: hcache.c:563
static size_t maildir_hcache_keylen(const char *fn)
Calculate the length of the Maildir path.
Definition: maildir.c:599
bool maildir_parse_message(enum MailboxType type, const char *fname, bool is_old, struct Email *e)
Actually parse a maildir message.
Definition: maildir.c:954
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
Wrapper for Email retrieved from the header cache.
Definition: lib.h:101
uint32_t uidvalidity
IMAP-specific UIDVALIDITY.
Definition: lib.h:102
struct Email * email
Retrieved email.
Definition: lib.h:104
Header Cache.
Definition: lib.h:88
bool header_parsed
Definition: mdemail.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_read_dir()

static int maildir_read_dir ( struct Mailbox m,
const char *  subdir 
)
static

Read a Maildir style mailbox.

Parameters
mMailbox
subdirSubdir of the maildir mailbox to read from
Return values
0Success
-1Failure

Definition at line 694 of file maildir.c.

695{
696 if (!m)
697 return -1;
698
699 struct Progress *progress = NULL;
700
701 if (m->verbose)
702 {
703 char msg[PATH_MAX] = { 0 };
704 snprintf(msg, sizeof(msg), _("Scanning %s..."), mailbox_path(m));
705 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
706 }
707
709 if (!mdata)
710 {
712 m->mdata = mdata;
714 }
715
716 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
717 int rc = maildir_parse_dir(m, &mda, subdir, progress);
718 progress_free(&progress);
719 if (rc < 0)
720 return -1;
721
722 if (m->verbose)
723 {
724 char msg[PATH_MAX] = { 0 };
725 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
726 progress = progress_new(msg, MUTT_PROGRESS_READ, ARRAY_SIZE(&mda));
727 }
728 maildir_delayed_parsing(m, &mda, progress);
729 progress_free(&progress);
730
732 maildirarray_clear(&mda);
733
734 if (!mdata->mh_umask)
735 mdata->mh_umask = mh_umask(m);
736
737 return 0;
738}
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
void maildir_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:37
struct MaildirMboxData * maildir_mdata_new(void)
Create a new MaildirMboxData object.
Definition: mdata.c:49
mode_t mh_umask(struct Mailbox *m)
Create a umask from the mailbox directory.
Definition: shared.c:52
int maildir_move_to_mailbox(struct Mailbox *m, const struct MdEmailArray *mda)
Copy the Maildir list to the Mailbox.
Definition: shared.c:75
static void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
This function does the second parsing pass.
Definition: maildir.c:613
static int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda, const char *subdir, struct Progress *progress)
Read a Maildir mailbox.
Definition: maildir.c:532
void maildirarray_clear(struct MdEmailArray *mda)
Free a Maildir array.
Definition: mdemail.c:64
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: lib.h:49
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:92
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:124
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:142
bool verbose
Display status messages?
Definition: mailbox.h:116
mode_t mh_umask
umask to use when creating files
Definition: mdata.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_canon_filename()

static void maildir_canon_filename ( struct Buffer dest,
const char *  src 
)
static

Generate the canonical filename for a Maildir folder.

Parameters
destBuffer for the result
srcBuffer containing source filename
Note
maildir filename is defined as: <base filename>:2,<flags> but <base filename> may contain additional comma separated fields. Additionally, : may be replaced as the field delimiter by a user defined alternative.

Definition at line 750 of file maildir.c.

751{
752 if (!dest || !src)
753 return;
754
755 char *t = strrchr(src, '/');
756 if (t)
757 src = t + 1;
758
759 buf_strcpy(dest, src);
760
761 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
762
763 char searchable_bytes[8] = { 0 };
764 snprintf(searchable_bytes, sizeof(searchable_bytes), ",%c", c_maildir_field_delimiter);
765 char *u = strpbrk(dest->data, searchable_bytes);
766
767 if (u)
768 {
769 *u = '\0';
770 dest->dptr = u;
771 }
772}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_open_find_message_dir()

static FILE * maildir_open_find_message_dir ( const char *  folder,
const char *  unique,
const char *  subfolder,
char **  newname 
)
static

Find a message in a maildir folder.

Parameters
[in]folderBase folder
[in]uniqueUnique part of filename
[in]subfolderSubfolder to search, e.g. 'cur'
[out]newnameFile's new name
Return values
ptrFile handle

These functions try to find a message in a maildir folder when it has moved under our feet. Note that this code is rather expensive, but then again, it's called rarely.

Definition at line 786 of file maildir.c.

788{
789 struct Buffer *dirname = buf_pool_get();
790 struct Buffer *tunique = buf_pool_get();
791 struct Buffer *fname = buf_pool_get();
792
793 struct dirent *de = NULL;
794
795 FILE *fp = NULL;
796 int oe = ENOENT;
797
798 buf_printf(dirname, "%s/%s", folder, subfolder);
799
801 if (!dir)
802 {
803 errno = ENOENT;
804 goto cleanup;
805 }
806
807 while ((de = readdir(dir)))
808 {
809 maildir_canon_filename(tunique, de->d_name);
810
811 if (mutt_str_equal(buf_string(tunique), unique))
812 {
813 buf_printf(fname, "%s/%s/%s", folder, subfolder, de->d_name);
814 fp = fopen(buf_string(fname), "r");
815 oe = errno;
816 break;
817 }
818 }
819
820 closedir(dir);
821
822 if (newname && fp)
823 *newname = buf_strdup(fname);
824
825 errno = oe;
826
827cleanup:
828 buf_pool_release(&dirname);
829 buf_pool_release(&tunique);
830 buf_pool_release(&fname);
831
832 return fp;
833}
static void maildir_canon_filename(struct Buffer *dest, const char *src)
Generate the canonical filename for a Maildir folder.
Definition: maildir.c:750
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_flags()

void maildir_parse_flags ( struct Email e,
const char *  path 
)

Parse Maildir file flags.

Parameters
eEmail
pathPath to email file

Definition at line 840 of file maildir.c.

841{
842 char *q = NULL;
843
844 e->flagged = false;
845 e->read = false;
846 e->replied = false;
847
849
850 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
851 char *p = strrchr(path, c_maildir_field_delimiter);
852 if (p && mutt_str_startswith(p + 1, "2,"))
853 {
854 p += 3;
855
856 mutt_str_replace(&edata->maildir_flags, p);
857 q = edata->maildir_flags;
858
859 while (*p)
860 {
861 switch (*p)
862 {
863 case 'F': // Flagged
864 e->flagged = true;
865 break;
866
867 case 'R': // Replied
868 e->replied = true;
869 break;
870
871 case 'S': // Seen
872 e->read = true;
873 break;
874
875 case 'T': // Trashed
876 {
877 const bool c_flag_safe = cs_subset_bool(NeoMutt->sub, "flag_safe");
878 if (!e->flagged || !c_flag_safe)
879 {
880 e->trash = true;
881 e->deleted = true;
882 }
883 break;
884 }
885
886 default:
887 *q++ = *p;
888 break;
889 }
890 p++;
891 }
892 }
893
894 if (q == edata->maildir_flags)
895 FREE(&edata->maildir_flags);
896 else if (q)
897 *q = '\0';
898}
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_stream()

bool maildir_parse_stream ( enum MailboxType  type,
FILE *  fp,
const char *  fname,
bool  is_old,
struct Email e 
)

Parse a Maildir message.

Parameters
typeMailbox type, e.g. MUTT_MAILDIR
fpMessage file handle
fnameMessage filename
is_oldtrue, if the email is old (read)
eEmail
Return values
trueSuccess

Actually parse a maildir message. This may also be used to fill out a fake header structure generated by lazy maildir parsing.

Definition at line 912 of file maildir.c.

914{
915 if (!fp || !fname || !e)
916 return false;
917
918 const long size = mutt_file_get_size_fp(fp);
919 if (size == 0)
920 return false;
921
922 e->env = mutt_rfc822_read_header(fp, e, false, false);
923
924 if (e->received == 0)
925 e->received = e->date_sent;
926
927 /* always update the length since we have fresh information available. */
928 e->body->length = size - e->body->offset;
929
930 e->index = -1;
931
932 if (type == MUTT_MAILDIR)
933 {
934 /* maildir stores its flags in the filename, so ignore the
935 * flags in the header of the message */
936
937 e->old = is_old;
938 maildir_parse_flags(e, fname);
939 }
940 return e;
941}
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1170
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1578
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:48
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:58
int index
The absolute (unsorted) message number.
Definition: email.h:109
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_message()

bool maildir_parse_message ( enum MailboxType  type,
const char *  fname,
bool  is_old,
struct Email e 
)

Actually parse a maildir message.

Parameters
typeMailbox type, e.g. MUTT_MAILDIR
fnameMessage filename
is_oldtrue, if the email is old (read)
eEmail to populate
Return values
trueSuccess

This may also be used to fill out a fake header structure generated by lazy maildir parsing.

Definition at line 954 of file maildir.c.

956{
957 if (!fname || !e)
958 return false;
959
960 FILE *fp = fopen(fname, "r");
961 if (!fp)
962 return false;
963
964 bool rc = maildir_parse_stream(type, fp, fname, is_old, e);
965 mutt_file_fclose(&fp);
966 return rc;
967}
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
bool maildir_parse_stream(enum MailboxType type, FILE *fp, const char *fname, bool is_old, struct Email *e)
Parse a Maildir message.
Definition: maildir.c:912
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_sync_mailbox_message()

bool maildir_sync_mailbox_message ( struct Mailbox m,
struct Email e,
struct HeaderCache hc 
)

Save changes to the mailbox.

Parameters
mMailbox
eEmail
hcHeader cache handle
Return values
trueSuccess
falseError

Definition at line 977 of file maildir.c.

978{
979 if (!e)
980 return false;
981
982 const bool c_maildir_trash = cs_subset_bool(NeoMutt->sub, "maildir_trash");
983 if (e->deleted && !c_maildir_trash)
984 {
985 char path[PATH_MAX] = { 0 };
986 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
987#ifdef USE_HCACHE
988 if (hc)
989 {
990 const char *key = e->path + 3;
991 size_t keylen = maildir_hcache_keylen(key);
992 hcache_delete_record(hc, key, keylen);
993 }
994#endif
995 unlink(path);
996 }
997 else if (e->changed || e->attach_del ||
998 ((c_maildir_trash || e->trash) && (e->deleted != e->trash)))
999 {
1000 if (maildir_sync_message(m, e) == -1)
1001 return false;
1002 }
1003
1004#ifdef USE_HCACHE
1005 if (hc && e->changed)
1006 {
1007 const char *key = e->path + 3;
1008 size_t keylen = maildir_hcache_keylen(key);
1009 hcache_store(hc, key, keylen, e, 0);
1010 }
1011#endif
1012
1013 return true;
1014}
int hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:759
static int maildir_sync_message(struct Mailbox *m, struct Email *e)
Sync an email to a Maildir folder.
Definition: maildir.c:405
bool changed
Email has been edited.
Definition: email.h:75
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_open_find_message()

FILE * maildir_open_find_message ( const char *  folder,
const char *  msg,
char **  newname 
)

Find a message by name.

Parameters
[in]folderMaildir path
[in]msgEmail path
[out]newnameNew name, if it has moved
Return values
ptrFile handle

Definition at line 1023 of file maildir.c.

1024{
1025 static unsigned int new_hits = 0, cur_hits = 0; /* simple dynamic optimization */
1026
1027 struct Buffer *unique = buf_pool_get();
1028 maildir_canon_filename(unique, msg);
1029
1030 FILE *fp = maildir_open_find_message_dir(folder, buf_string(unique),
1031 (new_hits > cur_hits) ? "new" : "cur", newname);
1032 if (fp || (errno != ENOENT))
1033 {
1034 if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1035 {
1036 new_hits += ((new_hits > cur_hits) ? 1 : 0);
1037 cur_hits += ((new_hits > cur_hits) ? 0 : 1);
1038 }
1039
1040 goto cleanup;
1041 }
1042 fp = maildir_open_find_message_dir(folder, buf_string(unique),
1043 (new_hits > cur_hits) ? "cur" : "new", newname);
1044 if (fp || (errno != ENOENT))
1045 {
1046 if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1047 {
1048 new_hits += ((new_hits > cur_hits) ? 0 : 1);
1049 cur_hits += ((new_hits > cur_hits) ? 1 : 0);
1050 }
1051
1052 goto cleanup;
1053 }
1054
1055 fp = NULL;
1056
1057cleanup:
1058 buf_pool_release(&unique);
1059
1060 return fp;
1061}
static FILE * maildir_open_find_message_dir(const char *folder, const char *unique, const char *subfolder, char **newname)
Find a message in a maildir folder.
Definition: maildir.c:786
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_check_empty()

int maildir_check_empty ( struct Buffer path)

Is the mailbox empty.

Parameters
pathMailbox to check
Return values
1Mailbox is empty
0Mailbox contains mail
-1Error

Definition at line 1070 of file maildir.c.

1071{
1072 DIR *dir = NULL;
1073 struct dirent *de = NULL;
1074 int rc = 1; /* assume empty until we find a message */
1075 char realpath[PATH_MAX] = { 0 };
1076 int iter = 0;
1077
1078 /* Strategy here is to look for any file not beginning with a period */
1079
1080 do
1081 {
1082 /* we do "cur" on the first iteration since it's more likely that we'll
1083 * find old messages without having to scan both subdirs */
1084 snprintf(realpath, sizeof(realpath), "%s/%s", buf_string(path),
1085 (iter == 0) ? "cur" : "new");
1086 dir = mutt_file_opendir(realpath, MUTT_OPENDIR_CREATE);
1087 if (!dir)
1088 return -1;
1089 while ((de = readdir(dir)))
1090 {
1091 if (*de->d_name != '.')
1092 {
1093 rc = 0;
1094 break;
1095 }
1096 }
1097 closedir(dir);
1098 iter++;
1099 } while (rc && iter < 2);
1100
1101 return rc;
1102}
+ Here is the call graph for this function:

◆ maildir_check()

static enum MxStatus maildir_check ( struct Mailbox m)
static

Check for new mail.

Parameters
mMailbox
Return values
enumMxStatus

This function handles arrival of new mail and reopening of maildir folders. The basic idea here is we check to see if either the new or cur subdirectories have changed, and if so, we scan them for the list of files. We check for newly added messages, and then merge the flags messages we already knew about. We don't treat either subdirectory differently, as mail could be copied directly into the cur directory from another agent.

Definition at line 1199 of file maildir.c.

1200{
1201 struct stat st_new = { 0 }; /* status of the "new" subdirectory */
1202 struct stat st_cur = { 0 }; /* status of the "cur" subdirectory */
1203 int changed = MMC_NO_DIRS; /* which subdirectories have changed */
1204 bool occult = false; /* messages were removed from the mailbox */
1205 int num_new = 0; /* number of new messages added to the mailbox */
1206 bool flags_changed = false; /* message flags were changed in the mailbox */
1207 struct HashTable *hash_names = NULL; // Hash Table: "base-filename" -> MdEmail
1209
1210 /* XXX seems like this check belongs in mx_mbox_check() rather than here. */
1211 const bool c_check_new = cs_subset_bool(NeoMutt->sub, "check_new");
1212 if (!c_check_new)
1213 return MX_STATUS_OK;
1214
1215 struct Buffer *buf = buf_pool_get();
1216 buf_printf(buf, "%s/new", mailbox_path(m));
1217 if (stat(buf_string(buf), &st_new) == -1)
1218 {
1219 buf_pool_release(&buf);
1220 return MX_STATUS_ERROR;
1221 }
1222
1223 buf_printf(buf, "%s/cur", mailbox_path(m));
1224 if (stat(buf_string(buf), &st_cur) == -1)
1225 {
1226 buf_pool_release(&buf);
1227 return MX_STATUS_ERROR;
1228 }
1229
1230 /* determine which subdirectories need to be scanned */
1231 if (mutt_file_stat_timespec_compare(&st_new, MUTT_STAT_MTIME, &mdata->mtime) > 0)
1232 changed = MMC_NEW_DIR;
1234 changed |= MMC_CUR_DIR;
1235
1236 if (changed == MMC_NO_DIRS)
1237 {
1238 buf_pool_release(&buf);
1239 return MX_STATUS_OK; /* nothing to do */
1240 }
1241
1242 /* Update the modification times on the mailbox.
1243 *
1244 * The monitor code notices changes in the open mailbox too quickly.
1245 * In practice, this sometimes leads to all the new messages not being
1246 * noticed during the SAME group of mtime stat updates. To work around
1247 * the problem, don't update the stat times for a monitor caused check. */
1248#ifdef USE_INOTIFY
1250 {
1251 MonitorContextChanged = false;
1252 }
1253 else
1254#endif
1255 {
1258 }
1259
1260 /* do a fast scan of just the filenames in
1261 * the subdirectories that have changed. */
1262 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
1263 if (changed & MMC_NEW_DIR)
1264 maildir_parse_dir(m, &mda, "new", NULL);
1265 if (changed & MMC_CUR_DIR)
1266 maildir_parse_dir(m, &mda, "cur", NULL);
1267
1268 /* we create a hash table keyed off the canonical (sans flags) filename
1269 * of each message we scanned. This is used in the loop over the
1270 * existing messages below to do some correlation. */
1271 hash_names = mutt_hash_new(ARRAY_SIZE(&mda), MUTT_HASH_NO_FLAGS);
1272
1273 struct MdEmail *md = NULL;
1274 struct MdEmail **mdp = NULL;
1275 ARRAY_FOREACH(mdp, &mda)
1276 {
1277 md = *mdp;
1279 md->canon_fname = buf_strdup(buf);
1280 mutt_hash_insert(hash_names, md->canon_fname, md);
1281 }
1282
1283 /* check for modifications and adjust flags */
1284 for (int i = 0; i < m->msg_count; i++)
1285 {
1286 struct Email *e = m->emails[i];
1287 if (!e)
1288 break;
1289
1291 md = mutt_hash_find(hash_names, buf_string(buf));
1292 if (md && md->email)
1293 {
1294 /* message already exists, merge flags */
1295
1296 /* check to see if the message has moved to a different
1297 * subdirectory. If so, update the associated filename. */
1298 if (!mutt_str_equal(e->path, md->email->path))
1299 mutt_str_replace(&e->path, md->email->path);
1300
1301 /* if the user hasn't modified the flags on this message, update
1302 * the flags we just detected. */
1303 if (!e->changed)
1304 if (maildir_update_flags(m, e, md->email))
1305 flags_changed = true;
1306
1307 if (e->deleted == e->trash)
1308 {
1309 if (e->deleted != md->email->deleted)
1310 {
1311 e->deleted = md->email->deleted;
1312 flags_changed = true;
1313 }
1314 }
1315 e->trash = md->email->trash;
1316
1317 /* this is a duplicate of an existing email, so remove it */
1318 email_free(&md->email);
1319 }
1320 /* This message was not in the list of messages we just scanned.
1321 * Check to see if we have enough information to know if the
1322 * message has disappeared out from underneath us. */
1323 else if (((changed & MMC_NEW_DIR) && mutt_strn_equal(e->path, "new/", 4)) ||
1324 ((changed & MMC_CUR_DIR) && mutt_strn_equal(e->path, "cur/", 4)))
1325 {
1326 /* This message disappeared, so we need to simulate a "reopen"
1327 * event. We know it disappeared because we just scanned the
1328 * subdirectory it used to reside in. */
1329 occult = true;
1330 e->deleted = true;
1331 e->purge = true;
1332 }
1333 else
1334 {
1335 /* This message resides in a subdirectory which was not
1336 * modified, so we assume that it is still present and
1337 * unchanged. */
1338 }
1339 }
1340
1341 /* destroy the file name hash */
1342 mutt_hash_free(&hash_names);
1343
1344 /* If we didn't just get new mail, update the tables. */
1345 if (occult)
1347
1348 /* do any delayed parsing we need to do. */
1349 maildir_delayed_parsing(m, &mda, NULL);
1350
1351 /* Incorporate new messages */
1352 num_new = maildir_move_to_mailbox(m, &mda);
1353 maildirarray_clear(&mda);
1354
1355 if (num_new > 0)
1356 {
1358 m->changed = true;
1359 }
1360
1361 buf_pool_release(&buf);
1362
1363 ARRAY_FREE(&mda);
1364 if (occult)
1365 return MX_STATUS_REOPENED;
1366 if (num_new > 0)
1367 return MX_STATUS_NEW_MAIL;
1368 if (flags_changed)
1369 return MX_STATUS_FLAGS;
1370 return MX_STATUS_OK;
1371}
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition: hash.c:335
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:362
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:259
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:110
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:226
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:177
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:176
bool maildir_update_flags(struct Mailbox *m, struct Email *e_old, struct Email *e_new)
Update the mailbox flags.
Definition: shared.c:120
#define MMC_CUR_DIR
'cur' directory changed
Definition: maildir.c:75
#define MMC_NO_DIRS
No directories changed.
Definition: maildir.c:73
#define MMC_NEW_DIR
'new' directory changed
Definition: maildir.c:74
bool MonitorContextChanged
Set to true when the current mailbox has changed.
Definition: monitor.c:51
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:497
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:64
@ MX_STATUS_OK
No changes.
Definition: mxapi.h:65
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mxapi.h:69
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:68
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:66
bool purge
Skip trash folder when deleting.
Definition: email.h:77
A Hash Table.
Definition: hash.h:98
bool changed
Mailbox has been modified.
Definition: mailbox.h:109
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
struct timespec mtime_cur
Timestamp of the 'cur' dir.
Definition: mdata.h:37
struct timespec mtime
Time Mailbox was last changed.
Definition: mdata.h:36
char * canon_fname
Definition: mdemail.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function: