NeoMutt  2025-12-11-872-g385a04
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mbox.c File Reference

Mbox local mailbox type. More...

#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <utime.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "mutt.h"
#include "lib.h"
#include "progress/lib.h"
#include "globals.h"
#include "muttlib.h"
#include "mx.h"
+ Include dependency graph for mbox.c:

Go to the source code of this file.

Data Structures

struct  MUpdate
 Store of new offsets, used by mutt_sync_mailbox() More...
 

Functions

static void mbox_adata_free (void **ptr)
 Free the private Account data - Implements Account::adata_free() -.
 
static struct MboxAccountDatambox_adata_new (void)
 Create a new MboxAccountData struct.
 
static int init_mailbox (struct Mailbox *m)
 Add Mbox data to the Mailbox.
 
static struct MboxAccountDatambox_adata_get (struct Mailbox *m)
 Get the private data associated with a Mailbox.
 
static int mbox_lock_mailbox (struct Mailbox *m, bool excl, bool retry)
 Lock a mailbox.
 
static void mbox_unlock_mailbox (struct Mailbox *m)
 Unlock a mailbox.
 
static enum MxOpenReturns mmdf_parse_mailbox (struct Mailbox *m)
 Read a mailbox in MMDF format.
 
static enum MxOpenReturns mbox_parse_mailbox (struct Mailbox *m)
 Read a mailbox from disk.
 
static int reopen_mailbox (struct Mailbox *m)
 Close and reopen a mailbox.
 
static bool mbox_has_new (struct Mailbox *m)
 Does the mailbox have new mail.
 
void mbox_reset_atime (struct Mailbox *m, struct stat *st)
 Reset the access time on the mailbox file.
 
static bool mbox_ac_owns_path (struct Account *a, const char *path)
 Check whether an Account owns a Mailbox path - Implements MxOps::ac_owns_path() -.
 
static bool mbox_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -.
 
static FILE * mbox_open_readwrite (struct Mailbox *m)
 Open an mbox read-write.
 
static FILE * mbox_open_readonly (struct Mailbox *m)
 Open an mbox read-only.
 
static enum MxOpenReturns mbox_mbox_open (struct Mailbox *m)
 Open a Mailbox - Implements MxOps::mbox_open() -.
 
static bool mbox_mbox_open_append (struct Mailbox *m, OpenMailboxFlags flags)
 Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
 
static enum MxStatus mbox_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -.
 
static enum MxStatus mbox_mbox_sync (struct Mailbox *m)
 Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
 
static enum MxStatus mbox_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -.
 
static bool mbox_msg_open (struct Mailbox *m, struct Message *msg, struct Email *e)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -.
 
static bool mbox_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 mbox_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -.
 
static int mbox_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -.
 
static int mbox_msg_padding_size (struct Mailbox *m)
 Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
 
enum MailboxType mbox_path_probe (const char *path, const struct stat *st)
 Is this an mbox Mailbox?
 
static int mbox_path_canon (struct Buffer *path)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
 
static int mbox_path_is_empty (struct Buffer *path)
 Is the mailbox empty - Implements MxOps::path_is_empty() -.
 
static int mmdf_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -.
 
static int mmdf_msg_padding_size (struct Mailbox *m)
 Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
 
static enum MxStatus mbox_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -.
 

Variables

const struct MxOps MxMboxOps
 Mbox Mailbox - Implements MxOps -.
 
const struct MxOps MxMmdfOps
 MMDF Mailbox - Implements MxOps -.
 

Detailed Description

Mbox local mailbox type.

Authors
  • Richard Russon
  • Austin Ray
  • Ian Zimmerman
  • Pietro Cerutti

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 mbox.c.

Function Documentation

◆ mbox_adata_new()

static struct MboxAccountData * mbox_adata_new ( void )
static

Create a new MboxAccountData struct.

Return values
ptrNew MboxAccountData

Definition at line 91 of file mbox.c.

92{
93 return MUTT_MEM_CALLOC(1, struct MboxAccountData);
94}
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
Mbox-specific Account data -.
Definition lib.h:50
+ Here is the caller graph for this function:

◆ init_mailbox()

static int init_mailbox ( struct Mailbox * m)
static

Add Mbox data to the Mailbox.

Parameters
mMailbox
Return values
0Success
-1Error Bad format

Definition at line 102 of file mbox.c.

103{
104 if (!m || !m->account)
105 return -1;
106 if ((m->type != MUTT_MBOX) && (m->type != MUTT_MMDF))
107 return -1;
108 if (m->account->adata)
109 return 0;
110
113 return 0;
114}
@ MUTT_MMDF
'mmdf' Mailbox type
Definition mailbox.h:45
@ MUTT_MBOX
'mbox' Mailbox type
Definition mailbox.h:44
static void mbox_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free() -.
Definition mbox.c:76
static struct MboxAccountData * mbox_adata_new(void)
Create a new MboxAccountData struct.
Definition mbox.c:91
void(* adata_free)(void **ptr)
Definition account.h:53
void * adata
Private data (for Mailbox backends)
Definition account.h:42
enum MailboxType type
Mailbox type.
Definition mailbox.h:104
struct Account * account
Account that owns this Mailbox.
Definition mailbox.h:129
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_adata_get()

static struct MboxAccountData * mbox_adata_get ( struct Mailbox * m)
static

Get the private data associated with a Mailbox.

Parameters
mMailbox
Return values
ptrPrivate data

Definition at line 121 of file mbox.c.

122{
123 if (init_mailbox(m) == -1)
124 return NULL;
125 return m->account->adata;
126}
static int init_mailbox(struct Mailbox *m)
Add Mbox data to the Mailbox.
Definition mbox.c:102
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_lock_mailbox()

static int mbox_lock_mailbox ( struct Mailbox * m,
bool excl,
bool retry )
static

Lock a mailbox.

Parameters
mMailbox to lock
exclExclusive lock?
retryShould retry if unable to lock?
Return values
0Success
-1Failure

Definition at line 136 of file mbox.c.

137{
139 if (!adata)
140 return -1;
141
142 int rc = mutt_file_lock(fileno(adata->fp), excl, retry);
143 if (rc == 0)
144 {
145 adata->locked = true;
146 }
147 else if (retry && !excl)
148 {
149 m->readonly = true;
150 return 0;
151 }
152
153 return rc;
154}
int mutt_file_lock(int fd, bool excl, bool timeout)
(Try to) Lock a file using fcntl()
Definition file.c:1088
static struct MboxAccountData * mbox_adata_get(struct Mailbox *m)
Get the private data associated with a Mailbox.
Definition mbox.c:121
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:118
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_unlock_mailbox()

static void mbox_unlock_mailbox ( struct Mailbox * m)
static

Unlock a mailbox.

Parameters
mMailbox to unlock

Definition at line 160 of file mbox.c.

161{
163 if (!adata)
164 return;
165
166 if (adata->locked)
167 {
168 fflush(adata->fp);
169
170 mutt_file_unlock(fileno(adata->fp));
171 adata->locked = false;
172 }
173}
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition file.c:1135
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mmdf_parse_mailbox()

static enum MxOpenReturns mmdf_parse_mailbox ( struct Mailbox * m)
static

Read a mailbox in MMDF format.

Parameters
mMailbox
Return values
enumMxOpenReturns

Definition at line 180 of file mbox.c.

181{
182 if (!m)
183 return MX_OPEN_ERROR;
184
186 if (!adata)
187 return MX_OPEN_ERROR;
188
189 char buf[8192] = { 0 };
190 char return_path[1024] = { 0 };
191 int count = 0;
192 int lines;
193 time_t t = 0;
194 LOFF_T loc, tmploc;
195 struct Email *e = NULL;
196 struct stat st = { 0 };
197 struct Progress *progress = NULL;
199
200 /* Record mailbox timestamps and size for change detection */
201 if (stat(mailbox_path(m), &st) == -1)
202 {
203 mutt_perror("%s", mailbox_path(m));
204 goto fail;
205 }
208 m->size = st.st_size;
209
210 if (m->verbose)
211 {
212 progress = progress_new(MUTT_PROGRESS_READ, 0);
213 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
214 }
215
216 /* Read the file line-by-line looking for MMDF separator lines ("\001\001\001\001\n") */
217 while (true)
218 {
219 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
220 break;
221
222 if (SigInt)
223 break;
224
225 if (mutt_str_equal(buf, MMDF_SEP))
226 {
227 loc = ftello(adata->fp);
228 if (loc < 0)
229 goto fail;
230
231 count++;
232 progress_update(progress, count, (int) (loc / (m->size / 100 + 1)));
233
234 /* Allocate a new Email for this message */
236 e = email_new();
237 m->emails[m->msg_count] = e;
238 e->offset = loc;
239 e->index = m->msg_count;
240
241 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
242 {
243 mutt_debug(LL_DEBUG1, "unexpected EOF\n");
244 email_free(&m->emails[m->msg_count]);
245 break;
246 }
247
248 /* Try to parse "From " line for return path and date */
249 return_path[0] = '\0';
250
251 if (!is_from(buf, return_path, sizeof(return_path), &t))
252 {
253 if (!mutt_file_seek(adata->fp, loc, SEEK_SET))
254 {
255 mutt_error(_("Mailbox is corrupt"));
256 goto fail;
257 }
258 }
259 else
260 {
261 e->received = t - mutt_date_local_tz(t);
262 }
263
264 /* Parse RFC 822 headers to populate the envelope */
265 e->env = mutt_rfc822_read_header(adata->fp, e, false, false);
266
267 loc = ftello(adata->fp);
268 if (loc < 0)
269 goto fail;
270
271 /* Try to validate the Content-Length by seeking to the expected end
272 * and verifying an MMDF separator is there */
273 if ((e->body->length > 0) && (e->lines > 0))
274 {
275 tmploc = loc + e->body->length;
276
277 if ((tmploc > 0) && (tmploc < m->size))
278 {
279 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
280 !fgets(buf, sizeof(buf) - 1, adata->fp) || !mutt_str_equal(MMDF_SEP, buf))
281 {
282 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
283 e->body->length = -1;
284 }
285 }
286 else
287 {
288 e->body->length = -1;
289 }
290 }
291 else
292 {
293 e->body->length = -1;
294 }
295
296 /* If Content-Length was missing or invalid, scan for the next separator */
297 if (e->body->length < 0)
298 {
299 lines = -1;
300 do
301 {
302 loc = ftello(adata->fp);
303 if (loc < 0)
304 goto fail;
305 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
306 break;
307 lines++;
308 } while (!mutt_str_equal(buf, MMDF_SEP));
309
310 e->lines = lines;
311 e->body->length = loc - e->body->offset;
312 }
313
314 /* Populate return path and from address if missing from headers */
315 if (TAILQ_EMPTY(&e->env->return_path) && return_path[0])
316 mutt_addrlist_parse(&e->env->return_path, return_path);
317
318 if (TAILQ_EMPTY(&e->env->from))
319 mutt_addrlist_copy(&e->env->from, &e->env->return_path, false);
320
321 m->msg_count++;
322 }
323 else
324 {
325 mutt_debug(LL_DEBUG1, "corrupt mailbox\n");
326 mutt_error(_("Mailbox is corrupt"));
327 goto fail;
328 }
329 }
330
331 if (SigInt)
332 {
333 SigInt = false;
334 rc = MX_OPEN_ABORT; /* action aborted */
335 goto fail;
336 }
337
338 rc = MX_OPEN_OK;
339fail:
340 progress_free(&progress);
341 return rc;
342}
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition address.c:774
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:480
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:216
struct Email * email_new(void)
Create a new Email.
Definition email.c:77
void email_free(struct Email **ptr)
Free an Email.
Definition email.c:46
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition parse.c:1175
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:1474
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition file.c:648
@ MUTT_STAT_ATIME
File/dir's atime - last accessed time.
Definition file.h:58
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition file.h:59
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition from.c:49
#define mutt_error(...)
Definition logging2.h:94
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define MMDF_SEP
Definition lib.h:63
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition date.c:220
#define _(a)
Definition message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1208
MxOpenReturns
Return values for mbox_open()
Definition mxapi.h:83
@ MX_OPEN_ERROR
Open failed with an error.
Definition mxapi.h:85
@ MX_OPEN_ABORT
Open was aborted.
Definition mxapi.h:86
@ MX_OPEN_OK
Open succeeded.
Definition mxapi.h:84
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition lib.h:84
struct Progress * progress_new(enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition progress.c:139
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition progress.c:110
void progress_set_message(struct Progress *progress, const char *fmt,...) __attribute__((__format__(__printf__
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition progress.c:80
#define TAILQ_EMPTY(head)
Definition queue.h:778
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition signal.c:68
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
The envelope/body of an email.
Definition email.h:39
struct Envelope * env
Envelope information.
Definition email.h:68
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
LOFF_T offset
Where in the stream does this message begin?
Definition email.h:71
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
struct AddressList return_path
Return path for the Email.
Definition envelope.h:58
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
int msg_count
Total number of messages.
Definition mailbox.h:90
struct Email ** emails
Array of Emails.
Definition mailbox.h:98
off_t size
Size of the Mailbox.
Definition mailbox.h:86
bool verbose
Display status messages?
Definition mailbox.h:119
FILE * fp
Mailbox file.
Definition lib.h:51
struct timespec atime
File's last-access time.
Definition lib.h:53
struct timespec mtime
Time Mailbox was last changed.
Definition lib.h:52
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_parse_mailbox()

static enum MxOpenReturns mbox_parse_mailbox ( struct Mailbox * m)
static

Read a mailbox from disk.

Parameters
mMailbox
Return values
enumMxOpenReturns

Note that this function is also called when new mail is appended to the currently open folder, and NOT just when the mailbox is initially read.

Note
It is assumed that the mailbox being read has been locked before this routine gets called. Strange things could happen if it's not!

Definition at line 355 of file mbox.c.

356{
357 if (!m)
358 return MX_OPEN_ERROR;
359
361 if (!adata)
362 return MX_OPEN_ERROR;
363
364 struct stat st = { 0 };
365 char buf[8192] = { 0 };
366 char return_path[1024] = { 0 };
367 struct Email *e_cur = NULL;
368 time_t t = 0;
369 int count = 0, lines = 0;
370 bool has_mbox_sep = false;
371 bool expect_from_line = true;
372 LOFF_T loc;
373 struct Progress *progress = NULL;
375
376 /* Save information about the folder at the time we opened it. */
377 if (stat(mailbox_path(m), &st) == -1)
378 {
379 mutt_perror("%s", mailbox_path(m));
380 goto fail;
381 }
382
383 m->size = st.st_size;
386
387 if (!m->readonly)
388 m->readonly = access(mailbox_path(m), W_OK) ? true : false;
389
390 if (m->verbose)
391 {
392 progress = progress_new(MUTT_PROGRESS_READ, 0);
393 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
394 }
395
396 loc = ftello(adata->fp);
397 if (loc < 0)
398 {
399 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
400 loc = 0;
401 }
402
403 while ((fgets(buf, sizeof(buf), adata->fp)) && !SigInt)
404 {
405 if (is_from(buf, return_path, sizeof(return_path), &t))
406 {
407 /* Save the Content-Length of the previous message */
408 if (count > 0)
409 {
410 struct Email *e = m->emails[m->msg_count - 1];
411 if (!has_mbox_sep)
412 {
413 mutt_debug(LL_DEBUG1, "mbox_parse_mailbox: missing separator at location: " OFF_T_FMT "\n",
414 loc);
415 }
416 if (e->body->length < 0)
417 {
418 e->body->length = loc - e->body->offset - (has_mbox_sep ? 1 : 0);
419 if (e->body->length < 0)
420 e->body->length = 0;
421 }
422 if (e->lines == 0)
423 e->lines = lines ? lines - 1 : 0;
424 }
425
426 count++;
427 expect_from_line = false;
428
429 progress_update(progress, count, (int) (ftello(adata->fp) / (m->size / 100 + 1)));
430
432
433 m->emails[m->msg_count] = email_new();
434 e_cur = m->emails[m->msg_count];
435 e_cur->received = t - mutt_date_local_tz(t);
436 e_cur->offset = loc;
437 e_cur->index = m->msg_count;
438
439 e_cur->env = mutt_rfc822_read_header(adata->fp, e_cur, false, false);
440
441 /* if we know how long this message is, either just skip over the body,
442 * or if we don't know how many lines there are, count them now (this will
443 * save time by not having to search for the next message marker). */
444 if (e_cur->body->length > 0)
445 {
446 LOFF_T tmploc;
447
448 loc = ftello(adata->fp);
449 if (loc < 0)
450 {
451 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
452 loc = 0;
453 }
454
455 /* The test below avoids a potential integer overflow if the
456 * content-length is huge (thus necessarily invalid). */
457 tmploc = (e_cur->body->length < m->size) ? (loc + e_cur->body->length + 1) : -1;
458
459 if ((tmploc > 0) && (tmploc < m->size))
460 {
461 /* check to see if the content-length looks valid. we expect to
462 * to see a valid message separator at this point in the stream */
463 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
464 !fgets(buf, sizeof(buf), adata->fp) || !mutt_str_startswith(buf, "From "))
465 {
466 mutt_debug(LL_DEBUG1, "bad content-length in message %d (cl=" OFF_T_FMT ")\n",
467 e_cur->index, e_cur->body->length);
468 mutt_debug(LL_DEBUG1, " LINE: %s", buf);
469 /* nope, return the previous position */
470 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
471 e_cur->body->length = -1;
472 }
473 }
474 else if (tmploc != m->size)
475 {
476 /* content-length would put us past the end of the file, so it
477 * must be wrong */
478 e_cur->body->length = -1;
479 }
480
481 if (e_cur->body->length != -1)
482 {
483 /* good content-length. check to see if we know how many lines
484 * are in this message. */
485 if (e_cur->lines == 0)
486 {
487 LOFF_T cl = e_cur->body->length;
488
489 /* count the number of lines in this message */
490 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
491 while (cl-- > 0)
492 {
493 if (fgetc(adata->fp) == '\n')
494 e_cur->lines++;
495 }
496 }
497
498 /* return to the offset of the next *mbox* separator */
499 (void) mutt_file_seek(adata->fp, tmploc - 1, SEEK_SET);
500 expect_from_line = true;
501 }
502 }
503
504 m->msg_count++;
505
506 if (TAILQ_EMPTY(&e_cur->env->return_path) && return_path[0])
507 {
508 mutt_addrlist_parse(&e_cur->env->return_path, return_path);
509 }
510
511 if (TAILQ_EMPTY(&e_cur->env->from))
512 mutt_addrlist_copy(&e_cur->env->from, &e_cur->env->return_path, false);
513
514 lines = 0;
515 has_mbox_sep = false;
516 }
517 else
518 {
519 lines++;
520 has_mbox_sep = mutt_str_equal(MBOX_SEP, buf);
521 if (expect_from_line && !has_mbox_sep)
522 {
523 mutt_debug(LL_DEBUG1, "mbox_parse_mailbox: missing From_ line at location: " OFF_T_FMT "\n",
524 loc);
525 mutt_error(_("Mailbox is corrupt"));
526 goto fail;
527 }
528 }
529
530 loc = ftello(adata->fp);
531 }
532
533 /* Only set the content-length of the previous message if we have read more
534 * than one message during _this_ invocation. If this routine is called
535 * when new mail is received, we need to make sure not to clobber what
536 * previously was the last message since the headers may be sorted. */
537 if (count > 0)
538 {
539 struct Email *e = m->emails[m->msg_count - 1];
540 if (!has_mbox_sep)
541 {
543 "mbox_parse_mailbox: missing separator at location: " OFF_T_FMT "\n", loc);
544 }
545 if (e->body->length < 0)
546 {
547 e->body->length = ftello(adata->fp) - e->body->offset - (has_mbox_sep ? 1 : 0);
548 if (e->body->length < 0)
549 e->body->length = 0;
550 }
551
552 if (e->lines == 0)
553 e->lines = lines ? lines - 1 : 0;
554 }
555
556 if (SigInt)
557 {
558 SigInt = false;
559 rc = MX_OPEN_ABORT;
560 goto fail; /* action aborted */
561 }
562
563 rc = MX_OPEN_OK;
564fail:
565 progress_free(&progress);
566 return rc;
567}
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
#define MBOX_SEP
Definition mx.h:69
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ reopen_mailbox()

static int reopen_mailbox ( struct Mailbox * m)
static

Close and reopen a mailbox.

Parameters
mMailbox
Return values
>0Success, e.g. MX_STATUS_REOPENED, MX_STATUS_NEW_MAIL
-1Error

Definition at line 575 of file mbox.c.

576{
577 if (!m)
578 return -1;
579
581 if (!adata)
582 return -1;
583
584 bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL;
585 struct Email **e_old = NULL;
586 int old_msg_count;
587 bool msg_mod = false;
588 int rc = -1;
589
590 /* silent operations */
591 m->verbose = false;
592
593 /* our heuristics require the old mailbox to be unsorted */
594 const enum EmailSortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
595 if (c_sort != EMAIL_SORT_UNSORTED)
596 {
599 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
600 }
601
602 e_old = NULL;
603 old_msg_count = 0;
604
605 /* simulate a close */
609 FREE(&m->v2r);
610 if (m->readonly)
611 {
612 for (int i = 0; i < m->msg_count; i++)
613 email_free(&(m->emails[i])); /* nothing to do! */
614 FREE(&m->emails);
615 }
616 else
617 {
618 /* save the old headers */
619 old_msg_count = m->msg_count;
620 e_old = m->emails;
621 m->emails = NULL;
622 }
623
624 m->email_max = 0; /* force allocation of new headers */
625 m->msg_count = 0;
626 m->vcount = 0;
627 m->msg_tagged = 0;
628 m->msg_deleted = 0;
629 m->msg_new = 0;
630 m->msg_unread = 0;
631 m->msg_flagged = 0;
632 m->changed = false;
633 m->id_hash = NULL;
634 m->subj_hash = NULL;
636
637 switch (m->type)
638 {
639 case MUTT_MBOX:
640 case MUTT_MMDF:
641 cmp_headers = email_cmp_strict;
642 mutt_file_fclose(&adata->fp);
643 adata->fp = mutt_file_fopen(mailbox_path(m), "r");
644 if (!adata->fp)
645 rc = -1;
646 else if (m->type == MUTT_MBOX)
647 rc = mbox_parse_mailbox(m);
648 else
649 rc = mmdf_parse_mailbox(m);
650 break;
651
652 default:
653 rc = -1;
654 break;
655 }
656
657 if (rc == -1)
658 {
659 /* free the old headers */
660 for (int i = 0; i < old_msg_count; i++)
661 email_free(&(e_old[i]));
662 FREE(&e_old);
663
664 m->verbose = true;
665 return -1;
666 }
667
668 mutt_file_touch_atime(fileno(adata->fp));
669
670 /* now try to recover the old flags */
671
672 if (!m->readonly)
673 {
674 for (int i = 0; i < m->msg_count; i++)
675 {
676 bool found = false;
677
678 /* some messages have been deleted, and new messages have been
679 * appended at the end; the heuristic is that old messages have then
680 * "advanced" towards the beginning of the folder, so we begin the
681 * search at index "i" */
682 int j;
683 for (j = i; j < old_msg_count; j++)
684 {
685 if (!e_old[j])
686 continue;
687 if (cmp_headers(m->emails[i], e_old[j]))
688 {
689 found = true;
690 break;
691 }
692 }
693 if (!found)
694 {
695 for (j = 0; (j < i) && (j < old_msg_count); j++)
696 {
697 if (!e_old[j])
698 continue;
699 if (cmp_headers(m->emails[i], e_old[j]))
700 {
701 found = true;
702 break;
703 }
704 }
705 }
706
707 if (found)
708 {
709 m->changed = true;
710 if (e_old[j]->changed)
711 {
712 /* Only update the flags if the old header was changed;
713 * otherwise, the header may have been modified externally,
714 * and we don't want to lose _those_ changes */
715 mutt_set_flag(m, m->emails[i], MUTT_FLAG, e_old[j]->flagged, true);
716 mutt_set_flag(m, m->emails[i], MUTT_REPLIED, e_old[j]->replied, true);
717 mutt_set_flag(m, m->emails[i], MUTT_OLD, e_old[j]->old, true);
718 mutt_set_flag(m, m->emails[i], MUTT_READ, e_old[j]->read, true);
719 }
720 mutt_set_flag(m, m->emails[i], MUTT_DELETE, e_old[j]->deleted, true);
721 mutt_set_flag(m, m->emails[i], MUTT_PURGE, e_old[j]->purge, true);
722 mutt_set_flag(m, m->emails[i], MUTT_TAG, e_old[j]->tagged, true);
723
724 /* we don't need this header any more */
725 email_free(&(e_old[j]));
726 }
727 }
728
729 /* free the remaining old emails */
730 for (int j = 0; j < old_msg_count; j++)
731 {
732 if (e_old[j])
733 {
734 email_free(&(e_old[j]));
735 msg_mod = true;
736 }
737 }
738 FREE(&e_old);
739 }
740
742 m->verbose = true;
743
744 return (m->changed || msg_mod) ? MX_STATUS_REOPENED : MX_STATUS_NEW_MAIL;
745}
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition helpers.c:266
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition mailbox.c:232
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition mailbox.h:183
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition mailbox.h:184
bool email_cmp_strict(const struct Email *e1, const struct Email *e2)
Strictly compare message emails.
Definition email.c:96
void mutt_make_label_hash(struct Mailbox *m)
Create a Hash Table to store the labels.
Definition header.c:405
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
void mutt_file_touch_atime(int fd)
Set the access time to current time.
Definition file.c:961
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition flags.c:54
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:459
static enum MxOpenReturns mbox_parse_mailbox(struct Mailbox *m)
Read a mailbox from disk.
Definition mbox.c:355
static enum MxOpenReturns mmdf_parse_mailbox(struct Mailbox *m)
Read a mailbox in MMDF format.
Definition mbox.c:180
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
@ MUTT_READ
Messages that have been read.
Definition mutt.h:92
@ MUTT_OLD
Old messages.
Definition mutt.h:90
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition mutt.h:96
@ MUTT_TAG
Tagged messages.
Definition mutt.h:99
@ MUTT_FLAG
Flagged messages.
Definition mutt.h:98
@ MUTT_DELETE
Messages to be deleted.
Definition mutt.h:94
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:91
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:75
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:73
bool changed
Email has been edited.
Definition email.h:77
int vcount
The number of virtual messages.
Definition mailbox.h:101
bool changed
Mailbox has been modified.
Definition mailbox.h:112
int * v2r
Mapping from virtual to real msgno.
Definition mailbox.h:100
int msg_new
Number of new messages.
Definition mailbox.h:94
int email_max
Size of emails array.
Definition mailbox.h:99
struct HashTable * subj_hash
Hash Table: "Subject" -> Email.
Definition mailbox.h:126
struct HashTable * id_hash
Hash Table: "Message-ID" -> Email.
Definition mailbox.h:125
int msg_deleted
Number of deleted messages.
Definition mailbox.h:95
struct HashTable * label_hash
Hash Table: "X-Label" -> Email.
Definition mailbox.h:127
int msg_flagged
Number of flagged messages.
Definition mailbox.h:92
int msg_tagged
How many messages are tagged?
Definition mailbox.h:96
int msg_unread
Number of unread messages.
Definition mailbox.h:91
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition subset.c:303
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_has_new()

static bool mbox_has_new ( struct Mailbox * m)
static

Does the mailbox have new mail.

Parameters
mMailbox
Return values
trueThe mailbox has at least 1 new messages (not old)
falseotherwise

Definition at line 753 of file mbox.c.

754{
755 for (int i = 0; i < m->msg_count; i++)
756 {
757 struct Email *e = m->emails[i];
758 if (!e)
759 break;
760 if (!e->deleted && !e->read && !e->old)
761 return true;
762 }
763 return false;
764}
bool read
Email is read.
Definition email.h:50
bool old
Email is seen, but unread.
Definition email.h:49
bool deleted
Email is deleted.
Definition email.h:78
+ Here is the caller graph for this function:

◆ mbox_reset_atime()

void mbox_reset_atime ( struct Mailbox * m,
struct stat * st )

Reset the access time on the mailbox file.

Parameters
mMailbox
stTimestamp

if mailbox has at least 1 new message, sets mtime > atime of mailbox so mailbox check reports new mail

Definition at line 774 of file mbox.c.

775{
776 struct stat st2 = { 0 };
777 if (!st)
778 {
779 if (stat(mailbox_path(m), &st2) < 0)
780 return;
781 st = &st2;
782 }
783
784 struct utimbuf utimebuf = { 0 };
785 utimebuf.actime = st->st_atime;
786 utimebuf.modtime = st->st_mtime;
787
788 /* When $mbox_check_recent is set, existing new mail is ignored, so do not
789 * reset the atime to mtime-1 to signal new mail. */
790 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
791 if (!c_mail_check_recent && (utimebuf.actime >= utimebuf.modtime) && mbox_has_new(m))
792 {
793 utimebuf.actime = utimebuf.modtime - 1;
794 }
795
796 utime(mailbox_path(m), &utimebuf);
797}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
static bool mbox_has_new(struct Mailbox *m)
Does the mailbox have new mail.
Definition mbox.c:753
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_open_readwrite()

static FILE * mbox_open_readwrite ( struct Mailbox * m)
static

Open an mbox read-write.

Parameters
mMailbox
Return values
ptrFILE handle

This function ensures that the FILE and readonly flag are changed atomically.

Definition at line 829 of file mbox.c.

830{
831 FILE *fp = mutt_file_fopen(mailbox_path(m), "r+");
832 if (fp)
833 m->readonly = false;
834 return fp;
835}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_open_readonly()

static FILE * mbox_open_readonly ( struct Mailbox * m)
static

Open an mbox read-only.

Parameters
mMailbox
Return values
ptrFILE handle

This function ensures that the FILE and readonly flag are changed atomically.

Definition at line 844 of file mbox.c.

845{
846 FILE *fp = mutt_file_fopen(mailbox_path(m), "r");
847 if (fp)
848 m->readonly = true;
849 return fp;
850}
+ Here is the call graph for this function:
+ Here is the caller graph for this function: