NeoMutt  2025-09-05-7-geaa2bd
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? - Implements MxOps::path_probe() -.
 

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:281
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
bool replied
Message has been replied to.
Definition: message.h:43
char * committed_path
the final path generated by mx_msg_commit()
Definition: message.h:37
bool flagged
Message is flagged.
Definition: message.h:42
bool read
Message has been read.
Definition: message.h:41
struct Message::@0 flags
Flags for the Message.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mh_rewrite_message()

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

Sync a message in an MH folder.

Parameters
mMailbox
eEmail
Return values
0Success
-1Error

Definition at line 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:910
#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:580
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:254
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:471
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition: hcache.c:542
struct HCacheEntry hcache_fetch_email(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:562
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:670
@ 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:36
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:739
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: