NeoMutt  2024-11-14-34-g5aaf0d
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 "globals.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 80 of file mh.c.

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

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

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

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

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

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

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

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

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

513{
514 FILE *fp = mutt_file_fopen(fname, "r");
515 if (!fp)
516 {
517 return NULL;
518 }
519
520 const long size = mutt_file_get_size_fp(fp);
521 if (size == 0)
522 {
523 mutt_file_fclose(&fp);
524 return NULL;
525 }
526
527 if (!e)
528 e = email_new();
529
530 e->env = mutt_rfc822_read_header(fp, e, false, false);
531
532 if (e->received != 0)
533 e->received = e->date_sent;
534
535 /* always update the length since we have fresh information available. */
536 e->body->length = size - e->body->offset;
537 e->index = -1;
538
539 mutt_file_fclose(&fp);
540 return e;
541}
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:1537
#define mutt_file_fclose(FP)
Definition: file.h:138
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:137
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 549 of file mh.c.

551{
552 char fn[PATH_MAX] = { 0 };
553
554#ifdef USE_HCACHE
555 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
556 struct HeaderCache *hc = hcache_open(c_header_cache, mailbox_path(m), NULL, true);
557#endif
558
559 struct MhEmail *md = NULL;
560 struct MhEmail **mdp = NULL;
561 ARRAY_FOREACH(mdp, mha)
562 {
563 md = *mdp;
564 if (!md || !md->email || md->header_parsed)
565 continue;
566
567 progress_update(progress, ARRAY_FOREACH_IDX, -1);
568
569#ifdef USE_HCACHE
570 const char *key = md->email->path;
571 size_t keylen = strlen(key);
572 struct HCacheEntry hce = hcache_fetch_email(hc, key, keylen, 0);
573
574 if (hce.email)
575 {
576 hce.email->old = md->email->old;
577 hce.email->path = mutt_str_dup(md->email->path);
578 email_free(&md->email);
579 md->email = hce.email;
580 }
581 else
582#endif
583 {
584 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
585
586 if (mh_parse_message(fn, md->email))
587 {
588 md->header_parsed = true;
589#ifdef USE_HCACHE
590 key = md->email->path;
591 keylen = strlen(key);
592 hcache_store_email(hc, key, keylen, md->email, 0);
593#endif
594 }
595 else
596 {
597 email_free(&md->email);
598 }
599 }
600 }
601#ifdef USE_HCACHE
602 hcache_close(&hc);
603#endif
604
605 const enum SortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
606 if (m && mha && (ARRAY_SIZE(mha) > 0) && (c_sort == SORT_ORDER))
607 {
608 mutt_debug(LL_DEBUG3, "mh: sorting %s into natural order\n", mailbox_path(m));
609 ARRAY_SORT(mha, mh_sort_path, NULL);
610 }
611}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:279
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
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:496
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:45
static struct Email * mh_parse_message(const char *fname, struct Email *e)
Actually parse an MH message.
Definition: mh.c:512
SortType
Methods for sorting.
Definition: sort2.h:34
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:40
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:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
+ 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 620 of file mh.c.

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

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

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

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

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