NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mailbox.c File Reference

Maildir Mailbox. More...

#include "config.h"
#include <dirent.h>
#include <errno.h>
#include <limits.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 "mailbox.h"
#include "progress/lib.h"
#include "edata.h"
#include "hcache.h"
#include "mdata.h"
#include "mdemail.h"
#include "mx.h"
#include "shared.h"
#include "monitor.h"
+ Include dependency graph for mailbox.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.
 
void maildir_parse_flags (struct Email *e, const char *path)
 Parse Maildir file flags.
 
bool maildir_parse_stream (FILE *fp, const char *fname, bool is_old, struct Email *e)
 Parse a Maildir message.
 
bool maildir_parse_message (const char *fname, bool is_old, struct Email *e)
 Actually parse a maildir message.
 
static int maildir_move_to_mailbox (struct Mailbox *m, const struct MdEmailArray *mda)
 Copy the Maildir list to the Mailbox.
 
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 void maildir_delayed_parsing (struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
 This function does the second parsing pass.
 
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_read_dir (struct Mailbox *m, const char *subdir)
 Read a Maildir style mailbox.
 
static enum MxStatus maildir_check (struct Mailbox *m)
 Check for new mail.
 
void maildir_update_mtime (struct Mailbox *m)
 Update our record of the Maildir modification time.
 
enum MxOpenReturns maildir_mbox_open (struct Mailbox *m)
 Open a Mailbox - Implements MxOps::mbox_open() -.
 
bool maildir_mbox_open_append (struct Mailbox *m, OpenMailboxFlags flags)
 Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
 
enum MxStatus maildir_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -.
 
enum MxStatus maildir_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -.
 
enum MxStatus maildir_mbox_sync (struct Mailbox *m)
 Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
 
enum MxStatus maildir_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -.
 

Detailed Description

Maildir Mailbox.

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

Macro Definition Documentation

◆ MMC_NO_DIRS

#define MMC_NO_DIRS   0

No directories changed.

Definition at line 56 of file mailbox.c.

◆ MMC_NEW_DIR

#define MMC_NEW_DIR   (1 << 0)

'new' directory changed

Definition at line 57 of file mailbox.c.

◆ MMC_CUR_DIR

#define MMC_CUR_DIR   (1 << 1)

'cur' directory changed

Definition at line 58 of file mailbox.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 68 of file mailbox.c.

69{
70 struct Email *e = email_new();
73
74 return e;
75}
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
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:39
void * edata
Driver-specific data.
Definition email.h:74
void(* edata_free)(void **ptr)
Definition email.h:90
+ 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 82 of file mailbox.c.

83{
84 char *q = NULL;
85
86 e->flagged = false;
87 e->read = false;
88 e->replied = false;
89
91 if (!edata)
92 {
94 edata = e->edata;
95 }
96
97 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
98 char *p = strrchr(path, c_maildir_field_delimiter);
99 if (p && mutt_str_startswith(p + 1, "2,"))
100 {
101 p += 3;
102
103 mutt_str_replace(&edata->custom_flags, p);
104 q = edata->custom_flags;
105
106 while (*p)
107 {
108 switch (*p)
109 {
110 case 'F': // Flagged
111 e->flagged = true;
112 break;
113
114 case 'R': // Replied
115 e->replied = true;
116 break;
117
118 case 'S': // Seen
119 e->read = true;
120 break;
121
122 case 'T': // Trashed
123 {
124 const bool c_flag_safe = cs_subset_bool(NeoMutt->sub, "flag_safe");
125 if (!e->flagged || !c_flag_safe)
126 {
127 e->trash = true;
128 e->deleted = true;
129 }
130 break;
131 }
132
133 default:
134 *q++ = *p;
135 break;
136 }
137 p++;
138 }
139 }
140
141 if (q == edata->custom_flags)
142 FREE(&edata->custom_flags);
143 else if (q)
144 *q = '\0';
145}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
const char * cc_maildir_field_delimiter(void)
Get the cached value of $maildir_field_delimiter.
struct MaildirEmailData * maildir_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:63
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.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
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
bool read
Email is read.
Definition email.h:50
bool flagged
Marked important?
Definition email.h:47
bool replied
Email has been replied to.
Definition email.h:51
char * path
Path of Email (for local Mailboxes)
Definition email.h:70
bool deleted
Email is deleted.
Definition email.h:78
bool trash
Message is marked as trashed on disk (used by the maildir_trash option)
Definition email.h:53
Maildir-specific Email data -.
Definition edata.h:32
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_stream()

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

Parse a Maildir message.

Parameters
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 158 of file mailbox.c.

159{
160 if (!fp || !fname || !e)
161 return false;
162
163 const long size = mutt_file_get_size_fp(fp);
164 if (size == 0)
165 return false;
166
167 e->env = mutt_rfc822_read_header(fp, e, false, false);
168
169 if (e->received == 0)
170 e->received = e->date_sent;
171
172 /* always update the length since we have fresh information available. */
173 e->body->length = size - e->body->offset;
174
175 e->index = -1;
176
177 /* maildir stores its flags in the filename, so ignore the
178 * flags in the header of the message */
179 e->old = is_old;
180 maildir_parse_flags(e, fname);
181
182 return true;
183}
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1210
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1427
void maildir_parse_flags(struct Email *e, const char *path)
Parse Maildir file flags.
Definition mailbox.c:82
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
struct Envelope * env
Envelope information.
Definition email.h:68
struct Body * body
List of MIME parts.
Definition email.h:69
bool old
Email is seen, but unread.
Definition email.h:49
time_t date_sent
Time when the message was sent (UTC)
Definition email.h:60
int index
The absolute (unsorted) message number.
Definition email.h:110
time_t received
Time when the message was placed in the mailbox.
Definition email.h:61
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_message()

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

Actually parse a maildir message.

Parameters
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 195 of file mailbox.c.

196{
197 if (!fname || !e)
198 return false;
199
200 FILE *fp = mutt_file_fopen(fname, "r");
201 if (!fp)
202 return false;
203
204 bool rc = maildir_parse_stream(fp, fname, is_old, e);
205 mutt_file_fclose(&fp);
206 return rc;
207}
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
bool maildir_parse_stream(FILE *fp, const char *fname, bool is_old, struct Email *e)
Parse a Maildir message.
Definition mailbox.c:158
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_move_to_mailbox()

static int maildir_move_to_mailbox ( struct Mailbox * m,
const struct MdEmailArray * mda )
static

Copy the Maildir list to the Mailbox.

Parameters
[in]mMailbox
[out]mdaMaildir array to copy, then free
Return values
numNumber of new emails
0Error

Definition at line 216 of file mailbox.c.

217{
218 if (!m)
219 return 0;
220
221 int oldmsgcount = m->msg_count;
222
223 struct MdEmail *md = NULL;
224 struct MdEmail **mdp = NULL;
225 ARRAY_FOREACH(mdp, mda)
226 {
227 md = *mdp;
228 mutt_debug(LL_DEBUG2, "Considering %s\n", NONULL(md->canon_fname));
229 if (!md->email)
230 continue;
231
232 mutt_debug(LL_DEBUG2, "Adding header structure. Flags: %s%s%s%s%s\n",
233 md->email->flagged ? "f" : "", md->email->deleted ? "D" : "",
234 md->email->replied ? "r" : "", md->email->old ? "O" : "",
235 md->email->read ? "R" : "");
237
238 m->emails[m->msg_count] = md->email;
239 m->emails[m->msg_count]->index = m->msg_count;
240 mailbox_size_add(m, md->email);
241
242 md->email = NULL;
243 m->msg_count++;
244 }
245
246 int num = 0;
247 if (m->msg_count > oldmsgcount)
248 num = m->msg_count - oldmsgcount;
249
250 return num;
251}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
void mailbox_size_add(struct Mailbox *m, const struct Email *e)
Add an email's size to the total size of a Mailbox.
Definition mailbox.c:248
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1208
#define NONULL(x)
Definition string2.h:44
int msg_count
Total number of messages.
Definition mailbox.h:87
struct Email ** emails
Array of Emails.
Definition mailbox.h:95
A Maildir Email helper.
Definition mdemail.h:34
char * canon_fname
Canonical filename for hashing.
Definition mdemail.h:36
struct Email * email
Temporary Email.
Definition mdemail.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 274 of file mailbox.c.

276{
277 struct dirent *de = NULL;
278 int rc = 0;
279 bool is_old = false;
280 struct MdEmail *entry = NULL;
281 struct Email *e = NULL;
282
283 struct Buffer *buf = buf_pool_get();
284
285 buf_printf(buf, "%s/%s", mailbox_path(m), subdir);
286 is_old = mutt_str_equal("cur", subdir);
287
289 if (!dir)
290 {
291 rc = -1;
292 goto cleanup;
293 }
294
295 while (((de = readdir(dir))) && !SigInt)
296 {
297 if (*de->d_name == '.')
298 continue;
299
300 mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
301
302 e = maildir_email_new();
303 e->old = is_old;
304 maildir_parse_flags(e, de->d_name);
305
306 progress_update(progress, ARRAY_SIZE(mda) + 1, -1);
307
308 buf_printf(buf, "%s/%s", subdir, de->d_name);
309 e->path = buf_strdup(buf);
310
311 entry = maildir_entry_new();
312 entry->email = e;
313 entry->inode = de->d_ino;
314 ARRAY_ADD(mda, entry);
315 }
316
317 closedir(dir);
318
319 if (SigInt)
320 {
321 SigInt = false;
322 rc = -2; /* action aborted */
323 goto cleanup;
324 }
325
326 ARRAY_SORT(mda, maildir_sort_inode, NULL);
327
328cleanup:
329 buf_pool_release(&buf);
330
331 return rc;
332}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:373
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:213
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition file.c:539
@ MUTT_OPENDIR_CREATE
Create the directory if it doesn't exist.
Definition file.h:64
static int maildir_sort_inode(const void *a, const void *b, void *sdata)
Compare two Maildirs by inode number - Implements sort_t -.
Definition mailbox.c:256
struct Email * maildir_email_new(void)
Create a Maildir Email.
Definition mailbox.c:68
struct MdEmail * maildir_entry_new(void)
Create a new Maildir entry.
Definition mdemail.c:39
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
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
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition progress.c:80
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition signal.c:68
String manipulation buffer.
Definition buffer.h:36
ino_t inode
Inode number of the file.
Definition mdemail.h:38
+ 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 340 of file mailbox.c.

342{
343 char fn[PATH_MAX] = { 0 };
344
345 struct HeaderCache *hc = maildir_hcache_open(m);
346
347 struct MdEmail *md = NULL;
348 struct MdEmail **mdp = NULL;
349 ARRAY_FOREACH(mdp, mda)
350 {
351 md = *mdp;
352 if (!md || !md->email || md->header_parsed)
353 continue;
354
355 progress_update(progress, ARRAY_FOREACH_IDX_mdp, -1);
356
357 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
358
359 struct Email *e = maildir_hcache_read(hc, md->email, fn);
360 if (e)
361 {
362 email_free(&md->email);
363 md->email = e;
364 }
365 else
366 {
367 if (maildir_parse_message(fn, md->email->old, md->email))
368 {
369 md->header_parsed = true;
371 }
372 else
373 {
374 email_free(&md->email);
375 }
376 }
377 }
378
380}
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
int maildir_hcache_store(struct HeaderCache *hc, struct Email *e)
Save an Email to the Header Cache.
Definition hcache.c:163
struct Email * maildir_hcache_read(struct HeaderCache *hc, struct Email *e, const char *fn)
Read an Email from the Header Cache.
Definition hcache.c:121
struct HeaderCache * maildir_hcache_open(struct Mailbox *m)
Open the Header Cache.
Definition hcache.c:104
void maildir_hcache_close(struct HeaderCache **ptr)
Close the Header Cache.
Definition hcache.c:77
bool maildir_parse_message(const char *fname, bool is_old, struct Email *e)
Actually parse a maildir message.
Definition mailbox.c:195
#define PATH_MAX
Definition mutt.h:49
Header Cache.
Definition lib.h:87
bool header_parsed
Has the Email header been parsed?
Definition mdemail.h:37
+ 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 391 of file mailbox.c.

393{
394 DIR *dir = NULL;
395 struct dirent *de = NULL;
396 char *p = NULL;
397 struct stat st = { 0 };
398
399 struct Buffer *path = buf_pool_get();
400 struct Buffer *msgpath = buf_pool_get();
401 buf_printf(path, "%s/%s", mailbox_path(m), dir_name);
402
403 /* when $mail_check_recent is set, if the new/ directory hasn't been modified since
404 * the user last exited the mailbox, then we know there is no recent mail. */
405 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
406 if (check_new && c_mail_check_recent)
407 {
408 if ((stat(buf_string(path), &st) == 0) &&
410 {
411 check_new = false;
412 }
413 }
414
415 if (!(check_new || check_stats))
416 goto cleanup;
417
419 if (!dir)
420 {
421 m->type = MUTT_UNKNOWN;
422 goto cleanup;
423 }
424
425 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
426
427 char delimiter_version[8] = { 0 };
428 snprintf(delimiter_version, sizeof(delimiter_version), "%c2,", c_maildir_field_delimiter);
429 while ((de = readdir(dir)))
430 {
431 if (*de->d_name == '.')
432 continue;
433
434 p = strstr(de->d_name, delimiter_version);
435 if (p && strchr(p + 3, 'T'))
436 continue;
437
438 if (check_stats)
439 {
440 m->msg_count++;
441 if (p && strchr(p + 3, 'F'))
442 m->msg_flagged++;
443 }
444 if (!p || !strchr(p + 3, 'S'))
445 {
446 if (check_stats)
447 m->msg_unread++;
448 if (check_new)
449 {
450 if (c_mail_check_recent)
451 {
452 buf_printf(msgpath, "%s/%s", buf_string(path), de->d_name);
453 /* ensure this message was received since leaving this m */
454 if ((stat(buf_string(msgpath), &st) == 0) &&
456 {
457 continue;
458 }
459 }
460 m->has_new = true;
461 if (check_stats)
462 {
463 m->msg_new++;
464 }
465 else
466 {
467 break;
468 }
469 }
470 }
471 }
472
473 closedir(dir);
474
475cleanup:
476 buf_pool_release(&path);
477 buf_pool_release(&msgpath);
478}
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition mailbox.h:43
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:1509
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition file.h:55
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition file.h:54
bool has_new
Mailbox has new mail.
Definition mailbox.h:84
int msg_new
Number of new messages.
Definition mailbox.h:91
enum MailboxType type
Mailbox type.
Definition mailbox.h:101
int msg_flagged
Number of flagged messages.
Definition mailbox.h:89
struct timespec last_visited
Time of last exit from this mailbox.
Definition mailbox.h:103
int msg_unread
Number of unread messages.
Definition mailbox.h:88
+ 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 487 of file mailbox.c.

488{
489 if (!m)
490 return -1;
491
492 mutt_path_tidy(&m->pathbuf, true);
493
494 struct Progress *progress = NULL;
495
496 if (m->verbose)
497 {
498 progress = progress_new(MUTT_PROGRESS_READ, 0);
499 progress_set_message(progress, _("Scanning %s..."), mailbox_path(m));
500 }
501
503 if (!mdata)
504 {
506 m->mdata = mdata;
508 }
509
510 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
511 int rc = maildir_parse_dir(m, &mda, subdir, progress);
512 progress_free(&progress);
513 if (rc < 0)
514 return -1;
515
516 if (m->verbose)
517 {
518 progress = progress_new(MUTT_PROGRESS_READ, ARRAY_SIZE(&mda));
519 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
520 }
521 maildir_delayed_parsing(m, &mda, progress);
522 progress_free(&progress);
523
525 maildirarray_clear(&mda);
526
527 if (!mdata->umask)
528 mdata->umask = maildir_umask(m);
529
530 return 0;
531}
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
void maildir_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free() -.
Definition mdata.c:37
static void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
This function does the second parsing pass.
Definition mailbox.c:340
static int maildir_move_to_mailbox(struct Mailbox *m, const struct MdEmailArray *mda)
Copy the Maildir list to the Mailbox.
Definition mailbox.c:216
static int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda, const char *subdir, struct Progress *progress)
Read a Maildir mailbox.
Definition mailbox.c:274
struct MaildirMboxData * maildir_mdata_get(struct Mailbox *m)
Get the private data for this Mailbox.
Definition mdata.c:59
struct MaildirMboxData * maildir_mdata_new(void)
Create a new MaildirMboxData object.
Definition mdata.c:49
mode_t maildir_umask(struct Mailbox *m)
Create a umask from the mailbox directory.
Definition shared.c:46
void maildirarray_clear(struct MdEmailArray *mda)
Free a Maildir array.
Definition mdemail.c:64
#define _(a)
Definition message.h:28
bool mutt_path_tidy(struct Buffer *path, bool is_dir)
Remove unnecessary parts of a path.
Definition path.c:169
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition lib.h:84
struct Progress * progress_new(enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition progress.c:139
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition progress.c:110
void progress_set_message(struct Progress *progress, const char *fmt,...) __attribute__((__format__(__printf__
void(* mdata_free)(void **ptr)
Definition mailbox.h:142
void * mdata
Driver specific data.
Definition mailbox.h:131
struct Buffer pathbuf
Path of the Mailbox.
Definition mailbox.h:79
bool verbose
Display status messages?
Definition mailbox.h:116
Maildir-specific Mailbox data -.
Definition mdata.h:35
mode_t 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_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 545 of file mailbox.c.

546{
547 struct stat st_new = { 0 }; /* status of the "new" subdirectory */
548 struct stat st_cur = { 0 }; /* status of the "cur" subdirectory */
549 int changed = MMC_NO_DIRS; /* which subdirectories have changed */
550 bool occult = false; /* messages were removed from the mailbox */
551 int num_new = 0; /* number of new messages added to the mailbox */
552 bool flags_changed = false; /* message flags were changed in the mailbox */
553 struct HashTable *hash_names = NULL; // Hash Table: "base-filename" -> MdEmail
555
556 const bool c_check_new = cs_subset_bool(NeoMutt->sub, "check_new");
557 if (!c_check_new)
558 return MX_STATUS_OK;
559
560 struct Buffer *buf = buf_pool_get();
561 buf_printf(buf, "%s/new", mailbox_path(m));
562 if (stat(buf_string(buf), &st_new) == -1)
563 {
564 buf_pool_release(&buf);
565 return MX_STATUS_ERROR;
566 }
567
568 buf_printf(buf, "%s/cur", mailbox_path(m));
569 if (stat(buf_string(buf), &st_cur) == -1)
570 {
571 buf_pool_release(&buf);
572 return MX_STATUS_ERROR;
573 }
574
575 /* determine which subdirectories need to be scanned */
576 if (mutt_file_stat_timespec_compare(&st_new, MUTT_STAT_MTIME, &mdata->mtime) > 0)
577 changed = MMC_NEW_DIR;
579 changed |= MMC_CUR_DIR;
580
581 if (changed == MMC_NO_DIRS)
582 {
583 buf_pool_release(&buf);
584 return MX_STATUS_OK; /* nothing to do */
585 }
586
587 /* Update the modification times on the mailbox.
588 *
589 * The monitor code notices changes in the open mailbox too quickly.
590 * In practice, this sometimes leads to all the new messages not being
591 * noticed during the SAME group of mtime stat updates. To work around
592 * the problem, don't update the stat times for a monitor caused check. */
593#ifdef USE_INOTIFY
595 {
596 MonitorCurMboxChanged = false;
597 }
598 else
599#endif
600 {
603 }
604
605 /* do a fast scan of just the filenames in
606 * the subdirectories that have changed. */
607 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
608 if (changed & MMC_NEW_DIR)
609 maildir_parse_dir(m, &mda, "new", NULL);
610 if (changed & MMC_CUR_DIR)
611 maildir_parse_dir(m, &mda, "cur", NULL);
612
613 /* we create a hash table keyed off the canonical (sans flags) filename
614 * of each message we scanned. This is used in the loop over the
615 * existing messages below to do some correlation. */
616 hash_names = mutt_hash_new(ARRAY_SIZE(&mda), MUTT_HASH_NO_FLAGS);
617
618 struct MdEmail *md = NULL;
619 struct MdEmail **mdp = NULL;
620 ARRAY_FOREACH(mdp, &mda)
621 {
622 md = *mdp;
624 md->canon_fname = buf_strdup(buf);
625 mutt_hash_insert(hash_names, md->canon_fname, md);
626 }
627
628 /* check for modifications and adjust flags */
629 for (int i = 0; i < m->msg_count; i++)
630 {
631 struct Email *e = m->emails[i];
632 if (!e)
633 break;
634
636 md = mutt_hash_find(hash_names, buf_string(buf));
637 if (md && md->email)
638 {
639 /* message already exists, merge flags */
640
641 /* check to see if the message has moved to a different
642 * subdirectory. If so, update the associated filename. */
643 if (!mutt_str_equal(e->path, md->email->path))
644 mutt_str_replace(&e->path, md->email->path);
645
646 /* if the user hasn't modified the flags on this message, update
647 * the flags we just detected. */
648 if (!e->changed)
649 if (maildir_update_flags(m, e, md->email))
650 flags_changed = true;
651
652 if (e->deleted == e->trash)
653 {
654 if (e->deleted != md->email->deleted)
655 {
656 e->deleted = md->email->deleted;
657 flags_changed = true;
658 }
659 }
660 e->trash = md->email->trash;
661
662 /* this is a duplicate of an existing email, so remove it */
663 email_free(&md->email);
664 }
665 /* This message was not in the list of messages we just scanned.
666 * Check to see if we have enough information to know if the
667 * message has disappeared out from underneath us. */
668 else if (((changed & MMC_NEW_DIR) && mutt_strn_equal(e->path, "new/", 4)) ||
669 ((changed & MMC_CUR_DIR) && mutt_strn_equal(e->path, "cur/", 4)))
670 {
671 /* This message disappeared, so we need to simulate a "reopen"
672 * event. We know it disappeared because we just scanned the
673 * subdirectory it used to reside in. */
674 occult = true;
675 e->deleted = true;
676 e->purge = true;
677 }
678 else
679 {
680 /* This message resides in a subdirectory which was not
681 * modified, so we assume that it is still present and
682 * unchanged. */
683 }
684 }
685
686 /* destroy the file name hash */
687 mutt_hash_free(&hash_names);
688
689 /* If we didn't just get new mail, update the tables. */
690 if (occult)
692
693 /* do any delayed parsing we need to do. */
694 maildir_delayed_parsing(m, &mda, NULL);
695
696 /* Incorporate new messages */
697 num_new = maildir_move_to_mailbox(m, &mda);
698 maildirarray_clear(&mda);
699
700 if (num_new > 0)
701 {
703 m->changed = true;
704 }
705
706 buf_pool_release(&buf);
707
708 ARRAY_FREE(&mda);
709 if (occult)
710 return MX_STATUS_REOPENED;
711 if (num_new > 0)
712 return MX_STATUS_NEW_MAIL;
713 if (flags_changed)
714 return MX_STATUS_FLAGS;
715 return MX_STATUS_OK;
716}
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition mailbox.c:232
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition mailbox.h:180
@ NT_MAILBOX_INVALID
Email list was changed.
Definition mailbox.h:179
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:1469
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:337
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:364
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition hash.c:261
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:459
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition hash.h:111
#define MMC_CUR_DIR
'cur' directory changed
Definition mailbox.c:58
#define MMC_NO_DIRS
No directories changed.
Definition mailbox.c:56
#define MMC_NEW_DIR
'new' directory changed
Definition mailbox.c:57
bool maildir_update_flags(struct Mailbox *m, struct Email *e_old, struct Email *e_new)
Update the mailbox flags.
Definition shared.c:104
void maildir_canon_filename(struct Buffer *dest, const char *src)
Generate the canonical filename for a Maildir folder.
Definition shared.c:72
bool MonitorCurMboxChanged
Set to true when the current mailbox has changed.
Definition monitor.c:55
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:429
@ MX_STATUS_ERROR
An error occurred.
Definition mxapi.h:60
@ MX_STATUS_OK
No changes.
Definition mxapi.h:61
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition mxapi.h:65
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:64
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:62
bool purge
Skip trash folder when deleting.
Definition email.h:79
bool changed
Email has been edited.
Definition email.h:77
A Hash Table.
Definition hash.h:99
bool changed
Mailbox has been modified.
Definition mailbox.h:109
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_update_mtime()

void maildir_update_mtime ( struct Mailbox * m)

Update our record of the Maildir modification time.

Parameters
mMailbox

Definition at line 722 of file mailbox.c.

723{
724 char buf[PATH_MAX] = { 0 };
725 struct stat st = { 0 };
727 if (!mdata)
728 return;
729
730 snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "cur");
731 if (stat(buf, &st) == 0)
733
734 snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "new");
735 if (stat(buf, &st) == 0)
737}
+ Here is the call graph for this function:
+ Here is the caller graph for this function: