NeoMutt  2025-01-09-117-gace867
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mh.c File Reference

MH local mailbox type. More...

#include "config.h"
#include <ctype.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 79 of file mh.c.

80{
81 char path[PATH_MAX] = { 0 };
82 struct stat st = { 0 };
83
84 if ((snprintf(path, sizeof(path), "%s/%d", mailbox_path(m), msgno) < sizeof(path)) &&
85 (stat(path, &st) == 0))
86 {
88 }
89 return -1;
90}
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 101 of file mh.c.

102{
103 for (; *s; s++)
104 {
105 if (!isdigit((unsigned char) *s))
106 return false;
107 }
108 return true;
109}
+ 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 118 of file mh.c.

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

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

246{
247 struct dirent *de = NULL;
248 char *cp = NULL, *dep = NULL;
249 unsigned int n, 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 (!isdigit((unsigned char) *cp))
276 break;
277 cp++;
278 }
279 if (*cp == '\0')
280 {
281 if (!mutt_str_atoui(dep, &n))
282 mutt_debug(LL_DEBUG2, "Invalid MH message number '%s'\n", dep);
283 if (n > hi)
284 hi = n;
285 }
286 }
287 closedir(dir);
288
289 /* Now try to rename the file to the proper name.
290 * Note: We may have to try multiple times, until we find a free slot. */
291
292 while (true)
293 {
294 hi++;
295 snprintf(tmp, sizeof(tmp), "%u", hi);
296 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), tmp);
297 if (mutt_file_safe_rename(msg->path, path) == 0)
298 {
299 if (e)
300 mutt_str_replace(&e->path, tmp);
301 mutt_str_replace(&msg->committed_path, path);
302 FREE(&msg->path);
303 break;
304 }
305 else if (errno != EEXIST)
306 {
307 mutt_perror("%s", mailbox_path(m));
308 return -1;
309 }
310 }
311 if (updseq)
312 {
313 mh_seq_add_one(m, hi, !msg->flags.read, msg->flags.flagged, msg->flags.replied);
314 }
315 return 0;
316}
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:55
#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 325 of file mh.c.

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

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

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

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

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

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

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

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

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

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

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