NeoMutt  2025-12-11-911-gd8d604
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 {
95 edata = e->edata;
96 }
97
98 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
99 const char *p = strrchr(path, c_maildir_field_delimiter);
100 if (p && mutt_str_startswith(p + 1, "2,"))
101 {
102 p += 3;
103
104 mutt_str_replace(&edata->custom_flags, p);
105 q = edata->custom_flags;
106
107 while (*p)
108 {
109 switch (*p)
110 {
111 case 'F': // Flagged
112 e->flagged = true;
113 break;
114
115 case 'R': // Replied
116 e->replied = true;
117 break;
118
119 case 'S': // Seen
120 e->read = true;
121 break;
122
123 case 'T': // Trashed
124 {
125 const bool c_flag_safe = cs_subset_bool(NeoMutt->sub, "flag_safe");
126 if (!e->flagged || !c_flag_safe)
127 {
128 e->trash = true;
129 e->deleted = true;
130 }
131 break;
132 }
133
134 default:
135 *q++ = *p;
136 break;
137 }
138 p++;
139 }
140 }
141
142 if (q == edata->custom_flags)
143 FREE(&edata->custom_flags);
144 else if (q)
145 *q = '\0';
146}
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 159 of file mailbox.c.

160{
161 if (!fp || !fname || !e)
162 return false;
163
164 const long size = mutt_file_get_size_fp(fp);
165 if (size == 0)
166 return false;
167
168 e->env = mutt_rfc822_read_header(fp, e, false, false);
169
170 if (e->received == 0)
171 e->received = e->date_sent;
172
173 /* always update the length since we have fresh information available. */
174 e->body->length = size - e->body->offset;
175
176 e->index = -1;
177
178 /* maildir stores its flags in the filename, so ignore the
179 * flags in the header of the message */
180 e->old = is_old;
181 maildir_parse_flags(e, fname);
182
183 return true;
184}
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1261
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1432
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 196 of file mailbox.c.

197{
198 if (!fname || !e)
199 return false;
200
201 FILE *fp = mutt_file_fopen(fname, "r");
202 if (!fp)
203 return false;
204
205 bool rc = maildir_parse_stream(fp, fname, is_old, e);
206 mutt_file_fclose(&fp);
207 return rc;
208}
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
bool maildir_parse_stream(FILE *fp, const char *fname, bool is_old, struct Email *e)
Parse a Maildir message.
Definition mailbox.c:159
+ 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 217 of file mailbox.c.

218{
219 if (!m)
220 return 0;
221
222 int oldmsgcount = m->msg_count;
223
224 struct MdEmail *md = NULL;
225 struct MdEmail **mdp = NULL;
226 ARRAY_FOREACH(mdp, mda)
227 {
228 md = *mdp;
229 mutt_debug(LL_DEBUG2, "Considering %s\n", NONULL(md->canon_fname));
230 if (!md->email)
231 continue;
232
233 mutt_debug(LL_DEBUG2, "Adding header structure. Flags: %s%s%s%s%s\n",
234 md->email->flagged ? "f" : "", md->email->deleted ? "D" : "",
235 md->email->replied ? "r" : "", md->email->old ? "O" : "",
236 md->email->read ? "R" : "");
238
239 m->emails[m->msg_count] = md->email;
240 m->emails[m->msg_count]->index = m->msg_count;
241 mailbox_size_add(m, md->email);
242
243 md->email = NULL;
244 m->msg_count++;
245 }
246
247 int num = 0;
248 if (m->msg_count > oldmsgcount)
249 num = m->msg_count - oldmsgcount;
250
251 return num;
252}
#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:90
struct Email ** emails
Array of Emails.
Definition mailbox.h:98
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 275 of file mailbox.c.

277{
278 struct dirent *de = NULL;
279 int rc = 0;
280 bool is_old = false;
281 struct MdEmail *entry = NULL;
282 struct Email *e = NULL;
283
284 struct Buffer *buf = buf_pool_get();
285
286 buf_printf(buf, "%s/%s", mailbox_path(m), subdir);
287 is_old = mutt_str_equal("cur", subdir);
288
290 if (!dir)
291 {
292 rc = -1;
293 goto cleanup;
294 }
295
296 while (((de = readdir(dir))) && !SigInt)
297 {
298 if (*de->d_name == '.')
299 continue;
300
301 mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
302
303 e = maildir_email_new();
304 e->old = is_old;
305 maildir_parse_flags(e, de->d_name);
306
307 progress_update(progress, ARRAY_SIZE(mda) + 1, -1);
308
309 buf_printf(buf, "%s/%s", subdir, de->d_name);
310 e->path = buf_strdup(buf);
311
312 entry = maildir_entry_new();
313 entry->email = e;
314 entry->inode = de->d_ino;
315 ARRAY_ADD(mda, entry);
316 }
317
318 closedir(dir);
319
320 if (SigInt)
321 {
322 SigInt = false;
323 rc = -2; /* action aborted */
324 goto cleanup;
325 }
326
327 ARRAY_SORT(mda, maildir_sort_inode, NULL);
328
329cleanup:
330 buf_pool_release(&buf);
331
332 return rc;
333}
#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:216
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition file.c:535
@ MUTT_OPENDIR_CREATE
Create the directory if it doesn't exist.
Definition file.h:69
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:257
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:665
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 341 of file mailbox.c.

343{
344 char fn[PATH_MAX] = { 0 };
345
346 struct HeaderCache *hc = maildir_hcache_open(m);
347
348 struct MdEmail *md = NULL;
349 struct MdEmail **mdp = NULL;
350 ARRAY_FOREACH(mdp, mda)
351 {
352 md = *mdp;
353 if (!md || !md->email || md->header_parsed)
354 continue;
355
356 progress_update(progress, ARRAY_FOREACH_IDX_mdp, -1);
357
358 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
359
360 struct Email *e = maildir_hcache_read(hc, md->email, fn);
361 if (e)
362 {
363 email_free(&md->email);
364 md->email = e;
365 }
366 else
367 {
368 if (maildir_parse_message(fn, md->email->old, md->email))
369 {
370 md->header_parsed = true;
372 }
373 else
374 {
375 email_free(&md->email);
376 }
377 }
378 }
379
381}
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:196
#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 392 of file mailbox.c.

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

489{
490 if (!m)
491 return -1;
492
493 mutt_path_tidy(&m->pathbuf, true);
494
495 struct Progress *progress = NULL;
496
497 if (m->verbose)
498 {
499 progress = progress_new(MUTT_PROGRESS_READ, 0);
500 progress_set_message(progress, _("Scanning %s..."), mailbox_path(m));
501 }
502
504 if (!mdata)
505 {
507 m->mdata = mdata;
509 }
510
511 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
512 int rc = maildir_parse_dir(m, &mda, subdir, progress);
513 progress_free(&progress);
514 if (rc < 0)
515 return -1;
516
517 if (m->verbose)
518 {
519 progress = progress_new(MUTT_PROGRESS_READ, ARRAY_SIZE(&mda));
520 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
521 }
522 maildir_delayed_parsing(m, &mda, progress);
523 progress_free(&progress);
524
526 maildirarray_clear(&mda);
527
528 if (!mdata->umask)
529 mdata->umask = maildir_umask(m);
530
531 return 0;
532}
#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:341
static int maildir_move_to_mailbox(struct Mailbox *m, const struct MdEmailArray *mda)
Copy the Maildir list to the Mailbox.
Definition mailbox.c:217
static int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda, const char *subdir, struct Progress *progress)
Read a Maildir mailbox.
Definition mailbox.c:275
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:145
void * mdata
Driver specific data.
Definition mailbox.h:134
struct Buffer pathbuf
Path of the Mailbox.
Definition mailbox.h:82
bool verbose
Display status messages?
Definition mailbox.h:119
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 546 of file mailbox.c.

