NeoMutt  2025-12-11-435-g4ac674
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:1509
@ 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:539
@ 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 mutt_debug(LL_DEBUG2, "Invalid MH message number '%s'\n", dep);
281 if (n > hi)
282 hi = n;
283 }
284 }
285 closedir(dir);
286
287 /* Now try to rename the file to the proper name.
288 * Note: We may have to try multiple times, until we find a free slot. */
289
290 while (true)
291 {
292 hi++;
293 snprintf(tmp, sizeof(tmp), "%u", hi);
294 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), tmp);
295 if (mutt_file_safe_rename(msg->path, path) == 0)
296 {
297 if (e)
298 mutt_str_replace(&e->path, tmp);
299 mutt_str_replace(&msg->committed_path, path);
300 FREE(&msg->path);
301 break;
302 }
303 else if (errno != EEXIST)
304 {
305 mutt_perror("%s", mailbox_path(m));
306 return -1;
307 }
308 }
309 if (updseq)
310 {
311 mh_seq_add_one(m, hi, !msg->flags.read, msg->flags.flagged, msg->flags.replied);
312 }
313 return 0;
314}
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:306
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: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 323 of file mh.c.

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

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

417{
418 char buf[PATH_MAX] = { 0 };
419 struct stat st = { 0 };
420 struct MhMboxData *mdata = mh_mdata_get(m);
421
422 snprintf(buf, sizeof(buf), "%s/.mh_sequences", mailbox_path(m));
423 if (stat(buf, &st) == 0)
425
426 mutt_str_copy(buf, mailbox_path(m), sizeof(buf));
427
428 if (stat(buf, &st) == 0)
430}
void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *st, enum MuttStatType type)
Read the stat() time into a time value.
Definition file.c:1469
struct 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 441 of file mh.c.

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

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

548{
549 char fn[PATH_MAX] = { 0 };
550
551#ifdef USE_HCACHE
552 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
553 struct HeaderCache *hc = hcache_open(c_header_cache, mailbox_path(m), NULL, true);
554#endif
555
556 struct MhEmail *md = NULL;
557 struct MhEmail **mdp = NULL;
558 ARRAY_FOREACH(mdp, mha)
559 {
560 md = *mdp;
561 if (!md || !md->email || md->header_parsed)
562 continue;
563
564 progress_update(progress, ARRAY_FOREACH_IDX_mdp, -1);
565
566#ifdef USE_HCACHE
567 const char *key = md->email->path;
568 size_t keylen = strlen(key);
569 struct HCacheEntry hce = hcache_fetch_email(hc, key, keylen, 0);
570
571 if (hce.email)
572 {
573 hce.email->old = md->email->old;
574 hce.email->path = mutt_str_dup(md->email->path);
575 email_free(&md->email);
576 md->email = hce.email;
577 }
578 else
579#endif
580 {
581 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
582
583 if (mh_parse_message(fn, md->email))
584 {
585 md->header_parsed = true;
586#ifdef USE_HCACHE
587 key = md->email->path;
588 keylen = strlen(key);
589 hcache_store_email(hc, key, keylen, md->email, 0);
590#endif
591 }
592 else
593 {
594 email_free(&md->email);
595 }
596 }
597 }
598#ifdef USE_HCACHE
599 hcache_close(&hc);
600#endif
601
602 const enum EmailSortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
603 if (m && mha && (ARRAY_SIZE(mha) > 0) && (c_sort == EMAIL_SORT_UNSORTED))
604 {
605 mutt_debug(LL_DEBUG3, "mh: sorting %s into natural order\n", mailbox_path(m));
606 ARRAY_SORT(mha, mh_sort_path, NULL);
607 }
608}
#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:493
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer, bool create)
Multiplexor for StoreOps::open.
Definition hcache.c:476
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition hcache.c:547
struct HCacheEntry hcache_fetch_email(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition hcache.c:567
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:675
@ 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:509
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 617 of file mh.c.

618{
619 if (!m)
620 return 0;
621
622 int oldmsgcount = m->msg_count;
623
624 struct MhEmail *md = NULL;
625 struct MhEmail **mdp = NULL;
626 ARRAY_FOREACH(mdp, mha)
627 {
628 md = *mdp;
629 mutt_debug(LL_DEBUG2, "Considering %s\n", NONULL(md->canon_fname));
630 if (!md->email)
631 continue;
632
633 mutt_debug(LL_DEBUG2, "Adding header structure. Flags: %s%s%s%s%s\n",
634 md->email->flagged ? "f" : "", md->email->deleted ? "D" : "",
635 md->email->replied ? "r" : "", md->email->old ? "O" : "",
636 md->email->read ? "R" : "");
638
639 m->emails[m->msg_count] = md->email;
640 m->emails[m->msg_count]->index = m->msg_count;
641 mailbox_size_add(m, md->email);
642
643 md->email = NULL;
644 m->msg_count++;
645 }
646
647 int num = 0;
648 if (m->msg_count > oldmsgcount)
649 num = m->msg_count - oldmsgcount;
650
651 return num;
652}
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 660 of file mh.c.

661{
662 if (!m)
663 return false;
664
665 mutt_path_tidy(&m->pathbuf, true);
666
667 struct MhSequences mhs = { 0 };
668 struct Progress *progress = NULL;
669
670 if (m->verbose)
671 {
672 progress = progress_new(MUTT_PROGRESS_READ, 0);
673 progress_set_message(progress, _("Scanning %s..."), mailbox_path(m));
674 }
675
676 struct MhMboxData *mdata = mh_mdata_get(m);
677 if (!mdata)
678 {
680 m->mdata = mdata;
682 }
683
685
686 struct MhEmailArray mha = ARRAY_HEAD_INITIALIZER;
687 int rc = mh_parse_dir(m, &mha, progress);
688 progress_free(&progress);
689 if (rc < 0)
690 return false;
691
692 if (m->verbose)
693 {
694 progress = progress_new(MUTT_PROGRESS_READ, ARRAY_SIZE(&mha));
695 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
696 }
697 mh_delayed_parsing(m, &mha, progress);
698 progress_free(&progress);
699
700 if (mh_seq_read(&mhs, mailbox_path(m)) < 0)
701 {
702 mharray_clear(&mha);
703 return false;
704 }
705 mh_update_emails(&mha, &mhs);
706 mh_seq_free(&mhs);
707
708 mh_move_to_mailbox(m, &mha);
709 mharray_clear(&mha);
710
711 if (!mdata->umask)
712 mdata->umask = mh_umask(m);
713
714 return true;
715}
#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:416
static int mh_parse_dir(struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
Read an Mh mailbox.
Definition mh.c:441
static void mh_delayed_parsing(struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
This function does the second parsing pass.
Definition mh.c:546
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:617
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:378
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 725 of file mh.c.

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

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

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