NeoMutt  2025-09-05-43-g177ed6
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 "copy.h"
#include "mdata.h"
#include "mhemail.h"
#include "mx.h"
#include "protos.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

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

79{
80 char path[PATH_MAX] = { 0 };
81 struct stat st = { 0 };
82
83 if ((snprintf(path, sizeof(path), "%s/%d", mailbox_path(m), msgno) < sizeof(path)) &&
84 (stat(path, &st) == 0))
85 {
87 }
88 return -1;
89}
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:223
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:1512
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition file.h:54
#define PATH_MAX
Definition mutt.h:42
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 100 of file mh.c.

101{
102 for (; *s; s++)
103 {
104 if (!mutt_isdigit(*s))
105 return false;
106 }
107 return true;
108}
bool mutt_isdigit(int arg)
Wrapper for isdigit(3)
Definition ctype.c:65
+ 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 117 of file mh.c.

118{
119 struct dirent *de = NULL;
120 int rc = 1; /* assume empty until we find a message */
121
123 if (!dir)
124 return -1;
125 while ((de = readdir(dir)))
126 {
127 if (mh_valid_message(de->d_name))
128 {
129 rc = 0;
130 break;
131 }
132 }
133 closedir(dir);
134
135 return rc;
136}
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:542
@ 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:100
+ 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 211 of file mh.c.

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

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

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

399{
400 if (!m || !e)
401 return -1;
402
403 if (e->attach_del || e->env->changed)
404 {
405 if (mh_rewrite_message(m, e) != 0)
406 return -1;
407 e->env->changed = false;
408 }
409
410 return 0;
411}
static int mh_rewrite_message(struct Mailbox *m, struct Email *e)
Sync a message in an MH folder.
Definition mh.c:324
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 417 of file mh.c.

418{
419 char buf[PATH_MAX] = { 0 };
420 struct stat st = { 0 };
421 struct MhMboxData *mdata = mh_mdata_get(m);
422
423 snprintf(buf, sizeof(buf), "%s/.mh_sequences", mailbox_path(m));
424 if (stat(buf, &st) == 0)
426
427 mutt_str_copy(buf, mailbox_path(m), sizeof(buf));
428
429 if (stat(buf, &st) == 0)
431}
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:1472
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:132
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 442 of file mh.c.

443{
444 struct dirent *de = NULL;
445 int rc = 0;
446 struct MhEmail *entry = NULL;
447 struct Email *e = NULL;
448
449 struct Buffer *buf = buf_pool_get();
450 buf_strcpy(buf, mailbox_path(m));
451
453 if (!dir)
454 {
455 rc = -1;
456 goto cleanup;
457 }
458
459 while (((de = readdir(dir))) && !SigInt)
460 {
461 if (!mh_valid_message(de->d_name))
462 continue;
463
464 mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
465
466 e = email_new();
467
468 progress_update(progress, ARRAY_SIZE(mha) + 1, -1);
469
470 e->path = mutt_str_dup(de->d_name);
471
472 entry = mh_entry_new();
473 entry->email = e;
474 ARRAY_ADD(mha, entry);
475 }
476
477 closedir(dir);
478
479 if (SigInt)
480 {
481 SigInt = false;
482 return -2; /* action aborted */
483 }
484
485cleanup:
486 buf_pool_release(&buf);
487
488 return rc;
489}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:156
#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:255
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:96
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:69
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 510 of file mh.c.

511{
512 FILE *fp = mutt_file_fopen(fname, "r");
513 if (!fp)
514 {
515 return NULL;
516 }
517
518 const long size = mutt_file_get_size_fp(fp);
519 if (size == 0)
520 {
521 mutt_file_fclose(&fp);
522 return NULL;
523 }
524
525 if (!e)
526 e = email_new();
527
528 e->env = mutt_rfc822_read_header(fp, e, false, false);
529
530 if (e->received != 0)
531 e->received = e->date_sent;
532
533 /* always update the length since we have fresh information available. */
534 e->body->length = size - e->body->offset;
535 e->index = -1;
536
537 mutt_file_fclose(&fp);
538 return e;
539}
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1204
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1430
#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 547 of file mh.c.

549{
550 char fn[PATH_MAX] = { 0 };
551
552#ifdef USE_HCACHE
553 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
554 struct HeaderCache *hc = hcache_open(c_header_cache, mailbox_path(m), NULL, true);
555#endif
556
557 struct MhEmail *md = NULL;
558 struct MhEmail **mdp = NULL;
559 ARRAY_FOREACH(mdp, mha)
560 {
561 md = *mdp;
562 if (!md || !md->email || md->header_parsed)
563 continue;
564
565 progress_update(progress, ARRAY_FOREACH_IDX_mdp, -1);
566
567#ifdef USE_HCACHE
568 const char *key = md->email->path;
569 size_t keylen = strlen(key);
570 struct HCacheEntry hce = hcache_fetch_email(hc, key, keylen, 0);
571
572 if (hce.email)
573 {
574 hce.email->old = md->email->old;
575 hce.email->path = mutt_str_dup(md->email->path);
576 email_free(&md->email);
577 md->email = hce.email;
578 }
579 else
580#endif
581 {
582 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
583
584 if (mh_parse_message(fn, md->email))
585 {
586 md->header_parsed = true;
587#ifdef USE_HCACHE
588 key = md->email->path;
589 keylen = strlen(key);
590 hcache_store_email(hc, key, keylen, md->email, 0);
591#endif
592 }
593 else
594 {
595 email_free(&md->email);
596 }
597 }
598 }
599#ifdef USE_HCACHE
600 hcache_close(&hc);
601#endif
602
603 const enum EmailSortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
604 if (m && mha && (ARRAY_SIZE(mha) > 0) && (c_sort == EMAIL_SORT_UNSORTED))
605 {
606 mutt_debug(LL_DEBUG3, "mh: sorting %s into natural order\n", mailbox_path(m));
607 ARRAY_SORT(mha, mh_sort_path, NULL);
608 }
609}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:335
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:494
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer, bool create)
Multiplexor for StoreOps::open.
Definition hcache.c:473
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition hcache.c:544
struct HCacheEntry hcache_fetch_email(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition hcache.c:564
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:672
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:46
static struct Email * mh_parse_message(const char *fname, struct Email *e)
Actually parse an MH message.
Definition mh.c:510
bool old
Email is seen, but unread.
Definition email.h:49
Wrapper for Email retrieved from the header cache.
Definition lib.h:99
struct Email * email
Retrieved email.
Definition lib.h:102
Header Cache.
Definition lib.h:86
bool header_parsed
Has the Email header been parsed?
Definition mhemail.h:39
Container for Accounts, Notifications.
Definition neomutt.h:43
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:47
+ 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 618 of file mh.c.

619{
620 if (!m)
621 return 0;
622
623 int oldmsgcount = m->msg_count;
624
625 struct MhEmail *md = NULL;
626 struct MhEmail **mdp = NULL;
627 ARRAY_FOREACH(mdp, mha)
628 {
629 md = *mdp;
630 mutt_debug(LL_DEBUG2, "Considering %s\n", NONULL(md->canon_fname));
631 if (!md->email)
632 continue;
633
634 mutt_debug(LL_DEBUG2, "Adding header structure. Flags: %s%s%s%s%s\n",
635 md->email->flagged ? "f" : "", md->email->deleted ? "D" : "",
636 md->email->replied ? "r" : "", md->email->old ? "O" : "",
637 md->email->read ? "R" : "");
639
640 m->emails[m->msg_count] = md->email;
641 m->emails[m->msg_count]->index = m->msg_count;
642 mailbox_size_add(m, md->email);
643
644 md->email = NULL;
645 m->msg_count++;
646 }
647
648 int num = 0;
649 if (m->msg_count > oldmsgcount)
650 num = m->msg_count - oldmsgcount;
651
652 return num;
653}
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:249
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1211
#define NONULL(x)
Definition string2.h:43
bool deleted
Email is deleted.
Definition email.h:78
int msg_count
Total number of messages.
Definition mailbox.h:88
struct Email ** emails
Array of Emails.
Definition mailbox.h:96
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 661 of file mh.c.

662{
663 if (!m)
664 return false;
665
666 mutt_path_tidy(&m->pathbuf, true);
667
668 struct MhSequences mhs = { 0 };
669 struct Progress *progress = NULL;
670
671 if (m->verbose)
672 {
673 progress = progress_new(MUTT_PROGRESS_READ, 0);
674 progress_set_message(progress, _("Scanning %s..."), mailbox_path(m));
675 }
676
677 struct MhMboxData *mdata = mh_mdata_get(m);
678 if (!mdata)
679 {
681 m->mdata = mdata;
683 }
684
686
687 struct MhEmailArray mha = ARRAY_HEAD_INITIALIZER;
688 int rc = mh_parse_dir(m, &mha, progress);
689 progress_free(&progress);
690 if (rc < 0)
691 return false;
692
693 if (m->verbose)
694 {
695 progress = progress_new(MUTT_PROGRESS_READ, ARRAY_SIZE(&mha));
696 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
697 }
698 mh_delayed_parsing(m, &mha, progress);
699 progress_free(&progress);
700
701 if (mh_seq_read(&mhs, mailbox_path(m)) < 0)
702 {
703 mharray_clear(&mha);
704 return false;
705 }
706 mh_update_emails(&mha, &mhs);
707 mh_seq_free(&mhs);
708
709 mh_move_to_mailbox(m, &mha);
710 mharray_clear(&mha);
711
712 if (!mdata->umask)
713 mdata->umask = mh_umask(m);
714
715 return true;
716}
#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:417
static int mh_parse_dir(struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
Read an Mh mailbox.
Definition mh.c:442
static void mh_delayed_parsing(struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
This function does the second parsing pass.
Definition mh.c:547
static void mh_update_emails(struct MhEmailArray *mha, struct MhSequences *mhs)
Update our record of flags.
Definition mh.c:211
static int mh_move_to_mailbox(struct Mailbox *m, const struct MhEmailArray *mha)
Copy the Mh list to the Mailbox.
Definition mh.c:618
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:83
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:378
void(* mdata_free)(void **ptr)
Definition mailbox.h:143
struct Buffer pathbuf
Path of the Mailbox.
Definition mailbox.h:80
bool verbose
Display status messages?
Definition mailbox.h:117
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 726 of file mh.c.

727{
728 if (!m || !e)
729 return -1;
730
731 if (e->deleted)
732 {
733 char path[PATH_MAX] = { 0 };
734 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
735 const bool c_mh_purge = cs_subset_bool(NeoMutt->sub, "mh_purge");
736 if (c_mh_purge)
737 {
738#ifdef USE_HCACHE
739 if (hc)
740 {
741 const char *key = e->path;
742 size_t keylen = strlen(key);
743 hcache_delete_email(hc, key, keylen);
744 }
745#endif
746 unlink(path);
747 }
748 else
749 {
750 /* MH just moves files out of the way when you delete them */
751 if (*e->path != ',')
752 {
753 char tmp[PATH_MAX] = { 0 };
754 snprintf(tmp, sizeof(tmp), "%s/,%s", mailbox_path(m), e->path);
755 unlink(tmp);
756 if (rename(path, tmp) != 0)
757 {
758 return -1;
759 }
760 }
761 }
762 }
763 else if (e->changed || e->attach_del)
764 {
765 if (mh_sync_message(m, e) == -1)
766 return -1;
767 }
768
769#ifdef USE_HCACHE
770 if (hc && e->changed)
771 {
772 const char *key = e->path;
773 size_t keylen = strlen(key);
774 hcache_store_email(hc, key, keylen, e, 0);
775 }
776#endif
777
778 return 0;
779}
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:741
static int mh_sync_message(struct Mailbox *m, struct Email *e)
Sync an email to an MH folder.
Definition mh.c:398
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 856 of file mh.c.

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

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