547{
548 struct stat st_new = { 0 }; /* status of the "new" subdirectory */
549 struct stat st_cur = { 0 }; /* status of the "cur" subdirectory */
550 int changed = MMC_NO_DIRS; /* which subdirectories have changed */
551 bool occult = false; /* messages were removed from the mailbox */
552 int num_new = 0; /* number of new messages added to the mailbox */
553 bool flags_changed = false; /* message flags were changed in the mailbox */
554 struct HashTable *hash_names = NULL; // Hash Table: "base-filename" -> MdEmail
556 if (!mdata)
557 return MX_STATUS_ERROR;
558
559 const bool c_check_new = cs_subset_bool(NeoMutt->sub, "check_new");
560 if (!c_check_new)
561 return MX_STATUS_OK;
562
563 struct Buffer *buf = buf_pool_get();
564 buf_printf(buf, "%s/new", mailbox_path(m));
565 if (stat(buf_string(buf), &st_new) == -1)
566 {
567 buf_pool_release(&buf);
568 return MX_STATUS_ERROR;
569 }
570
571 buf_printf(buf, "%s/cur", mailbox_path(m));
572 if (stat(buf_string(buf), &st_cur) == -1)
573 {
574 buf_pool_release(&buf);
575 return MX_STATUS_ERROR;
576 }
577
578 /* determine which subdirectories need to be scanned */
579 if (mutt_file_stat_timespec_compare(&st_new, MUTT_STAT_MTIME, &mdata->mtime) > 0)
580 changed = MMC_NEW_DIR;
582 changed |= MMC_CUR_DIR;
583
584 if (changed == MMC_NO_DIRS)
585 {
586 buf_pool_release(&buf);
587 return MX_STATUS_OK; /* nothing to do */
588 }
589
590 /* Update the modification times on the mailbox.
591 *
592 * The monitor code notices changes in the open mailbox too quickly.
593 * In practice, this sometimes leads to all the new messages not being
594 * noticed during the SAME group of mtime stat updates. To work around
595 * the problem, don't update the stat times for a monitor caused check. */
596#ifdef USE_INOTIFY
598 {
599 MonitorCurMboxChanged = false;
600 }
601 else
602#endif
603 {
606 }
607
608 /* do a fast scan of just the filenames in
609 * the subdirectories that have changed. */
610 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
611 if (changed & MMC_NEW_DIR)
612 maildir_parse_dir(m, &mda, "new", NULL);
613 if (changed & MMC_CUR_DIR)
614 maildir_parse_dir(m, &mda, "cur", NULL);
615
616 /* we create a hash table keyed off the canonical (sans flags) filename
617 * of each message we scanned. This is used in the loop over the
618 * existing messages below to do some correlation. */
619 hash_names = mutt_hash_new(ARRAY_SIZE(&mda), MUTT_HASH_NONE);
620
621 struct MdEmail *md = NULL;
622 struct MdEmail **mdp = NULL;
623 ARRAY_FOREACH(mdp, &mda)
624 {
625 md = *mdp;
627 md->canon_fname = buf_strdup(buf);
628 mutt_hash_insert(hash_names, md->canon_fname, md);
629 }
630
631 /* check for modifications and adjust flags */
632 for (int i = 0; i < m->msg_count; i++)
633 {
634 struct Email *e = m->emails[i];
635 if (!e)
636 break;
637
639 md = mutt_hash_find(hash_names, buf_string(buf));
640 if (md && md->email)
641 {
642 /* message already exists, merge flags */
643
644 /* check to see if the message has moved to a different
645 * subdirectory. If so, update the associated filename. */
646 if (!mutt_str_equal(e->path, md->email->path))
647 mutt_str_replace(&e->path, md->email->path);
648
649 /* if the user hasn't modified the flags on this message, update
650 * the flags we just detected. */
651 if (!e->changed)
652 if (maildir_update_flags(m, e, md->email))
653 flags_changed = true;
654
655 if (e->deleted == e->trash)
656 {
657 if (e->deleted != md->email->deleted)
658 {
659 e->deleted = md->email->deleted;
660 flags_changed = true;
661 }
662 }
663 e->trash = md->email->trash;
664
665 /* this is a duplicate of an existing email, so remove it */
666 email_free(&md->email);
667 }
668 /* This message was not in the list of messages we just scanned.
669 * Check to see if we have enough information to know if the
670 * message has disappeared out from underneath us. */
671 else if (((changed & MMC_NEW_DIR) && mutt_strn_equal(e->path, "new/", 4)) ||
672 ((changed & MMC_CUR_DIR) && mutt_strn_equal(e->path, "cur/", 4)))
673 {
674 /* This message disappeared, so we need to simulate a "reopen"
675 * event. We know it disappeared because we just scanned the
676 * subdirectory it used to reside in. */
677 occult = true;
678 e->deleted = true;
679 e->purge = true;
680 }
681 else
682 {
683 /* This message resides in a subdirectory which was not
684 * modified, so we assume that it is still present and
685 * unchanged. */
686 }
687 }
688
689 /* destroy the file name hash */
690 mutt_hash_free(&hash_names);
691
692 /* If we didn't just get new mail, update the tables. */
693 if (occult)
695
696 /* do any delayed parsing we need to do. */
697 maildir_delayed_parsing(m, &mda, NULL);
698
699 /* Incorporate new messages */
700 num_new = maildir_move_to_mailbox(m, &mda);
701 maildirarray_clear(&mda);
702
703 if (num_new > 0)
704 {
706 m->changed = true;
707 }
708
709 buf_pool_release(&buf);
710
711 ARRAY_FREE(&mda);
712 if (occult)
713 return MX_STATUS_REOPENED;
714 if (num_new > 0)
715 return MX_STATUS_NEW_MAIL;
716 if (flags_changed)
717 return MX_STATUS_FLAGS;
718 return MX_STATUS_OK;
719}
#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:183
@ NT_MAILBOX_INVALID
Email list was changed.
Definition mailbox.h:182
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:1474
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
@ MUTT_HASH_NONE
No flags are set.
Definition hash.h:115
#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:71
@ MX_STATUS_OK
No changes.
Definition mxapi.h:72
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition mxapi.h:76
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:75
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:73
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:112
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 725 of file mailbox.c.

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