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

MH local mailbox type. More...

#include "config.h"
#include <ctype.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 "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 "sequence.h"
#include "monitor.h"
#include "hcache/lib.h"
+ Include dependency graph for mh.c:

Go to the source code of this file.

Functions

bool mh_mkstemp (struct Mailbox *m, FILE **fp, char **tgt)
 Create a temporary file.
 
static int mh_already_notified (struct Mailbox *m, int msgno)
 Has the message changed.
 
static bool mh_valid_message (const char *s)
 Is this a valid MH message filename.
 
int mh_check_empty (struct Buffer *path)
 Is mailbox empty.
 
static enum MxStatus mh_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -.
 
static void mh_update_maildir (struct MdEmailArray *mda, struct MhSequences *mhs)
 Update our record of flags.
 
static int mh_commit_msg (struct Mailbox *m, struct Message *msg, struct Email *e, bool updseq)
 Commit a message to an MH folder.
 
static int mh_rewrite_message (struct Mailbox *m, struct Email *e)
 Sync a message in an MH folder.
 
static int mh_sync_message (struct Mailbox *m, struct Email *e)
 Sync an email to an MH folder.
 
static void mh_update_mtime (struct Mailbox *m)
 Update our record of the Maildir modification time.
 
static int mh_parse_dir (struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
 Read a Maildir mailbox.
 
static int mh_sort_path (const void *a, const void *b, void *sdata)
 Compare two Maildirs by path - Implements sort_t -.
 
static struct Emailmh_parse_message (const char *fname, struct Email *e)
 Actually parse an MH message.
 
static void mh_delayed_parsing (struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
 This function does the second parsing pass.
 
static bool mh_read_dir (struct Mailbox *m)
 Read an MH mailbox.
 
int mh_sync_mailbox_message (struct Mailbox *m, struct Email *e, struct HeaderCache *hc)
 Save changes to the mailbox.
 
static int mh_msg_save_hcache (struct Mailbox *m, struct Email *e)
 Save message to the header cache - Implements MxOps::msg_save_hcache() -.
 
static bool mh_ac_owns_path (struct Account *a, const char *path)
 Check whether an Account own a Mailbox path - Implements MxOps::ac_owns_path() -.
 
static bool mh_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -.
 
static enum MxOpenReturns mh_mbox_open (struct Mailbox *m)
 Open a Mailbox - Implements MxOps::mbox_open() -.
 
static bool mh_mbox_open_append (struct Mailbox *m, OpenMailboxFlags flags)
 Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
 
static enum MxStatus mh_check (struct Mailbox *m)
 Check for new mail.
 
static enum MxStatus mh_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -.
 
static enum MxStatus mh_mbox_sync (struct Mailbox *m)
 Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
 
static enum MxStatus mh_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -.
 
static bool mh_msg_open (struct Mailbox *m, struct Message *msg, struct Email *e)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -.
 
static bool mh_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 mh_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -.
 
static int mh_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -.
 
static int mh_path_canon (struct Buffer *path)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
 
static int mh_path_parent (struct Buffer *path)
 Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
 
static int mh_path_pretty (struct Buffer *path, const char *folder)
 Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
 
static enum MailboxType mh_path_probe (const char *path, const struct stat *st)
 Is this an mh Mailbox? - Implements MxOps::path_probe() -.
 

Variables

const struct MxOps MxMhOps
 MH Mailbox - Implements MxOps -.
 

Detailed Description

MH 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 mh.c.

Function Documentation

◆ mh_mkstemp()

bool mh_mkstemp ( struct Mailbox m,
FILE **  fp,
char **  tgt 
)

Create a temporary file.

Parameters
[in]mMailbox to create the file in
[out]fpFile handle
[out]tgtFile name
Return values
trueSuccess
falseFailure

Definition at line 78 of file mh.c.

79{
80 int fd;
81 char path[PATH_MAX] = { 0 };
82
83 mode_t omask = umask(mh_umask(m));
84 while (true)
85 {
86 snprintf(path, sizeof(path), "%s/.neomutt-%s-%d-%" PRIu64, mailbox_path(m),
87 NONULL(ShortHostname), (int) getpid(), mutt_rand64());
88 fd = open(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
89 if (fd == -1)
90 {
91 if (errno != EEXIST)
92 {
93 mutt_perror("%s", path);
94 umask(omask);
95 return false;
96 }
97 }
98 else
99 {
100 *tgt = mutt_str_dup(path);
101 break;
102 }
103 }
104 umask(omask);
105
106 *fp = fdopen(fd, "w");
107 if (!*fp)
108 {
109 FREE(tgt);
110 close(fd);
111 unlink(path);
112 return false;
113 }
114
115 return true;
116}
char * ShortHostname
Short version of the hostname.
Definition: globals.c:40
#define mutt_perror(...)
Definition: logging2.h:93
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
mode_t mh_umask(struct Mailbox *m)
Create a umask from the mailbox directory.
Definition: shared.c:52
#define FREE(x)
Definition: memory.h:45
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
#define PATH_MAX
Definition: mutt.h:41
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:134
#define NONULL(x)
Definition: string2.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_already_notified()

static int mh_already_notified ( struct Mailbox m,
int  msgno 
)
static

Has the message changed.

Parameters
mMailbox
msgnoMessage number
Return values
1Modification time on the message file is older than the last visit to this mailbox
0Modification time on the message file is newer
-1Error

Definition at line 126 of file mh.c.

127{
128 char path[PATH_MAX] = { 0 };
129 struct stat st = { 0 };
130
131 if ((snprintf(path, sizeof(path), "%s/%d", mailbox_path(m), msgno) < sizeof(path)) &&
132 (stat(path, &st) == 0))
133 {
135 }
136 return -1;
137}
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_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:104
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_valid_message()

static bool mh_valid_message ( const char *  s)
static

Is this a valid MH message filename.

Parameters
sPathname to examine
Return values
truename is valid
falsename is invalid

Ignore the garbage files. A valid MH message consists of only digits. Deleted message get moved to a filename with a comma before it.

Definition at line 149 of file mh.c.

150{
151 for (; *s; s++)
152 {
153 if (!isdigit((unsigned char) *s))
154 return false;
155 }
156 return true;
157}
+ Here is the caller graph for this function:

◆ mh_check_empty()

int mh_check_empty ( struct Buffer path)

Is mailbox empty.

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

Definition at line 166 of file mh.c.

167{
168 struct dirent *de = NULL;
169 int rc = 1; /* assume empty until we find a message */
170
172 if (!dir)
173 return -1;
174 while ((de = readdir(dir)))
175 {
176 if (mh_valid_message(de->d_name))
177 {
178 rc = 0;
179 break;
180 }
181 }
182 closedir(dir);
183
184 return rc;
185}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:616
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition: file.h:73
static bool mh_valid_message(const char *s)
Is this a valid MH message filename.
Definition: mh.c:149
+ Here is the call graph for this function:

◆ mh_update_maildir()

static void mh_update_maildir ( struct MdEmailArray *  mda,
struct MhSequences mhs 
)
static

Update our record of flags.

Parameters
mdaMaildir array to update
mhsSequences

Definition at line 260 of file mh.c.

261{
262 struct MdEmail *md = NULL;
263 struct MdEmail **mdp = NULL;
264 ARRAY_FOREACH(mdp, mda)
265 {
266 md = *mdp;
267 char *p = strrchr(md->email->path, '/');
268 if (p)
269 p++;
270 else
271 p = md->email->path;
272
273 int i = 0;
274 if (!mutt_str_atoi_full(p, &i))
275 continue;
276 MhSeqFlags flags = mh_seq_check(mhs, i);
277
278 md->email->read = !(flags & MH_SEQ_UNSEEN);
279 md->email->flagged = (flags & MH_SEQ_FLAGGED);
280 md->email->replied = (flags & MH_SEQ_REPLIED);
281 }
282}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:211
MhSeqFlags mh_seq_check(struct MhSequences *mhs, int i)
Get the flags for a given sequence.
Definition: sequence.c:78
#define MH_SEQ_UNSEEN
Email hasn't been read.
Definition: sequence.h:33
#define MH_SEQ_REPLIED
Email has been replied to.
Definition: sequence.h:34
uint8_t MhSeqFlags
Flags, e.g. MH_SEQ_UNSEEN.
Definition: sequence.h:31
#define MH_SEQ_FLAGGED
Email is flagged.
Definition: sequence.h:35
bool read
Email is read.
Definition: email.h:48
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
A Maildir Email helper.
Definition: mdemail.h:34
struct Email * email
Definition: mdemail.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_commit_msg()

static int mh_commit_msg ( struct Mailbox m,
struct Message msg,
struct Email e,
bool  updseq 
)
static

Commit a message to an MH folder.

Parameters
mMailbox
msgMessage to commit
eEmail
updseqIf true, update the sequence number
Return values
0Success
-1Failure

Definition at line 293 of file mh.c.

294{
295 struct dirent *de = NULL;
296 char *cp = NULL, *dep = NULL;
297 unsigned int n, hi = 0;
298 char path[PATH_MAX] = { 0 };
299 char tmp[16] = { 0 };
300
301 if (mutt_file_fsync_close(&msg->fp))
302 {
303 mutt_perror(_("Could not flush message to disk"));
304 return -1;
305 }
306
308 if (!dir)
309 {
310 mutt_perror("%s", mailbox_path(m));
311 return -1;
312 }
313
314 /* figure out what the next message number is */
315 while ((de = readdir(dir)))
316 {
317 dep = de->d_name;
318 if (*dep == ',')
319 dep++;
320 cp = dep;
321 while (*cp)
322 {
323 if (!isdigit((unsigned char) *cp))
324 break;
325 cp++;
326 }
327 if (*cp == '\0')
328 {
329 if (!mutt_str_atoui(dep, &n))
330 mutt_debug(LL_DEBUG2, "Invalid MH message number '%s'\n", dep);
331 if (n > hi)
332 hi = n;
333 }
334 }
335 closedir(dir);
336
337 /* Now try to rename the file to the proper name.
338 * Note: We may have to try multiple times, until we find a free slot. */
339
340 while (true)
341 {
342 hi++;
343 snprintf(tmp, sizeof(tmp), "%u", hi);
344 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), tmp);
345 if (mutt_file_safe_rename(msg->path, path) == 0)
346 {
347 if (e)
348 mutt_str_replace(&e->path, tmp);
349 mutt_str_replace(&msg->committed_path, path);
350 FREE(&msg->path);
351 break;
352 }
353 else if (errno != EEXIST)
354 {
355 mutt_perror("%s", mailbox_path(m));
356 return -1;
357 }
358 }
359 if (updseq)
360 {
361 mh_seq_add_one(m, hi, !msg->flags.read, msg->flags.flagged, msg->flags.replied);
362 }
363 return 0;
364}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:213
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
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
#define _(a)
Definition: message.h:28
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
void mh_seq_add_one(struct Mailbox *m, int n, bool unseen, bool flagged, bool replied)
Update the flags for one sequence.
Definition: sequence.c:107
FILE * fp
pointer to the message data
Definition: message.h:35
char * path
path to temp file
Definition: message.h:36
bool replied
Message has been replied to.
Definition: message.h:43
char * committed_path
the final path generated by mx_msg_commit()
Definition: message.h:37
bool flagged
Message is flagged.
Definition: message.h:42
bool read
Message has been read.
Definition: message.h:41
struct Message::@0 flags
Flags for the Message.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_rewrite_message()

static int mh_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 373 of file mh.c.

374{
375 if (!m || !e)
376 return -1;
377
378 bool restore = true;
379
380 long old_body_offset = e->body->offset;
381 long old_body_length = e->body->length;
382 long old_hdr_lines = e->lines;
383
384 struct Message *src = mx_msg_open(m, e);
385 struct Message *dest = mx_msg_open_new(m, e, MUTT_MSG_NO_FLAGS);
386 if (!src || !dest)
387 return -1;
388
389 int rc = mutt_copy_message(dest->fp, e, src, MUTT_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN, 0);
390 if (rc == 0)
391 {
392 char oldpath[PATH_MAX] = { 0 };
393 char partpath[PATH_MAX] = { 0 };
394 snprintf(oldpath, sizeof(oldpath), "%s/%s", mailbox_path(m), e->path);
395 mutt_str_copy(partpath, e->path, sizeof(partpath));
396
397 rc = mh_commit_msg(m, dest, e, false);
398
399 if (rc == 0)
400 {
401 unlink(oldpath);
402 restore = false;
403 }
404
405 /* Try to move the new message to the old place.
406 * (MH only.)
407 *
408 * This is important when we are just updating flags.
409 *
410 * Note that there is a race condition against programs which
411 * use the first free slot instead of the maximum message
412 * number. NeoMutt does _not_ behave like this.
413 *
414 * Anyway, if this fails, the message is in the folder, so
415 * all what happens is that a concurrently running neomutt will
416 * lose flag modifications. */
417 if (rc == 0)
418 {
419 char newpath[PATH_MAX] = { 0 };
420 snprintf(newpath, sizeof(newpath), "%s/%s", mailbox_path(m), e->path);
421 rc = mutt_file_safe_rename(newpath, oldpath);
422 if (rc == 0)
423 mutt_str_replace(&e->path, partpath);
424 }
425 }
426 mx_msg_close(m, &src);
427 mx_msg_close(m, &dest);
428
429 if ((rc == -1) && restore)
430 {
431 e->body->offset = old_body_offset;
432 e->body->length = old_body_length;
433 e->lines = old_hdr_lines;
434 }
435
437 return rc;
438}
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 mh_commit_msg(struct Mailbox *m, struct Message *msg, struct Email *e, bool updseq)
Commit a message to an MH folder.
Definition: mh.c:293
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
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:

◆ mh_sync_message()

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

Sync an email to an MH folder.

Parameters
mMailbox
eEmail
Return values
0Success
-1Error

Definition at line 447 of file mh.c.

448{
449 if (!m || !e)
450 return -1;
451
452 if (e->attach_del || e->env->changed)
453 {
454 if (mh_rewrite_message(m, e) != 0)
455 return -1;
456 e->env->changed = false;
457 }
458
459 return 0;
460}
static int mh_rewrite_message(struct Mailbox *m, struct Email *e)
Sync a message in an MH folder.
Definition: mh.c:373
struct Envelope * env
Envelope information.
Definition: email.h:66
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
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:

◆ mh_update_mtime()

static void mh_update_mtime ( struct Mailbox m)
static

Update our record of the Maildir modification time.

Parameters
mMailbox

Definition at line 466 of file mh.c.

467{
468 char buf[PATH_MAX] = { 0 };
469 struct stat st = { 0 };
471
472 snprintf(buf, sizeof(buf), "%s/.mh_sequences", mailbox_path(m));
473 if (stat(buf, &st) == 0)
475
476 mutt_str_copy(buf, mailbox_path(m), sizeof(buf));
477
478 if (stat(buf, &st) == 0)
480}
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:

◆ mh_parse_dir()

static int mh_parse_dir ( struct Mailbox m,
struct MdEmailArray *  mda,
struct Progress *  progress 
)
static

Read a Maildir mailbox.

Parameters
[in]mMailbox
[out]mdaArray for results
[in]progressProgress bar
Return values
0Success
-1Error
-2Aborted

Definition at line 491 of file mh.c.

492{
493 struct dirent *de = NULL;
494 int rc = 0;
495 struct MdEmail *entry = NULL;
496 struct Email *e = NULL;
497
498 struct Buffer *buf = buf_pool_get();
499 buf_strcpy(buf, mailbox_path(m));
500
502 if (!dir)
503 {
504 rc = -1;
505 goto cleanup;
506 }
507
508 while (((de = readdir(dir))) && !SigInt)
509 {
510 if (!mh_valid_message(de->d_name))
511 continue;
512
513 mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
514
515 e = email_new();
518
519 progress_update(progress, ARRAY_SIZE(mda) + 1, -1);
520
521 e->path = mutt_str_dup(de->d_name);
522
523 entry = maildir_entry_new();
524 entry->email = e;
525 ARRAY_ADD(mda, entry);
526 }
527
528 closedir(dir);
529
530 if (SigInt)
531 {
532 SigInt = false;
533 return -2; /* action aborted */
534 }
535
536cleanup:
537 buf_pool_release(&buf);
538
539 return rc;
540}
#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
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:59
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
struct MdEmail * maildir_entry_new(void)
Create a new Maildir entry.
Definition: mdemail.c:39
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
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
String manipulation buffer.
Definition: buffer.h:34
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:

◆ mh_parse_message()

static struct Email * mh_parse_message ( const char *  fname,
struct Email e 
)
static

Actually parse an MH message.

Parameters
fnameMessage filename
eEmail to populate (OPTIONAL)
Return values
ptrPopulated Email

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

Definition at line 561 of file mh.c.

562{
563 FILE *fp = fopen(fname, "r");
564 if (!fp)
565 {
566 return NULL;
567 }
568
569 const long size = mutt_file_get_size_fp(fp);
570 if (size == 0)
571 {
572 mutt_file_fclose(&fp);
573 return NULL;
574 }
575
576 if (!e)
577 {
578 e = email_new();
581 }
582 e->env = mutt_rfc822_read_header(fp, e, false, false);
583
584 if (e->received != 0)
585 e->received = e->date_sent;
586
587 /* always update the length since we have fresh information available. */
588 e->body->length = size - e->body->offset;
589 e->index = -1;
590
591 mutt_file_fclose(&fp);
592 return e;
593}
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1170
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1578
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:

◆ mh_delayed_parsing()

static void mh_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 601 of file mh.c.

603{
604 char fn[PATH_MAX] = { 0 };
605
606#ifdef USE_HCACHE
607 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
608 struct HeaderCache *hc = hcache_open(c_header_cache, mailbox_path(m), NULL);
609#endif
610
611 struct MdEmail *md = NULL;
612 struct MdEmail **mdp = NULL;
613 ARRAY_FOREACH(mdp, mda)
614 {
615 md = *mdp;
616 if (!md || !md->email || md->header_parsed)
617 continue;
618
619 progress_update(progress, ARRAY_FOREACH_IDX, -1);
620
621 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
622
623#ifdef USE_HCACHE
624 struct stat st_lastchanged = { 0 };
625 int rc = 0;
626 const bool c_maildir_header_cache_verify = cs_subset_bool(NeoMutt->sub, "maildir_header_cache_verify");
627 if (c_maildir_header_cache_verify)
628 {
629 rc = stat(fn, &st_lastchanged);
630 }
631
632 const char *key = md->email->path;
633 size_t keylen = strlen(key);
634 struct HCacheEntry hce = hcache_fetch(hc, key, keylen, 0);
635
636 if (hce.email && (rc == 0) && (st_lastchanged.st_mtime <= hce.uidvalidity))
637 {
640 hce.email->old = md->email->old;
641 hce.email->path = mutt_str_dup(md->email->path);
642 email_free(&md->email);
643 md->email = hce.email;
644 }
645 else
646#endif
647 {
648 if (mh_parse_message(fn, md->email))
649 {
650 md->header_parsed = true;
651#ifdef USE_HCACHE
652 key = md->email->path;
653 keylen = strlen(key);
654 hcache_store(hc, key, keylen, md->email, 0);
655#endif
656 }
657 else
658 {
659 email_free(&md->email);
660 }
661 }
662 }
663#ifdef USE_HCACHE
664 hcache_close(&hc);
665#endif
666
667 const enum SortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
668 if (m && mda && (ARRAY_SIZE(mda) > 0) && (c_sort == SORT_ORDER))
669 {
670 mutt_debug(LL_DEBUG3, "maildir: sorting %s into natural order\n", mailbox_path(m));
671 ARRAY_SORT(mda, mh_sort_path, NULL);
672 }
673}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:278
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:267
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
static int mh_sort_path(const void *a, const void *b, void *sdata)
Compare two Maildirs by path - Implements sort_t -.
Definition: mh.c:545
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
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
static struct Email * mh_parse_message(const char *fname, struct Email *e)
Actually parse an MH message.
Definition: mh.c:561
SortType
Methods for sorting.
Definition: sort2.h:38
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:44
bool old
Email is seen, but unread.
Definition: email.h:47
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
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:

◆ mh_read_dir()

static bool mh_read_dir ( struct Mailbox m)
static

Read an MH mailbox.

Parameters
mMailbox
Return values
trueSuccess
falseError

Definition at line 681 of file mh.c.

682{
683 if (!m)
684 return false;
685
686 struct MhSequences mhs = { 0 };
687 struct Progress *progress = NULL;
688
689 if (m->verbose)
690 {
691 char msg[PATH_MAX] = { 0 };
692 snprintf(msg, sizeof(msg), _("Scanning %s..."), mailbox_path(m));
693 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
694 }
695
697 if (!mdata)
698 {
700 m->mdata = mdata;
702 }
703
705
706 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
707 int rc = mh_parse_dir(m, &mda, progress);
708 progress_free(&progress);
709 if (rc < 0)
710 return false;
711
712 if (m->verbose)
713 {
714 char msg[PATH_MAX] = { 0 };
715 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
716 progress = progress_new(msg, MUTT_PROGRESS_READ, ARRAY_SIZE(&mda));
717 }
718 mh_delayed_parsing(m, &mda, progress);
719 progress_free(&progress);
720
721 if (mh_seq_read(&mhs, mailbox_path(m)) < 0)
722 {
723 maildirarray_clear(&mda);
724 return false;
725 }
726 mh_update_maildir(&mda, &mhs);
727 mh_seq_free(&mhs);
728
730 maildirarray_clear(&mda);
731
732 if (!mdata->mh_umask)
733 mdata->mh_umask = mh_umask(m);
734
735 return true;
736}
#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
int maildir_move_to_mailbox(struct Mailbox *m, const struct MdEmailArray *mda)
Copy the Maildir list to the Mailbox.
Definition: shared.c:75
void maildirarray_clear(struct MdEmailArray *mda)
Free a Maildir array.
Definition: mdemail.c:64
static void mh_update_mtime(struct Mailbox *m)
Update our record of the Maildir modification time.
Definition: mh.c:466
static void mh_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
This function does the second parsing pass.
Definition: mh.c:601
static void mh_update_maildir(struct MdEmailArray *mda, struct MhSequences *mhs)
Update our record of flags.
Definition: mh.c:260
static int mh_parse_dir(struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
Read a Maildir mailbox.
Definition: mh.c:491
@ 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 mh_seq_free(struct MhSequences *mhs)
Free some sequences.
Definition: sequence.c:67
int mh_seq_read(struct MhSequences *mhs, const char *path)
Read a set of MH sequences.
Definition: sequence.c:377
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
Set of MH sequence numbers.
Definition: sequence.h:41
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_sync_mailbox_message()

int mh_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
0Success
-1Error

Definition at line 746 of file mh.c.

747{
748 if (!m || !e)
749 return -1;
750
751 if (e->deleted)
752 {
753 char path[PATH_MAX] = { 0 };
754 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
755 const bool c_mh_purge = cs_subset_bool(NeoMutt->sub, "mh_purge");
756 if (c_mh_purge)
757 {
758#ifdef USE_HCACHE
759 if (hc)
760 {
761 const char *key = e->path;
762 size_t keylen = strlen(key);
763 hcache_delete_record(hc, key, keylen);
764 }
765#endif
766 unlink(path);
767 }
768 else
769 {
770 /* MH just moves files out of the way when you delete them */
771 if (*e->path != ',')
772 {
773 char tmp[PATH_MAX] = { 0 };
774 snprintf(tmp, sizeof(tmp), "%s/,%s", mailbox_path(m), e->path);
775 unlink(tmp);
776 if (rename(path, tmp) != 0)
777 {
778 return -1;
779 }
780 }
781 }
782 }
783 else if (e->changed || e->attach_del)
784 {
785 if (mh_sync_message(m, e) == -1)
786 return -1;
787 }
788
789#ifdef USE_HCACHE
790 if (hc && e->changed)
791 {
792 const char *key = e->path;
793 size_t keylen = strlen(key);
794 hcache_store(hc, key, keylen, e, 0);
795 }
796#endif
797
798 return 0;
799}
int hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:759
static int mh_sync_message(struct Mailbox *m, struct Email *e)
Sync an email to an MH folder.
Definition: mh.c:447
bool changed
Email has been edited.
Definition: email.h:75
bool deleted
Email is deleted.
Definition: email.h:76
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_check()

static enum MxStatus mh_check ( struct Mailbox m)
static

Check for new mail.

Parameters
mMailbox
Return values
enumMxStatus

This function handles arrival of new mail and reopening of mh/maildir folders. Things are getting rather complex because we don't have a well-defined "mailbox order", so the tricks from mbox.c and mx.c won't work here.

Don't change this code unless you really understand what happens.

Definition at line 880 of file mh.c.

881{
882 char buf[PATH_MAX] = { 0 };
883 struct stat st = { 0 };
884 struct stat st_cur = { 0 };
885 bool modified = false, occult = false, flags_changed = false;
886 int num_new = 0;
887 struct MhSequences mhs = { 0 };
888 struct HashTable *fnames = NULL;
890
891 const bool c_check_new = cs_subset_bool(NeoMutt->sub, "check_new");
892 if (!c_check_new)
893 return MX_STATUS_OK;
894
895 mutt_str_copy(buf, mailbox_path(m), sizeof(buf));
896 if (stat(buf, &st) == -1)
897 return MX_STATUS_ERROR;
898
899 /* create .mh_sequences when there isn't one. */
900 snprintf(buf, sizeof(buf), "%s/.mh_sequences", mailbox_path(m));
901 int rc = stat(buf, &st_cur);
902 if ((rc == -1) && (errno == ENOENT))
903 {
904 char *tmp = NULL;
905 FILE *fp = NULL;
906
907 if (mh_mkstemp(m, &fp, &tmp))
908 {
909 mutt_file_fclose(&fp);
910 if (mutt_file_safe_rename(tmp, buf) == -1)
911 unlink(tmp);
912 FREE(&tmp);
913 }
914 }
915
916 if ((rc == -1) && (stat(buf, &st_cur) == -1))
917 modified = true;
918
919 if ((mutt_file_stat_timespec_compare(&st, MUTT_STAT_MTIME, &mdata->mtime) > 0) ||
920 (mutt_file_stat_timespec_compare(&st_cur, MUTT_STAT_MTIME, &mdata->mtime_cur) > 0))
921 {
922 modified = true;
923 }
924
925 if (!modified)
926 return MX_STATUS_OK;
927
928 /* Update the modification times on the mailbox.
929 *
930 * The monitor code notices changes in the open mailbox too quickly.
931 * In practice, this sometimes leads to all the new messages not being
932 * noticed during the SAME group of mtime stat updates. To work around
933 * the problem, don't update the stat times for a monitor caused check. */
934#ifdef USE_INOTIFY
936 {
937 MonitorContextChanged = false;
938 }
939 else
940#endif
941 {
944 }
945
946 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
947
948 mh_parse_dir(m, &mda, NULL);
949 mh_delayed_parsing(m, &mda, NULL);
950
951 if (mh_seq_read(&mhs, mailbox_path(m)) < 0)
952 return MX_STATUS_ERROR;
953 mh_update_maildir(&mda, &mhs);
954 mh_seq_free(&mhs);
955
956 /* check for modifications and adjust flags */
958
959 struct MdEmail *md = NULL;
960 struct MdEmail **mdp = NULL;
961 ARRAY_FOREACH(mdp, &mda)
962 {
963 md = *mdp;
964 /* the hash key must survive past the header, which is freed below. */
966 mutt_hash_insert(fnames, md->canon_fname, md);
967 }
968
969 for (int i = 0; i < m->msg_count; i++)
970 {
971 struct Email *e = m->emails[i];
972 if (!e)
973 break;
974
975 md = mutt_hash_find(fnames, e->path);
976 if (md && md->email && email_cmp_strict(e, md->email))
977 {
978 /* found the right message */
979 if (!e->changed)
980 if (maildir_update_flags(m, e, md->email))
981 flags_changed = true;
982
983 email_free(&md->email);
984 }
985 else /* message has disappeared */
986 {
987 occult = true;
988 }
989 }
990
991 /* destroy the file name hash */
992
993 mutt_hash_free(&fnames);
994
995 /* If we didn't just get new mail, update the tables. */
996 if (occult)
998
999 /* Incorporate new messages */
1000 num_new = maildir_move_to_mailbox(m, &mda);
1001 maildirarray_clear(&mda);
1002
1003 if (num_new > 0)
1004 {
1006 m->changed = true;
1007 }
1008
1009 ARRAY_FREE(&mda);
1010 if (occult)
1011 return MX_STATUS_REOPENED;
1012 if (num_new > 0)
1013 return MX_STATUS_NEW_MAIL;
1014 if (flags_changed)
1015 return MX_STATUS_FLAGS;
1016 return MX_STATUS_OK;
1017}
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
bool email_cmp_strict(const struct Email *e1, const struct Email *e2)
Strictly compare message emails.
Definition: email.c:100
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
bool mh_mkstemp(struct Mailbox *m, FILE **fp, char **tgt)
Create a temporary file.
Definition: mh.c:78
bool MonitorContextChanged
Set to true when the current mailbox has changed.
Definition: monitor.c:51
@ 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
A Hash Table.
Definition: hash.h:98
bool changed
Mailbox has been modified.
Definition: mailbox.h:109
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
char * canon_fname
Definition: mdemail.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function: