NeoMutt  2022-04-29-215-gc12b98
Teaching an old dog new tricks
DOXYGEN
mbox.c File Reference

Mbox local mailbox type. More...

#include "config.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 <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 "copy.h"
#include "mutt_globals.h"
#include "mutt_header.h"
#include "mutt_thread.h"
#include "muttlib.h"
#include "mx.h"
#include "protos.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() More...
 
static struct MboxAccountDatambox_adata_new (void)
 Create a new MboxAccountData struct. More...
 
static int init_mailbox (struct Mailbox *m)
 Add Mbox data to the Mailbox. More...
 
static struct MboxAccountDatambox_adata_get (struct Mailbox *m)
 Get the private data associated with a Mailbox. More...
 
static int mbox_lock_mailbox (struct Mailbox *m, bool excl, bool retry)
 Lock a mailbox. More...
 
static void mbox_unlock_mailbox (struct Mailbox *m)
 Unlock a mailbox. More...
 
static enum MxOpenReturns mmdf_parse_mailbox (struct Mailbox *m)
 Read a mailbox in MMDF format. More...
 
static enum MxOpenReturns mbox_parse_mailbox (struct Mailbox *m)
 Read a mailbox from disk. More...
 
static int reopen_mailbox (struct Mailbox *m)
 Close and reopen a mailbox. More...
 
static bool mbox_has_new (struct Mailbox *m)
 Does the mailbox have new mail. More...
 
static int fseek_last_message (FILE *fp)
 Find the last message in the file. More...
 
static bool test_last_status_new (FILE *fp)
 Is the last message new. More...
 
bool mbox_test_new_folder (const char *path)
 Test if an mbox or mmdf mailbox has new mail. More...
 
void mbox_reset_atime (struct Mailbox *m, struct stat *st)
 Reset the access time on the mailbox file. More...
 
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() -. More...
 
static bool mbox_ac_add (struct Account *a, struct Mailbox *m)
 Add a Mailbox to an Account - Implements MxOps::ac_add() -. More...
 
static FILE * mbox_open_readwrite (struct Mailbox *m)
 Open an mbox read-write. More...
 
static FILE * mbox_open_readonly (struct Mailbox *m)
 Open an mbox read-only. More...
 
static enum MxOpenReturns mbox_mbox_open (struct Mailbox *m)
 Open a Mailbox - Implements MxOps::mbox_open() -. More...
 
static bool mbox_mbox_open_append (struct Mailbox *m, OpenMailboxFlags flags)
 Open a Mailbox for appending - Implements MxOps::mbox_open_append() -. More...
 
static enum MxStatus mbox_mbox_check (struct Mailbox *m)
 Check for new mail - Implements MxOps::mbox_check() -. More...
 
static enum MxStatus mbox_mbox_sync (struct Mailbox *m)
 Save changes to the Mailbox - Implements MxOps::mbox_sync() -. More...
 
static enum MxStatus mbox_mbox_close (struct Mailbox *m)
 Close a Mailbox - Implements MxOps::mbox_close() -. More...
 
static bool mbox_msg_open (struct Mailbox *m, struct Message *msg, int msgno)
 Open an email message in a Mailbox - Implements MxOps::msg_open() -. More...
 
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() -. More...
 
static int mbox_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -. More...
 
static int mbox_msg_close (struct Mailbox *m, struct Message *msg)
 Close an email - Implements MxOps::msg_close() -. More...
 
static int mbox_msg_padding_size (struct Mailbox *m)
 Bytes of padding between messages - Implements MxOps::msg_padding_size() -. More...
 
enum MailboxType mbox_path_probe (const char *path, const struct stat *st)
 Is this an mbox Mailbox? - Implements MxOps::path_probe() -. More...
 
static int mbox_path_canon (char *buf, size_t buflen)
 Canonicalise a Mailbox path - Implements MxOps::path_canon() -. More...
 
static int mbox_path_pretty (char *buf, size_t buflen, const char *folder)
 Abbreviate a Mailbox path - Implements MxOps::path_pretty() -. More...
 
static int mbox_path_parent (char *buf, size_t buflen)
 Find the parent of a Mailbox path - Implements MxOps::path_parent() -. More...
 
static int mbox_path_is_empty (const char *path)
 Is the mailbox empty - Implements MxOps::path_is_empty() -. More...
 
static int mmdf_msg_commit (struct Mailbox *m, struct Message *msg)
 Save changes to an email - Implements MxOps::msg_commit() -. More...
 
static int mmdf_msg_padding_size (struct Mailbox *m)
 Bytes of padding between messages - Implements MxOps::msg_padding_size() -. More...
 
static enum MxStatus mbox_mbox_check_stats (struct Mailbox *m, uint8_t flags)
 Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -. More...
 

Variables

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

Detailed Description

Mbox local mailbox type.

Authors
  • Michael R. Elkins
  • Richard Russon
  • 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_free()

static void mbox_adata_free ( void **  ptr)
static

Free the private Account data - Implements Account::adata_free()

Definition at line 77 of file mbox.c.

78{
79 struct MboxAccountData *m = *ptr;
80
82 FREE(ptr);
83}
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
#define FREE(x)
Definition: memory.h:43
Mbox-specific Account data -.
Definition: lib.h:49
FILE * fp
Mailbox file.
Definition: lib.h:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_adata_new()

static struct MboxAccountData * mbox_adata_new ( void  )
static

Create a new MboxAccountData struct.

Return values
ptrNew MboxAccountData

Definition at line 89 of file mbox.c.

90{
91 return mutt_mem_calloc(1, sizeof(struct MboxAccountData));
92}
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
+ Here is the call graph for this function:
+ 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 100 of file mbox.c.

101{
102 if (!m || !m->account)
103 return -1;
104 if ((m->type != MUTT_MBOX) && (m->type != MUTT_MMDF))
105 return -1;
106 if (m->account->adata)
107 return 0;
108
111 return 0;
112}
@ MUTT_MMDF
'mmdf' Mailbox type
Definition: mailbox.h:46
@ MUTT_MBOX
'mbox' Mailbox type
Definition: mailbox.h:45
static struct MboxAccountData * mbox_adata_new(void)
Create a new MboxAccountData struct.
Definition: mbox.c:89
static void mbox_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free()
Definition: mbox.c:77
void(* adata_free)(void **ptr)
Free the private data attached to the Account.
Definition: account.h:53
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:127
+ 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 119 of file mbox.c.

120{
121 if (init_mailbox(m) == -1)
122 return NULL;
123 return m->account->adata;
124}
static int init_mailbox(struct Mailbox *m)
Add Mbox data to the Mailbox.
Definition: mbox.c:100
+ 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 134 of file mbox.c.

135{
137 if (!adata)
138 return -1;
139
140 int rc = mutt_file_lock(fileno(adata->fp), excl, retry);
141 if (rc == 0)
142 adata->locked = true;
143 else if (retry && !excl)
144 {
145 m->readonly = true;
146 return 0;
147 }
148
149 return rc;
150}
int mutt_file_lock(int fd, bool excl, bool timeout)
(Try to) Lock a file using fcntl()
Definition: file.c:1231
static struct MboxAccountData * mbox_adata_get(struct Mailbox *m)
Get the private data associated with a Mailbox.
Definition: mbox.c:119
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:115
+ 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 156 of file mbox.c.

157{
159 if (!adata)
160 return;
161
162 if (adata->locked)
163 {
164 fflush(adata->fp);
165
166 mutt_file_unlock(fileno(adata->fp));
167 adata->locked = false;
168 }
169}
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1279
+ 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 176 of file mbox.c.

