NeoMutt  2025-12-11-694-ga89709
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mh.c File Reference

MH local mailbox type. More...

#include "config.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "mutt.h"
#include "progress/lib.h"
#include "mdata.h"
#include "mhemail.h"
#include "mx.h"
#include "sequence.h"
#include "shared.h"
#include "monitor.h"
#include "hcache/lib.h"
+ Include dependency graph for mh.c:

Go to the source code of this file.

Functions

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_emails (struct MhEmailArray *mha, 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 mailbox modification time.
 
static int mh_parse_dir (struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
 Read an Mh mailbox.
 
static int mh_sort_path (const void *a, const void *b, void *sdata)
 Compare two Mh Mailboxes 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 MhEmailArray *mha, struct Progress *progress)
 This function does the second parsing pass.
 
static int mh_move_to_mailbox (struct Mailbox *m, const struct MhEmailArray *mha)
 Copy the Mh list to the Mailbox.
 
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 owns 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 bool mh_update_flags (struct Mailbox *m, struct Email *e_old, struct Email *e_new)
 Update the mailbox flags.
 
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 enum MailboxType mh_path_probe (const char *path, const struct stat *st)
 Is this an mh Mailbox?
 

Variables

const struct MxOps MxMhOps
 MH Mailbox - Implements MxOps -.
 

Detailed Description

MH local mailbox type.

Authors
  • Richard Russon
  • Austin Ray
  • Pietro Cerutti
  • Dennis Schön
  • Thomas Klausner

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_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 77 of file mh.c.

78{
79 char path[PATH_MAX] = { 0 };
80 struct stat st = { 0 };
81
82 if ((snprintf(path, sizeof(path), "%s/%d", mailbox_path(m), msgno) < sizeof(path)) &&
83 (stat(path, &st) == 0))
84 {
86 }
87 return -1;
88}
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:213
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_MTIME
File/dir's mtime - last modified time.
Definition file.h:54
#define PATH_MAX
Definition mutt.h:49
struct timespec last_visited
Time of last exit from this mailbox.
Definition mailbox.h:103
+ 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 99 of file mh.c.

100{
101 for (; *s; s++)
102 {
103 if (!mutt_isdigit(*s))
104 return false;
105 }
106 return true;
107}
bool mutt_isdigit(int arg)
Wrapper for isdigit(3)
Definition ctype.c:66
+ Here is the call graph for this function:
+ 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 116 of file mh.c.

117{
118 struct dirent *de = NULL;
119 int rc = 1; /* assume empty until we find a message */
120
122 if (!dir)
123 return -1;
124 while ((de = readdir(dir)))
125 {
126 if (mh_valid_message(de->d_name))
127 {
128 rc = 0;
129 break;
130 }
131 }
132 closedir(dir);
133
134 return rc;
135}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition file.c:535
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition file.h:63
static bool mh_valid_message(const char *s)
Is this a valid MH message filename.
Definition mh.c:99
+ Here is the call graph for this function:

◆ mh_update_emails()

static void mh_update_emails ( struct MhEmailArray * mha,
struct MhSequences * mhs )
static

Update our record of flags.

Parameters
mhaMh array to update
mhsSequences

Definition at line 210 of file mh.c.

211{
212 struct MhEmail *md = NULL;
213 struct MhEmail **mdp = NULL;
214 ARRAY_FOREACH(mdp, mha)
215 {
216 md = *mdp;
217 char *p = strrchr(md->email->path, '/');
218 if (p)
219 p++;
220 else
221 p = md->email->path;
222
223 int i = 0;
224 if (!mutt_str_atoi_full(p, &i))
225 continue;
226 MhSeqFlags flags = mh_seq_check(mhs, i);
227
228 md->email->read = !(flags & MH_SEQ_UNSEEN);
229 md->email->flagged = (flags & MH_SEQ_FLAGGED);
230 md->email->replied = (flags & MH_SEQ_REPLIED);
231 }
232}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
MhSeqFlags mh_seq_check(struct MhSequences *mhs, int i)
Get the flags for a given sequence.
Definition sequence.c:79
#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: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
A Mh Email helper.
Definition mhemail.h:36
struct Email * email
Temporary Email.
Definition mhemail.h:37
+ 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 243 of file mh.c.

244{
245 struct dirent *de = NULL;
246 char *cp = NULL, *dep = NULL;
247 unsigned int n, hi = 0;
248 char path[PATH_MAX] = { 0 };
249 char tmp[16] = { 0 };
250
251 if (mutt_file_fsync_close(&msg->fp))
252 {
253 mutt_perror(_("Could not flush message to disk"));
254 return -1;
255 }
256
258 if (!dir)
259 {
260 mutt_perror("%s", mailbox_path(m));
261 return -1;
262 }
263
264 /* figure out what the next message number is */
265 while ((de = readdir(dir)))
266 {
267 dep = de->d_name;
268 if (*dep == ',')
269 dep++;
270 cp = dep;
271 while (*cp)
272 {
273 if (!mutt_isdigit(*cp))
274 break;
275 cp++;
276 }
277 if (*cp == '\0')
278 {
279 if (!mutt_str_atoui(dep, &n))
280 {
281 mutt_debug(LL_DEBUG2, "Invalid MH message number '%s'\n", dep);
282 }
283 else if (n > hi)
284 {
285 hi = n;
286 }
287 }
288 }
289 closedir(dir);
290
291 /* Now try to rename the file to the proper name.
292 * Note: We may have to try multiple times, until we find a free slot. */
293
294 while (true)
295 {
296 hi++;
297 snprintf(tmp, sizeof(tmp), "%u", hi);
298 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), tmp);
299 if (mutt_file_safe_rename(msg->path, path) == 0)
300 {
301 if (e)
302 mutt_str_replace(&e->path, tmp);
303 mutt_str_replace(&msg->committed_path, path);
304 FREE(&msg->path);
305 break;
306 }
307 else if (errno != EEXIST)
308 {
309 mutt_perror("%s", mailbox_path(m));
310 return -1;
311 }
312 }
313 if (updseq)
314 {
315 mh_seq_add_one(m, hi, !msg->flags.read, msg->flags.flagged, msg->flags.replied);
316 }
317 return 0;
318}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition file.c:310
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition file.c:128
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define _(a)
Definition message.h:28
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
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:110
FILE * fp
pointer to the message data
Definition message.h:35
char * path
path to temp file
Definition message.h:36
struct Message::@264267271004327071125374067057142037276212342100 flags
Flags for the Message.
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
+ 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 327 of file mh.c.

328{
329 if (!m || !e)
330 return -1;
331
332 bool restore = true;
333
334 long old_body_offset = e->body->offset;
335 long old_body_length = e->body->length;
336 long old_hdr_lines = e->lines;
337
338 struct Message *src = mx_msg_open(m, e);
339 struct Message *dest = mx_msg_open_new(m, e, MUTT_MSG_NO_FLAGS);
340 if (!src || !dest)
341 return -1;
342
343 int rc = mutt_copy_message(dest->fp, e, src, MUTT_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN, 0);
344 if (rc == 0)
345 {
346 char oldpath[PATH_MAX] = { 0 };
347 char partpath[PATH_MAX] = { 0 };
348 snprintf(oldpath, sizeof(oldpath), "%s/%s", mailbox_path(m), e->path);
349 mutt_str_copy(partpath, e->path, sizeof(partpath));
350
351 rc = mh_commit_msg(m, dest, e, false);
352
353 if (rc == 0)
354 {
355 unlink(oldpath);
356 restore = false;
357 }
358
359 /* Try to move the new message to the old place.
360 * (MH only.)
361 *
362 * This is important when we are just updating flags.
363 *
364 * Note that there is a race condition against programs which
365 * use the first free slot instead of the maximum message
366 * number. NeoMutt does _not_ behave like this.
367 *
368 * Anyway, if this fails, the message is in the folder, so
369 * all what happens is that a concurrently running neomutt will
370 * lose flag modifications. */
371 if (rc == 0)
372 {
373 char newpath[PATH_MAX] = { 0 };
374 snprintf(newpath, sizeof(newpath), "%s/%s", mailbox_path(m), e->path);
375 rc = mutt_file_safe_rename(newpath, oldpath);
376 if (rc == 0)
377 mutt_str_replace(&e->path, partpath);
378 }
379 }
380 mx_msg_close(m, &src);
381 mx_msg_close(m, &dest);
382
383 if ((rc == -1) && restore)
384 {
385 e->body->offset = old_body_offset;
386 e->body->length = old_body_length;
387 e->lines = old_hdr_lines;
388 }
389
391 return rc;
392}
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_email.c:917
#define MUTT_CM_UPDATE
Update structs on sync.
Definition copy_email.h:42
#define CH_UPDATE
Update the status and x-status fields?
Definition copy_email.h:56
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition copy_email.h:66
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition body.c:58
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:243
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:586
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition mx.c:1182
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition mx.c:1136
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition mx.c:1041
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition mx.h:38
struct Body * parts
parts of a multipart or message/rfc822
Definition body.h:73
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:62
struct Body * body
List of MIME parts.
Definition email.h:69
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 401 of file mh.c.

402{
403 if (!m || !e)
404 return -1;
405
406 if (e->attach_del || e->env->changed)
407 {
408 if (mh_rewrite_message(m, e) != 0)
409 return -1;
410 e->env->changed = false;
411 }
412
413 return 0;
414}
static int mh_rewrite_message(struct Mailbox *m, struct Email *e)
Sync a message in an MH folder.
Definition mh.c:327
struct Envelope * env
Envelope information.
Definition email.h:68
bool attach_del
Has an attachment marked for deletion.
Definition email.h:99
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition envelope.h:90
+ 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 mailbox modification time.

Parameters
mMailbox

Definition at line 420 of file mh.c.

421{
422 char buf[PATH_MAX] = { 0 };
423 struct stat st = { 0 };
424 struct MhMboxData *mdata = mh_mdata_get(m);
425
426 snprintf(buf, sizeof(buf), "%s/.mh_sequences", mailbox_path(m));
427 if (stat(buf, &st) == 0)
429
430 mutt_str_copy(buf, mailbox_path(m), sizeof(buf));
431
432 if (stat(buf, &st) == 0)
434}
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 MhMboxData * mh_mdata_get(struct Mailbox *m)
Get the private data for this Mailbox.
Definition mdata.c:59
void * mdata
Driver specific data.
Definition mailbox.h:131
Mh-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 MhEmailArray * mha,
struct Progress * progress )
static

Read an Mh mailbox.

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

Definition at line 445 of file mh.c.

446{
447 struct dirent *de = NULL;
448 int rc = 0;
449 struct MhEmail *entry = NULL;
450 struct Email *e = NULL;
451
452 struct Buffer *buf = buf_pool_get();
453 buf_strcpy(buf, mailbox_path(m));
454
456 if (!dir)
457 {
458 rc = -1;
459 goto cleanup;
460 }
461
462 while (((de = readdir(dir))) && !SigInt)
463 {
464 if (!mh_valid_message(de->d_name))
465 continue;
466
467 mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
468
469 e = email_new();
470
471 progress_update(progress, ARRAY_SIZE(mha) + 1, -1);
472
473 e->path = mutt_str_dup(de->d_name);
474
475 entry = mh_entry_new();
476 entry->email = e;
477 ARRAY_ADD(mha, entry);
478 }
479
480 closedir(dir);
481
482 if (SigInt)
483 {
484 SigInt = false;
485 rc = -2; /* action aborted */
486 goto cleanup;
487 }
488
489cleanup:
490 buf_pool_release(&buf);
491
492 return rc;
493}
#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
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
struct MhEmail * mh_entry_new(void)
Create a new Mh entry.
Definition mhemail.c:39
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
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
The envelope/body of an email.
Definition email.h:39
+ 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 mh parsing.

Definition at line 514 of file mh.c.

515{
516 FILE *fp = mutt_file_fopen(fname, "r");
517 if (!fp)
518 {
519 return NULL;
520 }
521
522 const long size = mutt_file_get_size_fp(fp);
523 if (size == 0)
524 {
525 mutt_file_fclose(&fp);
526 return NULL;
527 }
528
529 if (!e)
530 e = email_new();
531
532 e->env = mutt_rfc822_read_header(fp, e, false, false);
533
534 if (e->received == 0)
535 e->received = e->date_sent;
536
537 /* always update the length since we have fresh information available. */
538 e->body->length = size - e->body->offset;
539 e->index = -1;
540
541 mutt_file_fclose(&fp);
542 return e;
543}
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:1432
#define mutt_file_fclose(FP)
Definition file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
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:

◆ mh_delayed_parsing()

static void mh_delayed_parsing ( struct Mailbox * m,
struct MhEmailArray * mha,
struct Progress * progress )
static

This function does the second parsing pass.

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

Definition at line 551 of file mh.c.

553{
554 char fn[PATH_MAX] = { 0 };
555
556#ifdef USE_HCACHE
557 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
558 struct HeaderCache *hc = hcache_open(c_header_cache, mailbox_path(m), NULL, true);
559#endif
560
561 struct MhEmail *md = NULL;
562 struct MhEmail **mdp = NULL;
563 ARRAY_FOREACH(mdp, mha)
564 {
565 md = *mdp;
566 if (!md || !md->email || md->header_parsed)
567 continue;
568
569 progress_update(progress, ARRAY_FOREACH_IDX_mdp, -1);
570
571#ifdef USE_HCACHE
572 const char *key = md->email->path;
573 size_t keylen = strlen(key);
574 struct HCacheEntry hce = hcache_fetch_email(hc, key, keylen, 0);
575
576 if (hce.email)
577 {
578 hce.email->old = md->email->old;
579 hce.email->path = mutt_str_dup(md->email->path);
580 email_free(&md->email);
581 md->email = hce.email;
582 }
583 else
584#endif
585 {
586 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
587
588 if (mh_parse_message(fn, md->email))
589 {
590 md->header_parsed = true;
591#ifdef USE_HCACHE
592 key = md->email->path;
593 keylen = strlen(key);
594 hcache_store_email(hc, key, keylen, md->email, 0);
595#endif
596 }
597 else
598 {
599 email_free(&md->email);
600 }
601 }
602 }
603#ifdef USE_HCACHE
604 hcache_close(&hc);
605#endif
606
607 const enum EmailSortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
608 if (m && mha && (ARRAY_SIZE(mha) > 0) && (c_sort == EMAIL_SORT_UNSORTED))
609 {
610 mutt_debug(LL_DEBUG3, "mh: sorting %s into natural order\n", mailbox_path(m));
611 ARRAY_SORT(mha, mh_sort_path, NULL);
612 }
613}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:373
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition helpers.c:168
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition helpers.c:266
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
EmailSortType
Methods for sorting Emails.
Definition sort.h:53
@ EMAIL_SORT_UNSORTED
Sort by the order the messages appear in the mailbox.
Definition sort.h:64
static int mh_sort_path(const void *a, const void *b, void *sdata)
Compare two Mh Mailboxes by path - Implements sort_t -.
Definition mh.c:498
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer, bool create)
Multiplexor for StoreOps::open.
Definition hcache.c:479
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition hcache.c:550
struct HCacheEntry hcache_fetch_email(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition hcache.c:570
int hcache_store_email(struct HeaderCache *hc, const char *key, size_t keylen, struct Email *e, uint32_t uidvalidity)
Multiplexor for StoreOps::store.
Definition hcache.c:684
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
static struct Email * mh_parse_message(const char *fname, struct Email *e)
Actually parse an MH message.
Definition mh.c:514
bool old
Email is seen, but unread.
Definition email.h:49
Wrapper for Email retrieved from the header cache.
Definition lib.h:100
struct Email * email
Retrieved email.
Definition lib.h:103
Header Cache.
Definition lib.h:87
bool header_parsed
Has the Email header been parsed?
Definition mhemail.h:39
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:

◆ mh_move_to_mailbox()

static int mh_move_to_mailbox ( struct Mailbox * m,
const struct MhEmailArray * mha )
static

Copy the Mh list to the Mailbox.

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

Definition at line 622 of file mh.c.

623{
624 if (!m)
625 return 0;
626
627 int oldmsgcount = m->msg_count;
628
629 struct MhEmail *md = NULL;
630 struct MhEmail **mdp = NULL;
631 ARRAY_FOREACH(mdp, mha)
632 {
633 md = *mdp;
634 mutt_debug(LL_DEBUG2, "Considering %s\n", NONULL(md->canon_fname));
635 if (!md->email)
636 continue;
637
638 mutt_debug(LL_DEBUG2, "Adding header structure. Flags: %s%s%s%s%s\n",
639 md->email->flagged ? "f" : "", md->email->deleted ? "D" : "",
640 md->email->replied ? "r" : "", md->email->old ? "O" : "",
641 md->email->read ? "R" : "");
643
644 m->emails[m->msg_count] = md->email;
645 m->emails[m->msg_count]->index = m->msg_count;
646 mailbox_size_add(m, md->email);
647
648 md->email = NULL;
649 m->msg_count++;
650 }
651
652 int num = 0;
653 if (m->msg_count > oldmsgcount)
654 num = m->msg_count - oldmsgcount;
655
656 return num;
657}
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
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
bool deleted
Email is deleted.
Definition email.h:78
int msg_count
Total number of messages.
Definition mailbox.h:87
struct Email ** emails
Array of Emails.
Definition mailbox.h:95
char * canon_fname
Canonical filename for hashing.
Definition mhemail.h:38
+ 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 665 of file mh.c.

666{
667 if (!m)
668 return false;
669
670 mutt_path_tidy(&m->pathbuf, true);
671
672 struct MhSequences mhs = { 0 };
673 struct Progress *progress = NULL;
674
675 if (m->verbose)
676 {
677 progress = progress_new(MUTT_PROGRESS_READ, 0);
678 progress_set_message(progress, _("Scanning %s..."), mailbox_path(m));
679 }
680
681 struct MhMboxData *mdata = mh_mdata_get(m);
682 if (!mdata)
683 {
685 m->mdata = mdata;
687 }
688
690
691 struct MhEmailArray mha = ARRAY_HEAD_INITIALIZER;
692 int rc = mh_parse_dir(m, &mha, progress);
693 progress_free(&progress);
694 if (rc < 0)
695 return false;
696
697 if (m->verbose)
698 {
699 progress = progress_new(MUTT_PROGRESS_READ, ARRAY_SIZE(&mha));
700 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
701 }
702 mh_delayed_parsing(m, &mha, progress);
703 progress_free(&progress);
704
705 if (mh_seq_read(&mhs, mailbox_path(m)) < 0)
706 {
707 mharray_clear(&mha);
708 return false;
709 }
710 mh_update_emails(&mha, &mhs);
711 mh_seq_free(&mhs);
712
713 mh_move_to_mailbox(m, &mha);
714 mharray_clear(&mha);
715
716 if (!mdata->umask)
717 mdata->umask = mh_umask(m);
718
719 return true;
720}
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
void mh_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free() -.
Definition mdata.c:37
struct MhMboxData * mh_mdata_new(void)
Create a new MhMboxData object.
Definition mdata.c:49
mode_t mh_umask(struct Mailbox *m)
Create a umask from the mailbox directory.
Definition shared.c:49
static void mh_update_mtime(struct Mailbox *m)
Update our record of the mailbox modification time.
Definition mh.c:420
static int mh_parse_dir(struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
Read an Mh mailbox.
Definition mh.c:445
static void mh_delayed_parsing(struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
This function does the second parsing pass.
Definition mh.c:551
static void mh_update_emails(struct MhEmailArray *mha, struct MhSequences *mhs)
Update our record of flags.
Definition mh.c:210
static int mh_move_to_mailbox(struct Mailbox *m, const struct MhEmailArray *mha)
Copy the Mh list to the Mailbox.
Definition mh.c:622
void mharray_clear(struct MhEmailArray *mha)
Free a Mh array.
Definition mhemail.c:64
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 mh_seq_free(struct MhSequences *mhs)
Free some sequences.
Definition sequence.c:68
int mh_seq_read(struct MhSequences *mhs, const char *path)
Read a set of MH sequences.
Definition sequence.c:380
void(* mdata_free)(void **ptr)
Definition mailbox.h:142
struct Buffer pathbuf
Path of the Mailbox.
Definition mailbox.h:79
bool verbose
Display status messages?
Definition mailbox.h:116
mode_t 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 730 of file mh.c.

731{
732 if (!m || !e)
733 return -1;
734
735 if (e->deleted)
736 {
737 char path[PATH_MAX] = { 0 };
738 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
739 const bool c_mh_purge = cs_subset_bool(NeoMutt->sub, "mh_purge");
740 if (c_mh_purge)
741 {
742#ifdef USE_HCACHE
743 if (hc)
744 {
745 const char *key = e->path;
746 size_t keylen = strlen(key);
747 hcache_delete_email(hc, key, keylen);
748 }
749#endif
750 unlink(path);
751 }
752 else
753 {
754 /* MH just moves files out of the way when you delete them */
755 if (*e->path != ',')
756 {
757 char tmp[PATH_MAX] = { 0 };
758 snprintf(tmp, sizeof(tmp), "%s/,%s", mailbox_path(m), e->path);
759 unlink(tmp);
760 if (rename(path, tmp) != 0)
761 {
762 return -1;
763 }
764 }
765 }
766 }
767 else if (e->changed || e->attach_del)
768 {
769 if (mh_sync_message(m, e) == -1)
770 return -1;
771 }
772
773#ifdef USE_HCACHE
774 if (hc && e->changed)
775 {
776 const char *key = e->path;
777 size_t keylen = strlen(key);
778 hcache_store_email(hc, key, keylen, e, 0);
779 }
780#endif
781
782 return 0;
783}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
int hcache_delete_email(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition hcache.c:753
static int mh_sync_message(struct Mailbox *m, struct Email *e)
Sync an email to an MH folder.
Definition mh.c:401
bool changed
Email has been edited.
Definition email.h:77
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_update_flags()

static bool mh_update_flags ( struct Mailbox * m,
struct Email * e_old,
struct Email * e_new )
static

Update the mailbox flags.

Parameters
mMailbox
e_oldOld Email
e_newNew Email
Return values
trueThe flags changed
falseOtherwise

Definition at line 860 of file mh.c.

861{
862 if (!m)
863 return false;
864
865 /* save the global state here so we can reset it at the
866 * end of list block if required. */
867 bool context_changed = m->changed;
868
869 /* user didn't modify this message. alter the flags to match the
870 * current state on disk. This may not actually do
871 * anything. mutt_set_flag() will just ignore the call if the status
872 * bits are already properly set, but it is still faster not to pass
873 * through it */
874 if (e_old->flagged != e_new->flagged)
875 mutt_set_flag(m, e_old, MUTT_FLAG, e_new->flagged, true);
876 if (e_old->replied != e_new->replied)
877 mutt_set_flag(m, e_old, MUTT_REPLIED, e_new->replied, true);
878 if (e_old->read != e_new->read)
879 mutt_set_flag(m, e_old, MUTT_READ, e_new->read, true);
880 if (e_old->old != e_new->old)
881 mutt_set_flag(m, e_old, MUTT_OLD, e_new->old, true);
882
883 /* mutt_set_flag() will set this, but we don't need to
884 * sync the changes we made because we just updated the
885 * context to match the current on-disk state of the
886 * message. */
887 bool header_changed = e_old->changed;
888 e_old->changed = false;
889
890 /* if the mailbox was not modified before we made these
891 * changes, unset the changed flag since nothing needs to
892 * be synchronized. */
893 if (!context_changed)
894 m->changed = false;
895
896 return header_changed;
897}
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition flags.c:54
@ MUTT_READ
Messages that have been read.
Definition mutt.h:92
@ MUTT_OLD
Old messages.
Definition mutt.h:90
@ MUTT_FLAG
Flagged messages.
Definition mutt.h:98
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:91
bool changed
Mailbox has been modified.
Definition mailbox.h:109
+ 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 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 911 of file mh.c.

912{
913 char buf[PATH_MAX] = { 0 };
914 struct stat st = { 0 };
915 struct stat st_cur = { 0 };
916 bool modified = false, occult = false, flags_changed = false;
917 int num_new = 0;
918 struct MhSequences mhs = { 0 };
919 struct HashTable *fnames = NULL;
920 struct MhMboxData *mdata = mh_mdata_get(m);
921
922 const bool c_check_new = cs_subset_bool(NeoMutt->sub, "check_new");
923 if (!c_check_new)
924 return MX_STATUS_OK;
925
926 mutt_str_copy(buf, mailbox_path(m), sizeof(buf));
927 if (stat(buf, &st) == -1)
928 return MX_STATUS_ERROR;
929
930 /* create .mh_sequences when there isn't one. */
931 snprintf(buf, sizeof(buf), "%s/.mh_sequences", mailbox_path(m));
932 int rc = stat(buf, &st_cur);
933 if ((rc == -1) && (errno == ENOENT))
934 {
935 char *tmp = NULL;
936 FILE *fp = NULL;
937
938 if (mh_mkstemp(m, &fp, &tmp))
939 {
940 mutt_file_fclose(&fp);
941 if (mutt_file_safe_rename(tmp, buf) == -1)
942 unlink(tmp);
943 FREE(&tmp);
944 }
945 }
946
947 if ((rc == -1) && (stat(buf, &st_cur) == -1))
948 modified = true;
949
950 if ((mutt_file_stat_timespec_compare(&st, MUTT_STAT_MTIME, &mdata->mtime) > 0) ||
951 (mutt_file_stat_timespec_compare(&st_cur, MUTT_STAT_MTIME, &mdata->mtime_seq) > 0))
952 {
953 modified = true;
954 }
955
956 if (!modified)
957 return MX_STATUS_OK;
958
959 /* Update the modification times on the mailbox.
960 *
961 * The monitor code notices changes in the open mailbox too quickly.
962 * In practice, this sometimes leads to all the new messages not being
963 * noticed during the SAME group of mtime stat updates. To work around
964 * the problem, don't update the stat times for a monitor caused check. */
965#ifdef USE_INOTIFY
967 {
968 MonitorCurMboxChanged = false;
969 }
970 else
971#endif
972 {
975 }
976
977 struct MhEmailArray mha = ARRAY_HEAD_INITIALIZER;
978
979 mh_parse_dir(m, &mha, NULL);
980 mh_delayed_parsing(m, &mha, NULL);
981
982 if (mh_seq_read(&mhs, mailbox_path(m)) < 0)
983 return MX_STATUS_ERROR;
984 mh_update_emails(&mha, &mhs);
985 mh_seq_free(&mhs);
986
987 /* check for modifications and adjust flags */
989
990 struct MhEmail *md = NULL;
991 struct MhEmail **mdp = NULL;
992 ARRAY_FOREACH(mdp, &mha)
993 {
994 md = *mdp;
995 /* the hash key must survive past the header, which is freed below. */
997 mutt_hash_insert(fnames, md->canon_fname, md);
998 }
999
1000 for (int i = 0; i < m->msg_count; i++)
1001 {
1002 struct Email *e = m->emails[i];
1003 if (!e)
1004 break;
1005
1006 md = mutt_hash_find(fnames, e->path);
1007 if (md && md->email && email_cmp_strict(e, md->email))
1008 {
1009 /* found the right message */
1010 if (!e->changed)
1011 if (mh_update_flags(m, e, md->email))
1012 flags_changed = true;
1013
1014 email_free(&md->email);
1015 }
1016 else /* message has disappeared */
1017 {
1018 occult = true;
1019 }
1020 }
1021
1022 /* destroy the file name hash */
1023
1024 mutt_hash_free(&fnames);
1025
1026 /* If we didn't just get new mail, update the tables. */
1027 if (occult)
1029
1030 /* Incorporate new messages */
1031 num_new = mh_move_to_mailbox(m, &mha);
1032 mharray_clear(&mha);
1033
1034 if (num_new > 0)
1035 {
1037 m->changed = true;
1038 }
1039
1040 ARRAY_FREE(&mha);
1041 if (occult)
1042 return MX_STATUS_REOPENED;
1043 if (num_new > 0)
1044 return MX_STATUS_NEW_MAIL;
1045 if (flags_changed)
1046 return MX_STATUS_FLAGS;
1047 return MX_STATUS_OK;
1048}
#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
bool email_cmp_strict(const struct Email *e1, const struct Email *e2)
Strictly compare message emails.
Definition email.c:96
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
bool mh_mkstemp(struct Mailbox *m, FILE **fp, char **tgt)
Create a temporary file.
Definition shared.c:73
static bool mh_update_flags(struct Mailbox *m, struct Email *e_old, struct Email *e_new)
Update the mailbox flags.
Definition mh.c:860
bool MonitorCurMboxChanged
Set to true when the current mailbox has changed.
Definition monitor.c:55
@ 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
A Hash Table.
Definition hash.h:99
+ Here is the call graph for this function:
+ Here is the caller graph for this function: