NeoMutt  2025-12-11-980-ge38c27
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:216
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:1515
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition file.h:59
#define PATH_MAX
Definition mutt.h:49
struct timespec last_visited
Time of last exit from this mailbox.
Definition mailbox.h:106
+ 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:68
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
@ MH_SEQ_UNSEEN
Email hasn't been read.
Definition sequence.h:37
@ MH_SEQ_FLAGGED
Email is flagged.
Definition sequence.h:39
@ MH_SEQ_REPLIED
Email has been replied to.
Definition sequence.h:38
uint8_t MhSeqFlags
Definition sequence.h:41
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;
247 char *dep = NULL;
248 unsigned int n = 0;
249 unsigned int hi = 0;
250 char path[PATH_MAX] = { 0 };
251 char tmp[16] = { 0 };
252
253 if (mutt_file_fsync_close(&msg->fp))
254 {
255 mutt_perror(_("Could not flush message to disk"));
256 return -1;
257 }
258
260 if (!dir)
261 {
262 mutt_perror("%s", mailbox_path(m));
263 return -1;
264 }
265
266 /* figure out what the next message number is */
267 while ((de = readdir(dir)))
268 {
269 dep = de->d_name;
270 if (*dep == ',')
271 dep++;
272 cp = dep;
273 while (*cp)
274 {
275 if (!mutt_isdigit(*cp))
276 break;
277 cp++;
278 }
279 if (*cp == '\0')
280 {
281 if (!mutt_str_atoui(dep, &n))
282 {
283 mutt_debug(LL_DEBUG2, "Invalid MH message number '%s'\n", dep);
284 }
285 else if (n > hi)
286 {
287 hi = n;
288 }
289 }
290 }
291 closedir(dir);
292
293 /* Now try to rename the file to the proper name.
294 * Note: We may have to try multiple times, until we find a free slot. */
295
296 while (true)
297 {
298 hi++;
299 snprintf(tmp, sizeof(tmp), "%u", hi);
300 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), tmp);
301 if (mutt_file_safe_rename(msg->path, path) == 0)
302 {
303 if (e)
304 mutt_str_replace(&e->path, tmp);
305 mutt_str_replace(&msg->committed_path, path);
306 FREE(&msg->path);
307 break;
308 }
309 else if (errno != EEXIST)
310 {
311 mutt_perror("%s", mailbox_path(m));
312 return -1;
313 }
314 }
315 if (updseq)
316 {
317 mh_seq_add_one(m, hi, !msg->flags.read, msg->flags.flagged, msg->flags.replied);
318 }
319 return 0;
320}
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 329 of file mh.c.

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

404{
405 if (!m || !e)
406 return -1;
407
408 if (e->attach_del || e->env->changed)
409 {
410 if (mh_rewrite_message(m, e) != 0)
411 return -1;
412 e->env->changed = false;
413 }
414
415 return 0;
416}
static int mh_rewrite_message(struct Mailbox *m, struct Email *e)
Sync a message in an MH folder.
Definition mh.c:329
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 422 of file mh.c.

423{
424 char buf[PATH_MAX] = { 0 };
425 struct stat st = { 0 };
426 struct MhMboxData *mdata = mh_mdata_get(m);
427
428 snprintf(buf, sizeof(buf), "%s/.mh_sequences", mailbox_path(m));
429 if (stat(buf, &st) == 0)
431
432 mutt_str_copy(buf, mailbox_path(m), sizeof(buf));
433
434 if (stat(buf, &st) == 0)
436}
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:1475
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:134
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 447 of file mh.c.

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

517{
518 FILE *fp = mutt_file_fopen(fname, "r");
519 if (!fp)
520 {
521 return NULL;
522 }
523
524 const long size = mutt_file_get_size_fp(fp);
525 if (size == 0)
526 {
527 mutt_file_fclose(&fp);
528 return NULL;
529 }
530
531 if (!e)
532 e = email_new();
533
534 e->env = mutt_rfc822_read_header(fp, e, false, false);
535
536 if (e->received == 0)
537 e->received = e->date_sent;
538
539 /* always update the length since we have fresh information available. */
540 e->body->length = size - e->body->offset;
541 e->index = -1;
542
543 mutt_file_fclose(&fp);
544 return e;
545}
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1325
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1433
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
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 553 of file mh.c.

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

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

668{
669 if (!m)
670 return false;
671
672 mutt_path_tidy(&m->pathbuf, true);
673
674 struct MhSequences mhs = { 0 };
675 struct Progress *progress = NULL;
676
677 if (m->verbose)
678 {
679 progress = progress_new(MUTT_PROGRESS_READ, 0);
680 progress_set_message(progress, _("Scanning %s..."), mailbox_path(m));
681 }
682
683 struct MhMboxData *mdata = mh_mdata_get(m);
684 if (!mdata)
685 {
687 m->mdata = mdata;
689 }
690
692
693 struct MhEmailArray mha = ARRAY_HEAD_INITIALIZER;
694 int rc = mh_parse_dir(m, &mha, progress);
695 progress_free(&progress);
696 if (rc < 0)
697 return false;
698
699 if (m->verbose)
700 {
701 progress = progress_new(MUTT_PROGRESS_READ, ARRAY_SIZE(&mha));
702 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
703 }
704 mh_delayed_parsing(m, &mha, progress);
705 progress_free(&progress);
706
707 if (mh_seq_read(&mhs, mailbox_path(m)) < 0)
708 {
709 mharray_clear(&mha);
710 return false;
711 }
712 mh_update_emails(&mha, &mhs);
713 mh_seq_free(&mhs);
714
715 mh_move_to_mailbox(m, &mha);
716 mharray_clear(&mha);
717
718 if (!mdata->umask)
719 mdata->umask = mh_umask(m);
720
721 return true;
722}
#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:422
static int mh_parse_dir(struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
Read an Mh mailbox.
Definition mh.c:447
static void mh_delayed_parsing(struct Mailbox *m, struct MhEmailArray *mha, struct Progress *progress)
This function does the second parsing pass.
Definition mh.c:553
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:624
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:145
struct Buffer pathbuf
Path of the Mailbox.
Definition mailbox.h:82
bool verbose
Display status messages?
Definition mailbox.h:119
mode_t umask
umask to use when creating files
Definition mdata.h:38
Set of MH sequence numbers.
Definition sequence.h:47
+ 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 732 of file mh.c.

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

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

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