NeoMutt  2025-09-05-70-gcfdde0
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mutt_mailbox.c
Go to the documentation of this file.
1
24
30
31#include "config.h"
32#include <string.h>
33#include <sys/stat.h>
34#include <sys/types.h>
35#include <utime.h>
36#include "mutt/lib.h"
37#include "config/lib.h"
38#include "core/lib.h"
39#include "mutt_mailbox.h"
40#include "postpone/lib.h"
41#include "muttlib.h"
42#include "mx.h"
43
44static time_t MailboxTime = 0;
45static time_t MailboxStatsTime = 0;
46static short MailboxCount = 0;
47static short MailboxNotify = 0;
48
58static bool is_same_mailbox(struct Mailbox *m1, struct Mailbox *m2,
59 struct stat *st1, struct stat *st2)
60{
61 if (!m1 || buf_is_empty(&m1->pathbuf) || !m2 || buf_is_empty(&m2->pathbuf) ||
62 (m1->type != m2->type))
63 {
64 return false;
65 }
66
67 const bool uses_protocol = (m2->type == MUTT_IMAP) || (m2->type == MUTT_NNTP) ||
68 (m2->type == MUTT_NOTMUCH) || (m2->type == MUTT_POP);
69
70 if (uses_protocol)
72 else
73 return ((st1->st_dev == st2->st_dev) && (st1->st_ino == st2->st_ino));
74}
75
83static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check,
84 struct stat *st_cur, CheckStatsFlags flags)
85{
86 struct stat st = { 0 };
87
88 enum MailboxType mb_type = mx_path_probe(mailbox_path(m_check));
89
90 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
91 if ((m_cur == m_check) && c_mail_check_recent)
92 m_check->has_new = false;
93
94 switch (mb_type)
95 {
96 case MUTT_POP:
97 case MUTT_NNTP:
98 case MUTT_NOTMUCH:
99 case MUTT_IMAP:
100 m_check->type = mb_type;
101 break;
102 default:
103 if ((stat(mailbox_path(m_check), &st) != 0) ||
104 ((m_check->type == MUTT_UNKNOWN) && S_ISREG(st.st_mode) && (st.st_size == 0)) ||
105 ((m_check->type == MUTT_UNKNOWN) &&
106 ((m_check->type = mx_path_probe(mailbox_path(m_check))) <= 0)))
107 {
108 /* if the mailbox still doesn't exist, set the newly created flag to be
109 * ready for when it does. */
110 m_check->newly_created = true;
111 m_check->type = MUTT_UNKNOWN;
112 m_check->size = 0;
113 return;
114 }
115 break; // kept for consistency.
116 }
117
118 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
119
120 /* check to see if the folder is the currently selected folder before polling */
121 if (!is_same_mailbox(m_cur, m_check, st_cur, &st))
122 {
123 switch (m_check->type)
124 {
125 case MUTT_NOTMUCH:
126 // Remove this when non-notmuch backends only check unread, flagged,
127 // and total counts per 'mbox_check_stats' docs.
128 if ((flags & MUTT_MAILBOX_CHECK_STATS) == 0)
129 break;
131
132 case MUTT_IMAP:
133 case MUTT_MBOX:
134 case MUTT_MMDF:
135 case MUTT_MAILDIR:
136 case MUTT_MH:
137 mx_mbox_check_stats(m_check, flags);
138 break;
139 default:; /* do nothing */
140 }
141 }
142 else if (c_check_mbox_size && m_cur && buf_is_empty(&m_cur->pathbuf))
143 {
144 m_check->size = (off_t) st.st_size; /* update the size of current folder */
145 }
146
147 if (!m_check->has_new)
148 {
149 m_check->notified = false;
150 }
151 else
152 {
153 // pretend that we've already notified for the mailbox
154 if (!m_check->notify_user)
155 m_check->notified = true;
156 else if (!m_check->notified)
158 }
159}
160
170{
171 if (ARRAY_EMPTY(&NeoMutt->accounts)) // fast return if there are no mailboxes
172 return 0;
173
176
177 const short c_mail_check = cs_subset_number(NeoMutt->sub, "mail_check");
178 const bool c_mail_check_stats = cs_subset_bool(NeoMutt->sub, "mail_check_stats");
179 const short c_mail_check_stats_interval = cs_subset_number(NeoMutt->sub, "mail_check_stats_interval");
180
181 time_t t = mutt_date_now();
182 if ((flags == MUTT_MAILBOX_CHECK_NO_FLAGS) && ((t - MailboxTime) < c_mail_check))
183 return MailboxCount;
184
185 if ((flags & MUTT_MAILBOX_CHECK_STATS) ||
186 (c_mail_check_stats && ((t - MailboxStatsTime) >= c_mail_check_stats_interval)))
187 {
190 }
191
192 MailboxTime = t;
193 MailboxCount = 0;
194 MailboxNotify = 0;
195
196 /* check device ID and serial number instead of comparing paths */
197 struct stat st_cur = { 0 };
198 if (!m_cur || (m_cur->type == MUTT_IMAP) || (m_cur->type == MUTT_POP) ||
199 (m_cur->type == MUTT_NNTP) || stat(mailbox_path(m_cur), &st_cur) != 0)
200 {
201 st_cur.st_dev = 0;
202 st_cur.st_ino = 0;
203 }
204
205 struct MailboxArray ma = neomutt_mailboxes_get(NeoMutt, MUTT_MAILBOX_ANY);
206 struct Mailbox **mp = NULL;
207 ARRAY_FOREACH(mp, &ma)
208 {
209 struct Mailbox *m = *mp;
210
211 if (!m->visible || !m->poll_new_mail)
212 continue;
213
214 CheckStatsFlags m_flags = flags;
215 if (!m->first_check_stats_done && c_mail_check_stats)
216 {
217 m_flags |= MUTT_MAILBOX_CHECK_STATS;
218 }
219 mailbox_check(m_cur, m, &st_cur, m_flags);
220 if (m->has_new)
221 MailboxCount++;
222 m->first_check_stats_done = true;
223 }
224 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
225
226 return MailboxCount;
227}
228
234bool mutt_mailbox_notify(struct Mailbox *m_cur)
235{
237 {
238 return mutt_mailbox_list();
239 }
240 return false;
241}
242
248{
249 int have_unnotified = MailboxNotify;
250
251 struct Buffer *path = buf_pool_get();
252 struct Buffer *mailboxlist = buf_pool_get();
253
254 buf_addstr(mailboxlist, _("New mail in "));
255 struct MailboxArray ma = neomutt_mailboxes_get(NeoMutt, MUTT_MAILBOX_ANY);
256 struct Mailbox **mp = NULL;
257 bool any_new = false;
258 ARRAY_FOREACH(mp, &ma)
259 {
260 struct Mailbox *m = *mp;
261
262 /* Is there new mail in this mailbox? */
263 if (!m->has_new || (have_unnotified && m->notified))
264 continue;
265
266 buf_strcpy(path, mailbox_path(m));
267 buf_pretty_mailbox(path);
268
269 if (any_new)
270 {
271 buf_addstr(mailboxlist, ", ");
272 }
273
274 if (!m->notified)
275 {
276 m->notified = true;
278 }
279 buf_addstr(mailboxlist, buf_string(path));
280 any_new = true;
281 }
282 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
283
284 buf_pool_release(&path);
285
286 if (any_new)
287 {
288 mutt_message("%s", buf_string(mailboxlist));
289 buf_pool_release(&mailboxlist);
290 return true;
291 }
292
293 /* there were no mailboxes needing to be notified, so clean up since
294 * MailboxNotify has somehow gotten out of sync */
295 MailboxNotify = 0;
296 buf_pool_release(&mailboxlist);
297 return false;
298}
299
305{
306 if (!m)
307 return;
308
309 m->notified = true;
311}
312
322static struct Mailbox *find_next_mailbox(struct Buffer *s, bool find_new)
323{
324 bool found = false;
325 for (int pass = 0; pass < 2; pass++)
326 {
327 struct MailboxArray ma = neomutt_mailboxes_get(NeoMutt, MUTT_MAILBOX_ANY);
328 struct Mailbox **mp = NULL;
329 ARRAY_FOREACH(mp, &ma)
330 {
331 struct Mailbox *m = *mp;
332
334 struct Mailbox *m_cur = m;
335
336 if ((found || (pass > 0)) && (find_new ? m_cur->has_new : m_cur->msg_unread > 0))
337 {
340 struct Mailbox *m_result = m;
341 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
342 return m_result;
343 }
345 found = true;
346 }
347 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
348 }
349
350 return NULL;
351}
352
362struct Mailbox *mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
363{
365
367 {
368 struct Mailbox *m_res = find_next_mailbox(s, true);
369 if (m_res)
370 return m_res;
371
373 }
374
375 buf_reset(s); // no folders with new mail
376 return NULL;
377}
378
388struct Mailbox *mutt_mailbox_next_unread(struct Mailbox *m_cur, struct Buffer *s)
389{
391
392 struct Mailbox *m_res = find_next_mailbox(s, false);
393 if (m_res)
394 return m_res;
395
396 buf_reset(s); // no folders with new mail
397 return NULL;
398}
399
408void mailbox_restore_timestamp(const char *path, struct stat *st)
409{
410#ifdef HAVE_UTIMENSAT
411 struct timespec ts[2] = { { 0 }, { 0 } };
412#else
413 struct utimbuf ut = { 0 };
414#endif
415
416 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
417 if (c_check_mbox_size)
418 {
419 struct Mailbox *m = mailbox_find(path);
420 if (m && !m->has_new)
422 }
423 else
424 {
425 /* fix up the times so mailbox won't get confused */
426 if (st->st_mtime > st->st_atime)
427 {
428#ifdef HAVE_UTIMENSAT
429 ts[0].tv_sec = 0;
430 ts[0].tv_nsec = UTIME_OMIT;
431 ts[1].tv_sec = 0;
432 ts[1].tv_nsec = UTIME_NOW;
433 utimensat(AT_FDCWD, buf, ts, 0);
434#else
435 ut.actime = st->st_atime;
436 ut.modtime = mutt_date_now();
437 utime(path, &ut);
438#endif
439 }
440 else
441 {
442#ifdef HAVE_UTIMENSAT
443 ts[0].tv_sec = 0;
444 ts[0].tv_nsec = UTIME_NOW;
445 ts[1].tv_sec = 0;
446 ts[1].tv_nsec = UTIME_NOW;
447 utimensat(AT_FDCWD, buf, ts, 0);
448#else
449 utime(path, NULL);
450#endif
451 }
452 }
453}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:214
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:204
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
void mailbox_update(struct Mailbox *m)
Get the mailbox's current size.
Definition mailbox.c:213
struct Mailbox * mailbox_find(const char *path)
Find the mailbox with a given path.
Definition mailbox.c:150
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:214
MailboxType
Supported mailbox formats.
Definition mailbox.h:41
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition mailbox.h:51
@ MUTT_MMDF
'mmdf' Mailbox type
Definition mailbox.h:46
@ MUTT_POP
'POP3' Mailbox type
Definition mailbox.h:52
@ MUTT_MH
'MH' Mailbox type
Definition mailbox.h:47
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:49
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:50
@ MUTT_MBOX
'mbox' Mailbox type
Definition mailbox.h:45
@ MUTT_MAILBOX_ANY
Match any Mailbox type.
Definition mailbox.h:42
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition mailbox.h:44
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition mailbox.h:48
#define mutt_message(...)
Definition logging2.h:92
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition date.c:455
void mutt_time_now(struct timespec *tp)
Set the provided time field to the current time.
Definition date.c:479
Convenience wrapper for the library headers.
#define FALLTHROUGH
Definition lib.h:113
#define _(a)
Definition message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:660
static time_t MailboxStatsTime
last time we check performed mail_check_stats
static short MailboxCount
how many boxes with new mail
static short MailboxNotify
int mutt_mailbox_check(struct Mailbox *m_cur, CheckStatsFlags flags)
Check all all Mailboxes for new mail.
void mutt_mailbox_set_notified(struct Mailbox *m)
Note when the user was last notified of new mail.
struct Mailbox * mutt_mailbox_next_unread(struct Mailbox *m_cur, struct Buffer *s)
Find next mailbox with unread mail.
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there's new mail.
struct Mailbox * mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
Incoming folders completion routine.
bool mutt_mailbox_list(void)
Show a message with the list of mailboxes with new mail.
static bool is_same_mailbox(struct Mailbox *m1, struct Mailbox *m2, struct stat *st1, struct stat *st2)
Compare two Mailboxes to see if they're equal.
static struct Mailbox * find_next_mailbox(struct Buffer *s, bool find_new)
Find the next mailbox with new or unread mail.
static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check, struct stat *st_cur, CheckStatsFlags flags)
Check a mailbox for new mail.
static time_t MailboxTime
last time we started checking for mail
void mailbox_restore_timestamp(const char *path, struct stat *st)
Restore the timestamp of a mailbox.
Mailbox helper functions.
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:518
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition muttlib.c:314
Some miscellaneous functions.
enum MxStatus mx_mbox_check_stats(struct Mailbox *m, uint8_t flags)
Check the statistics for a mailbox - Wrapper for MxOps::mbox_check_stats()
Definition mx.c:1776
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition mx.c:1326
API for mailboxes.
#define MUTT_MAILBOX_CHECK_NO_FLAGS
No flags are set.
Definition mxapi.h:50
#define MUTT_MAILBOX_CHECK_STATS
Ignore mail_check_stats and calculate statistics (used by <check-stats>)
Definition mxapi.h:52
#define MUTT_MAILBOX_CHECK_POSTPONED
Make sure the number of postponed messages is updated.
Definition mxapi.h:51
uint8_t CheckStatsFlags
Flags for mutt_mailbox_check.
Definition mxapi.h:49
struct MailboxArray neomutt_mailboxes_get(struct NeoMutt *n, enum MailboxType type)
Get an Array of matching Mailboxes.
Definition neomutt.c:184
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:96
Postponed Emails.
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition postpone.c:177
String manipulation buffer.
Definition buffer.h:36
A mailbox.
Definition mailbox.h:79
bool has_new
Mailbox has new mail.
Definition mailbox.h:85
bool first_check_stats_done
True when the check have been done at least one time.
Definition mailbox.h:112
enum MailboxType type
Mailbox type.
Definition mailbox.h:102
bool newly_created
Mbox or mmdf just popped into existence.
Definition mailbox.h:103
bool poll_new_mail
Check for new mail.
Definition mailbox.h:115
bool notify_user
Notify the user of new mail.
Definition mailbox.h:113
struct Buffer pathbuf
Path of the Mailbox.
Definition mailbox.h:80
bool notified
User has been notified.
Definition mailbox.h:101
off_t size
Size of the Mailbox.
Definition mailbox.h:84
bool visible
True if a result of "mailboxes".
Definition mailbox.h:130
struct timespec last_visited
Time of last exit from this mailbox.
Definition mailbox.h:104
int msg_unread
Number of unread messages.
Definition mailbox.h:89
Container for Accounts, Notifications.
Definition neomutt.h:42
struct AccountArray accounts
All Accounts.
Definition neomutt.h:47
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:46