177{
178 if (!m)
179 return MX_OPEN_ERROR;
180
182 if (!adata)
183 return MX_OPEN_ERROR;
184
185 char buf[8192] = { 0 };
186 char return_path[1024] = { 0 };
187 int count = 0;
188 int lines;
189 time_t t = 0;
190 LOFF_T loc, tmploc;
191 struct Email *e = NULL;
192 struct stat st = { 0 };
193 struct Progress *progress = NULL;
195
196 if (stat(mailbox_path(m), &st) == -1)
197 {
199 goto fail;
200 }
203 m->size = st.st_size;
204
205 buf[sizeof(buf) - 1] = '\0';
206
207 if (m->verbose)
208 {
209 char msg[PATH_MAX] = { 0 };
210 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
211 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
212 }
213
214 while (true)
215 {
216 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
217 break;
218
219 if (SigInt)
220 break;
221
222 if (mutt_str_equal(buf, MMDF_SEP))
223 {
224 loc = ftello(adata->fp);
225 if (loc < 0)
226 goto fail;
227
228 count++;
229 if (m->verbose)
230 progress_update(progress, count, (int) (loc / (m->size / 100 + 1)));
231
232 if (m->msg_count == m->email_max)
234 e = email_new();
235 m->emails[m->msg_count] = e;
236 e->offset = loc;
237 e->index = m->msg_count;
238
239 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
240 {
241 /* TODO: memory leak??? */
242 mutt_debug(LL_DEBUG1, "unexpected EOF\n");
243 break;
244 }
245
246 return_path[0] = '\0';
247
248 if (!is_from(buf, return_path, sizeof(return_path), &t))
249 {
250 if (!mutt_file_seek(adata->fp, loc, SEEK_SET))
251 {
252 mutt_error(_("Mailbox is corrupt"));
253 goto fail;
254 }
255 }
256 else
257 {
258 e->received = t - mutt_date_local_tz(t);
259 }
260
261 e->env = mutt_rfc822_read_header(adata->fp, e, false, false);
262
263 loc = ftello(adata->fp);
264 if (loc < 0)
265 goto fail;
266
267 if ((e->body->length > 0) && (e->lines > 0))
268 {
269 tmploc = loc + e->body->length;
270
271 if ((tmploc > 0) && (tmploc < m->size))
272 {
273 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
274 !fgets(buf, sizeof(buf) - 1, adata->fp) || !mutt_str_equal(MMDF_SEP, buf))
275 {
276 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
277 e->body->length = -1;
278 }
279 }
280 else
281 e->body->length = -1;
282 }
283 else
284 e->body->length = -1;
285
286 if (e->body->length < 0)
287 {
288 lines = -1;
289 do
290 {
291 loc = ftello(adata->fp);
292 if (loc < 0)
293 goto fail;
294 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
295 break;
296 lines++;
297 } while (!mutt_str_equal(buf, MMDF_SEP));
298
299 e->lines = lines;
300 e->body->length = loc - e->body->offset;
301 }
302
303 if (TAILQ_EMPTY(&e->env->return_path) && return_path[0])
304 mutt_addrlist_parse(&e->env->return_path, return_path);
305
306 if (TAILQ_EMPTY(&e->env->from))
307 mutt_addrlist_copy(&e->env->from, &e->env->return_path, false);
308
309 m->msg_count++;
310 }
311 else
312 {
313 mutt_debug(LL_DEBUG1, "corrupt mailbox\n");
314 mutt_error(_("Mailbox is corrupt"));
315 goto fail;
316 }
317 }
318
319 if (SigInt)
320 {
321 SigInt = false;
322 rc = MX_OPEN_ABORT; /* action aborted */
323 goto fail;
324 }
325
326 rc = MX_OPEN_OK;
327fail:
328 progress_free(&progress);
329 return rc;
330}
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:737
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition: date.c:206
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
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:1611
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:690
@ MUTT_STAT_ATIME
File/dir's atime - last accessed time.
Definition: file.h:62
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:63
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:48
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
#define MMDF_SEP
Definition: lib.h:61
#define _(a)
Definition: message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
#define PATH_MAX
Definition: mutt.h:40
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:69
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1219
MxOpenReturns
Return values for mbox_open()
Definition: mxapi.h:97
@ MX_OPEN_ERROR
Open failed with an error.
Definition: mxapi.h:99
@ MX_OPEN_ABORT
Open was aborted.
Definition: mxapi.h:100
@ MX_OPEN_OK
Open succeeded.
Definition: mxapi.h:98
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1158
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: lib.h:49
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:86
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:118
#define TAILQ_EMPTY(head)
Definition: queue.h:721
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:37
struct Envelope * env
Envelope information.
Definition: email.h:66
int lines
How many lines in the body of this message?
Definition: email.h:60
struct Body * body
List of MIME parts.
Definition: email.h:67
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:69
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:59
struct AddressList return_path
Return path for the Email.
Definition: envelope.h:58
struct AddressList from
Email's 'From' list.
Definition: envelope.h:59
struct timespec mtime
Time Mailbox was last changed.
Definition: mailbox.h:104
int msg_count
Total number of messages.
Definition: mailbox.h:88
int email_max
Number of pointers in emails.
Definition: mailbox.h:97
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
off_t size
Size of the Mailbox.
Definition: mailbox.h:84
bool verbose
Display status messages?
Definition: mailbox.h:114
struct timespec atime
File's last-access time.
Definition: lib.h:51
+ 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 343 of file mbox.c.

344{
345 if (!m)
346 return MX_OPEN_ERROR;
347
349 if (!adata)
350 return MX_OPEN_ERROR;
351
352 struct stat st = { 0 };
353 char buf[8192], return_path[256];
354 struct Email *e_cur = NULL;
355 time_t t = 0;
356 int count = 0, lines = 0;
357 LOFF_T loc;
358 struct Progress *progress = NULL;
360
361 /* Save information about the folder at the time we opened it. */
362 if (stat(mailbox_path(m), &st) == -1)
363 {
365 goto fail;
366 }
367
368 m->size = st.st_size;
371
372 if (!m->readonly)
373 m->readonly = access(mailbox_path(m), W_OK) ? true : false;
374
375 if (m->verbose)
376 {
377 char msg[PATH_MAX] = { 0 };
378 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
379 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
380 }
381
382 loc = ftello(adata->fp);
383 while ((fgets(buf, sizeof(buf), adata->fp)) && !SigInt)
384 {
385 if (is_from(buf, return_path, sizeof(return_path), &t))
386 {
387 /* Save the Content-Length of the previous message */
388 if (count > 0)
389 {
390 struct Email *e = m->emails[m->msg_count - 1];
391 if (e->body->length < 0)
392 {
393 e->body->length = loc - e->body->offset - 1;
394 if (e->body->length < 0)
395 e->body->length = 0;
396 }
397 if (!e->lines)
398 e->lines = lines ? lines - 1 : 0;
399 }
400
401 count++;
402
403 if (m->verbose)
404 {
405 progress_update(progress, count, (int) (ftello(adata->fp) / (m->size / 100 + 1)));
406 }
407
408 if (m->msg_count == m->email_max)
410
411 m->emails[m->msg_count] = email_new();
412 e_cur = m->emails[m->msg_count];
413 e_cur->received = t - mutt_date_local_tz(t);
414 e_cur->offset = loc;
415 e_cur->index = m->msg_count;
416
417 e_cur->env = mutt_rfc822_read_header(adata->fp, e_cur, false, false);
418
419 /* if we know how long this message is, either just skip over the body,
420 * or if we don't know how many lines there are, count them now (this will
421 * save time by not having to search for the next message marker). */
422 if (e_cur->body->length > 0)
423 {
424 LOFF_T tmploc;
425
426 loc = ftello(adata->fp);
427
428 /* The test below avoids a potential integer overflow if the
429 * content-length is huge (thus necessarily invalid). */
430 tmploc = (e_cur->body->length < m->size) ? (loc + e_cur->body->length + 1) : -1;
431
432 if ((tmploc > 0) && (tmploc < m->size))
433 {
434 /* check to see if the content-length looks valid. we expect to
435 * to see a valid message separator at this point in the stream */
436 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
437 !fgets(buf, sizeof(buf), adata->fp) || !mutt_str_startswith(buf, "From "))
438 {
439 mutt_debug(LL_DEBUG1, "bad content-length in message %d (cl=" OFF_T_FMT ")\n",
440 e_cur->index, e_cur->body->length);
441 mutt_debug(LL_DEBUG1, " LINE: %s", buf);
442 /* nope, return the previous position */
443 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
444 e_cur->body->length = -1;
445 }
446 }
447 else if (tmploc != m->size)
448 {
449 /* content-length would put us past the end of the file, so it
450 * must be wrong */
451 e_cur->body->length = -1;
452 }
453
454 if (e_cur->body->length != -1)
455 {
456 /* good content-length. check to see if we know how many lines
457 * are in this message. */
458 if (e_cur->lines == 0)
459 {
460 int cl = e_cur->body->length;
461
462 /* count the number of lines in this message */
463 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
464 while (cl-- > 0)
465 {
466 if (fgetc(adata->fp) == '\n')
467 e_cur->lines++;
468 }
469 }
470
471 /* return to the offset of the next message separator */
472 (void) mutt_file_seek(adata->fp, tmploc, SEEK_SET);
473 }
474 }
475
476 m->msg_count++;
477
478 if (TAILQ_EMPTY(&e_cur->env->return_path) && return_path[0])
479 {
480 mutt_addrlist_parse(&e_cur->env->return_path, return_path);
481 }
482
483 if (TAILQ_EMPTY(&e_cur->env->from))
484 mutt_addrlist_copy(&e_cur->env->from, &e_cur->env->return_path, false);
485
486 lines = 0;
487 }
488 else
489 lines++;
490
491 loc = ftello(adata->fp);
492 }
493
494 /* Only set the content-length of the previous message if we have read more
495 * than one message during _this_ invocation. If this routine is called
496 * when new mail is received, we need to make sure not to clobber what
497 * previously was the last message since the headers may be sorted. */
498 if (count > 0)
499 {
500 struct Email *e = m->emails[m->msg_count - 1];
501 if (e->body->length < 0)
502 {
503 e->body->length = ftello(adata->fp) - e->body->offset - 1;
504 if (e->body->length < 0)
505 e->body->length = 0;
506 }
507
508 if (!e->lines)
509 e->lines = lines ? lines - 1 : 0;
510 }
511
512 if (SigInt)
513 {
514 SigInt = false;
515 rc = MX_OPEN_ABORT;
516 goto fail; /* action aborted */
517 }
518
519 rc = MX_OPEN_OK;
520fail:
521 progress_free(&progress);
522 return rc;
523}
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
+ 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 531 of file mbox.c.

532{
533 if (!m)
534 return -1;
535
537 if (!adata)
538 return -1;
539
540 bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL;
541 struct Email **e_old = NULL;
542 int old_msg_count;
543 bool msg_mod = false;
544 int rc = -1;
545
546 /* silent operations */
547 m->verbose = false;
548
549 /* our heuristics require the old mailbox to be unsorted */
550 const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
551 if (c_sort != SORT_ORDER)
552 {
555 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
556 }
557
558 e_old = NULL;
559 old_msg_count = 0;
560
561 /* simulate a close */
565 FREE(&m->v2r);
566 if (m->readonly)
567 {
568 for (int i = 0; i < m->msg_count; i++)
569 email_free(&(m->emails[i])); /* nothing to do! */
570 FREE(&m->emails);
571 }
572 else
573 {
574 /* save the old headers */
575 old_msg_count = m->msg_count;
576 e_old = m->emails;
577 m->emails = NULL;
578 }
579
580 m->email_max = 0; /* force allocation of new headers */
581 m->msg_count = 0;
582 m->vcount = 0;
583 m->msg_tagged = 0;
584 m->msg_deleted = 0;
585 m->msg_new = 0;
586 m->msg_unread = 0;
587 m->msg_flagged = 0;
588 m->changed = false;
589 m->id_hash = NULL;
590 m->subj_hash = NULL;
592
593 switch (m->type)
594 {
595 case MUTT_MBOX:
596 case MUTT_MMDF:
597 cmp_headers = email_cmp_strict;
598 mutt_file_fclose(&adata->fp);
599 adata->fp = mutt_file_fopen(mailbox_path(m), "r");
600 if (!adata->fp)
601 rc = -1;
602 else if (m->type == MUTT_MBOX)
603 rc = mbox_parse_mailbox(m);
604 else
605 rc = mmdf_parse_mailbox(m);
606 break;
607
608 default:
609 rc = -1;
610 break;
611 }
612
613 if (rc == -1)
614 {
615 /* free the old headers */
616 for (int i = 0; i < old_msg_count; i++)
617 email_free(&(e_old[i]));
618 FREE(&e_old);
619
620 m->verbose = true;
621 return -1;
622 }
623
624 mutt_file_touch_atime(fileno(adata->fp));
625
626 /* now try to recover the old flags */
627
628 if (!m->readonly)
629 {
630 for (int i = 0; i < m->msg_count; i++)
631 {
632 bool found = false;
633
634 /* some messages have been deleted, and new messages have been
635 * appended at the end; the heuristic is that old messages have then
636 * "advanced" towards the beginning of the folder, so we begin the
637 * search at index "i" */
638 int j;
639 for (j = i; j < old_msg_count; j++)
640 {
641 if (!e_old[j])
642 continue;
643 if (cmp_headers(m->emails[i], e_old[j]))
644 {
645 found = true;
646 break;
647 }
648 }
649 if (!found)
650 {
651 for (j = 0; (j < i) && (j < old_msg_count); j++)
652 {
653 if (!e_old[j])
654 continue;
655 if (cmp_headers(m->emails[i], e_old[j]))
656 {
657 found = true;
658 break;
659 }
660 }
661 }
662
663 if (found)
664 {
665 m->changed = true;
666 if (e_old[j]->changed)
667 {
668 /* Only update the flags if the old header was changed;
669 * otherwise, the header may have been modified externally,
670 * and we don't want to lose _those_ changes */
671 mutt_set_flag(m, m->emails[i], MUTT_FLAG, e_old[j]->flagged);
672 mutt_set_flag(m, m->emails[i], MUTT_REPLIED, e_old[j]->replied);
673 mutt_set_flag(m, m->emails[i], MUTT_OLD, e_old[j]->old);
674 mutt_set_flag(m, m->emails[i], MUTT_READ, e_old[j]->read);
675 }
676 mutt_set_flag(m, m->emails[i], MUTT_DELETE, e_old[j]->deleted);
677 mutt_set_flag(m, m->emails[i], MUTT_PURGE, e_old[j]->purge);
678 mutt_set_flag(m, m->emails[i], MUTT_TAG, e_old[j]->tagged);
679
680 /* we don't need this header any more */
681 email_free(&(e_old[j]));
682 }
683 }
684
685 /* free the remaining old headers */
686 for (int j = 0; j < old_msg_count; j++)
687 {
688 if (e_old[j])
689 {
690 email_free(&(e_old[j]));
691 msg_mod = true;
692 }
693 }
694 FREE(&e_old);
695 }
696
698 m->verbose = true;
699
700 return (m->changed || msg_mod) ? MX_STATUS_REOPENED : MX_STATUS_NEW_MAIL;
701}
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
bool email_cmp_strict(const struct Email *e1, const struct Email *e2)
Strictly compare message emails.
Definition: email.c:100
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
void mutt_file_touch_atime(int fd)
Set the access time to current time.
Definition: file.c:1082
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:222
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:177
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:178
static enum MxOpenReturns mbox_parse_mailbox(struct Mailbox *m)
Read a mailbox from disk.
Definition: mbox.c:343
static enum MxOpenReturns mmdf_parse_mailbox(struct Mailbox *m)
Read a mailbox in MMDF format.
Definition: mbox.c:176
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:93
@ MUTT_OLD
Old messages.
Definition: mutt.h:91
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:97
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:100
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:99
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:95
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:92
void mutt_make_label_hash(struct Mailbox *m)
Create a Hash Table to store the labels.
Definition: mutt_header.c:371
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:89
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:87
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:63
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:48
bool changed
Email has been edited.
Definition: email.h:75
int vcount
The number of virtual messages.
Definition: mailbox.h:99
bool changed
Mailbox has been modified.
Definition: mailbox.h:110
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:98
int msg_new
Number of new messages.
Definition: mailbox.h:92
struct HashTable * subj_hash
Hash Table by subject.
Definition: mailbox.h:124
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:123
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:93
struct HashTable * label_hash
Hash Table for x-labels.
Definition: mailbox.h:125
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:90
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:94
int msg_unread
Number of unread messages.
Definition: mailbox.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
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:305
+ 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 709 of file mbox.c.

710{
711 for (int i = 0; i < m->msg_count; i++)
712 {
713 struct Email *e = m->emails[i];
714 if (!e)
715 break;
716 if (!e->deleted && !e->read && !e->old)
717 return true;
718 }
719 return false;
720}
bool read
Email is read.
Definition: email.h:48
bool old
Email is seen, but unread.
Definition: email.h:47
bool deleted
Email is deleted.
Definition: email.h:76
+ Here is the caller graph for this function:

◆ fseek_last_message()

static int fseek_last_message ( FILE *  fp)
static

Find the last message in the file.

Parameters
fpFile to search
Return values
0Success
-1No message found

Definition at line 728 of file mbox.c.

729{
730 LOFF_T pos;
731 char buf[BUFSIZ + 7] = { 0 }; // 7 for "\n\nFrom "
732 size_t bytes_read;
733
734 if (!mutt_file_seek(fp, 0, SEEK_END))
735 {
736 return -1;
737 }
738 pos = ftello(fp);
739
740 /* Set 'bytes_read' to the size of the last, probably partial, buf;
741 * 0 < 'bytes_read' <= 'BUFSIZ'. */
742 bytes_read = pos % BUFSIZ;
743 if (bytes_read == 0)
744 bytes_read = BUFSIZ;
745 /* Make 'pos' a multiple of 'BUFSIZ' (0 if the file is short), so that all
746 * reads will be on block boundaries, which might increase efficiency. */
747 while ((pos -= bytes_read) >= 0)
748 {
749 /* we save in the buf at the end the first 7 chars from the last read */
750 memcpy(buf + BUFSIZ, buf, 7);
751 if (!mutt_file_seek(fp, pos, SEEK_SET))
752 {
753 return -1;
754 }
755 bytes_read = fread(buf, sizeof(char), bytes_read, fp);
756 if (bytes_read == 0)
757 return -1;
758 /* 'i' is Index into 'buf' for scanning. */
759 for (int i = bytes_read; i >= 0; i--)
760 {
761 if (mutt_str_startswith(buf + i, "\n\nFrom "))
762 { /* found it - go to the beginning of the From */
763 if (!mutt_file_seek(fp, pos + i + 2, SEEK_SET))
764 {
765 return -1;
766 }
767 return 0;
768 }
769 }
770 bytes_read = BUFSIZ;
771 }
772
773 /* here we are at the beginning of the file */
774 if (mutt_str_startswith(buf, "From "))
775 {
776 if (!mutt_file_seek(fp, 0, SEEK_SET))
777 {
778 return -1;
779 }
780 return 0;
781 }
782
783 return -1;
784}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ test_last_status_new()

static bool test_last_status_new ( FILE *  fp)
static

Is the last message new.

Parameters
fpFile to check
Return values
trueThe last message is new

Definition at line 791 of file mbox.c.

792{
793 struct Email *e = NULL;
794 struct Envelope *tmp_envelope = NULL;
795 bool rc = false;
796
797 if (fseek_last_message(fp) == -1)
798 return false;
799
800 e = email_new();
801 tmp_envelope = mutt_rfc822_read_header(fp, e, false, false);
802 if (!e->read && !e->old)
803 rc = true;
804
805 mutt_env_free(&tmp_envelope);
806 email_free(&e);
807
808 return rc;
809}
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:97
static int fseek_last_message(FILE *fp)
Find the last message in the file.
Definition: mbox.c:728
The header of an Email.
Definition: envelope.h:57
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mbox_test_new_folder()

bool mbox_test_new_folder ( const char *  path)

Test if an mbox or mmdf mailbox has new mail.

Parameters
pathPath to the mailbox
Return values
trueThe folder contains new mail

Definition at line 816 of file mbox.c.

817{
818 bool rc = false;
819
820 enum MailboxType type = mx_path_probe(path);
821
822 if ((type != MUTT_MBOX) && (type != MUTT_MMDF))
823 return false;
824
825 FILE *fp = fopen(path, "rb");
826 if (fp)
827 {
828 rc = test_last_status_new(fp);
829 mutt_file_fclose(&fp);
830 }
831
832 return rc;
833}
MailboxType
Supported mailbox formats.
Definition: mailbox.h:41
static bool test_last_status_new(FILE *fp)
Is the last message new.
Definition: mbox.c:791
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1327
+ Here is the call 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 843 of file mbox.c.

844{
845 struct utimbuf utimebuf;
846 struct stat st2 = { 0 };
847
848 if (!st)
849 {
850 if (stat(mailbox_path(m), &st2) < 0)
851 return;
852 st = &st2;
853 }
854
855 utimebuf.actime = st->st_atime;
856 utimebuf.modtime = st->st_mtime;
857
858 /* When $mbox_check_recent is set, existing new mail is ignored, so do not
859 * reset the atime to mtime-1 to signal new mail. */
860 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
861 if (!c_mail_check_recent && (utimebuf.actime >= utimebuf.modtime) && mbox_has_new(m))
862 {
863 utimebuf.actime = utimebuf.modtime - 1;
864 }
865
866 utime(mailbox_path(m), &utimebuf);
867}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
static bool mbox_has_new(struct Mailbox *m)
Does the mailbox have new mail.
Definition: mbox.c:709
+ 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 899 of file mbox.c.

900{
901 FILE *fp = fopen(mailbox_path(m), "r+");
902 if (fp)
903 m->readonly = false;
904 return fp;
905}
+ 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 914 of file mbox.c.

915{
916 FILE *fp = fopen(mailbox_path(m), "r");
917 if (fp)
918 m->readonly = true;
919 return fp;
920}
+ Here is the call graph for this function:
+ Here is the caller graph for this function: