NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mutt_mailbox.c
Go to the documentation of this file.
1
28#include "config.h"
29#include <string.h>
30#include <sys/stat.h>
31#include <time.h>
32#include <utime.h>
33#include "mutt/lib.h"
34#include "config/lib.h"
35#include "core/lib.h"
36#include "mutt_mailbox.h"
37#include "postpone/lib.h"
38#include "muttlib.h"
39#include "mx.h"
40
41static time_t MailboxTime = 0;
42static time_t MailboxStatsTime = 0;
43static short MailboxCount = 0;
44static short MailboxNotify = 0;
45
55static bool is_same_mailbox(struct Mailbox *m1, struct Mailbox *m2,
56 struct stat *st1, struct stat *st2)
57{
58 if (!m1 || buf_is_empty(&m1->pathbuf) || !m2 || buf_is_empty(&m2->pathbuf) ||
59 (m1->type != m2->type))
60 {
61 return false;
62 }
63
64 const bool uses_protocol = (m2->type == MUTT_IMAP) || (m2->type == MUTT_NNTP) ||
65 (m2->type == MUTT_NOTMUCH) || (m2->type == MUTT_POP);
66
67 if (uses_protocol)
69 else
70 return ((st1->st_dev == st2->st_dev) && (st1->st_ino == st2->st_ino));
71}
72
80static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check,
81 struct stat *st_cur, CheckStatsFlags flags)
82{
83 struct stat st = { 0 };
84
85 enum MailboxType mb_type = mx_path_probe(mailbox_path(m_check));
86
87 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
88 if ((m_cur == m_check) && c_mail_check_recent)
89 m_check->has_new = false;
90
91 switch (mb_type)
92 {
93 case MUTT_POP:
94 case MUTT_NNTP:
95 case MUTT_NOTMUCH:
96 case MUTT_IMAP:
97 m_check->type = mb_type;
98 break;
99 default:
100 if ((stat(mailbox_path(m_check), &st) != 0) ||
101 ((m_check->type == MUTT_UNKNOWN) && S_ISREG(st.st_mode) && (st.st_size == 0)) ||
102 ((m_check->type == MUTT_UNKNOWN) &&
103 ((m_check->type = mx_path_probe(mailbox_path(m_check))) <= 0)))
104 {
105 /* if the mailbox still doesn't exist, set the newly created flag to be
106 * ready for when it does. */
107 m_check->newly_created = true;
108 m_check->type = MUTT_UNKNOWN;
109 m_check->size = 0;
110 return;
111 }
112 break; // kept for consistency.
113 }
114
115 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
116
117 /* check to see if the folder is the currently selected folder before polling */
118 if (!is_same_mailbox(m_cur, m_check, st_cur, &st))
119 {
120 switch (m_check->type)
121 {
122 case MUTT_NOTMUCH:
123 // Remove this when non-notmuch backends only check unread, flagged,
124 // and total counts per 'mbox_check_stats' docs.
125 if ((flags & MUTT_MAILBOX_CHECK_FORCE_STATS) == 0)
126 break;
127 /* fall through */
128 case MUTT_IMAP:
129 case MUTT_MBOX:
130 case MUTT_MMDF:
131 case MUTT_MAILDIR:
132 case MUTT_MH:
133 mx_mbox_check_stats(m_check, flags);
134 break;
135 default:; /* do nothing */
136 }
137 }
138 else if (c_check_mbox_size && m_cur && buf_is_empty(&m_cur->pathbuf))
139 {
140 m_check->size = (off_t) st.st_size; /* update the size of current folder */
141 }
142
143 if (!m_check->has_new)
144 {
145 m_check->notified = false;
146 }
147 else
148 {
149 // pretend that we've already notified for the mailbox
150 if (!m_check->notify_user)
151 m_check->notified = true;
152 else if (!m_check->notified)
154 }
155}
156
166{
167 if (TAILQ_EMPTY(&NeoMutt->accounts)) // fast return if there are no mailboxes
168 return 0;
169
170#ifdef USE_IMAP
171 if (flags & MUTT_MAILBOX_CHECK_FORCE)
173#endif
174
175 const short c_mail_check = cs_subset_number(NeoMutt->sub, "mail_check");
176 const bool c_mail_check_stats = cs_subset_bool(NeoMutt->sub, "mail_check_stats");
177 const short c_mail_check_stats_interval = cs_subset_number(NeoMutt->sub, "mail_check_stats_interval");
178
179 time_t t = mutt_date_now();
180 if ((flags == MUTT_MAILBOX_CHECK_NO_FLAGS) && ((t - MailboxTime) < c_mail_check))
181 return MailboxCount;
182
183 if ((flags & MUTT_MAILBOX_CHECK_FORCE_STATS) ||
184 (c_mail_check_stats && ((t - MailboxStatsTime) >= c_mail_check_stats_interval)))
185 {
188 }
189
190 MailboxTime = t;
191 MailboxCount = 0;
192 MailboxNotify = 0;
193
194 /* check device ID and serial number instead of comparing paths */
195 struct stat st_cur = { 0 };
196 if (!m_cur || (m_cur->type == MUTT_IMAP) || (m_cur->type == MUTT_POP)
197#ifdef USE_NNTP
198 || (m_cur->type == MUTT_NNTP)
199#endif
200 || stat(mailbox_path(m_cur), &st_cur) != 0)
201 {
202 st_cur.st_dev = 0;
203 st_cur.st_ino = 0;
204 }
205
206 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
208 struct MailboxNode *np = NULL;
209 STAILQ_FOREACH(np, &ml, entries)
210 {
211 struct Mailbox *m = np->mailbox;
212
213 if (!m->visible || !m->poll_new_mail)
214 continue;
215
216 CheckStatsFlags m_flags = flags;
217 if (!m->first_check_stats_done && c_mail_check_stats)
218 {
220 }
221 mailbox_check(m_cur, m, &st_cur, m_flags);
222 if (m->has_new)
223 MailboxCount++;
224 m->first_check_stats_done = true;
225 }
227
228 return MailboxCount;
229}
230
236bool mutt_mailbox_notify(struct Mailbox *m_cur)
237{
239 {
240 return mutt_mailbox_list();
241 }
242 return false;
243}
244
250{
251 char mailboxlist[512] = { 0 };
252 size_t pos = 0;
253 int first = 1;
254
255 int have_unnotified = MailboxNotify;
256
257 struct Buffer *path = buf_pool_get();
258
259 mailboxlist[0] = '\0';
260 pos += strlen(strncat(mailboxlist, _("New mail in "), sizeof(mailboxlist) - 1 - pos));
261 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
263 struct MailboxNode *np = NULL;
264 STAILQ_FOREACH(np, &ml, entries)
265 {
266 /* Is there new mail in this mailbox? */
267 if (!np->mailbox->has_new || (have_unnotified && np->mailbox->notified))
268 continue;
269
270 buf_strcpy(path, mailbox_path(np->mailbox));
271 buf_pretty_mailbox(path);
272
273 if (!first)
274 pos += strlen(strncat(mailboxlist + pos, ", ", sizeof(mailboxlist) - 1 - pos));
275
276 if (!np->mailbox->notified)
277 {
278 np->mailbox->notified = true;
280 }
281 pos += strlen(strncat(mailboxlist + pos, buf_string(path), sizeof(mailboxlist) - 1 - pos));
282 first = 0;
283 }
285
286 buf_pool_release(&path);
287
288 if (!first)
289 {
290 mutt_message("%s", mailboxlist);
291 return true;
292 }
293
294 /* there were no mailboxes needing to be notified, so clean up since
295 * MailboxNotify has somehow gotten out of sync */
296 MailboxNotify = 0;
297 return false;
298}
299
305{
306 if (!m)
307 return;
308
309 m->notified = true;
310#ifdef HAVE_CLOCK_GETTIME
311 clock_gettime(CLOCK_REALTIME, &m->last_visited);
312#else
314 m->last_visited.tv_nsec = 0;
315#endif
316}
317
327static struct Mailbox *find_next_mailbox(struct Buffer *s, bool find_new)
328{
329 bool found = false;
330 for (int pass = 0; pass < 2; pass++)
331 {
332 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
334 struct MailboxNode *np = NULL;
335 STAILQ_FOREACH(np, &ml, entries)
336 {
337 // Match only real mailboxes if looking for new mail.
338 if (find_new && np->mailbox->type == MUTT_NOTMUCH)
339 continue;
340
342 struct Mailbox *m_cur = np->mailbox;
343
344 if ((found || (pass > 0)) && (find_new ? m_cur->has_new : m_cur->msg_unread > 0))
345 {
348 struct Mailbox *m_result = np->mailbox;
350 return m_result;
351 }
353 found = true;
354 }
356 }
357
358 return NULL;
359}
360
370struct Mailbox *mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
371{
373
375 {
376 struct Mailbox *m_res = find_next_mailbox(s, true);
377 if (m_res)
378 return m_res;
379
380 mutt_mailbox_check(m_cur, MUTT_MAILBOX_CHECK_FORCE); /* mailbox was wrong - resync things */
381 }
382
383 buf_reset(s); // no folders with new mail
384 return NULL;
385}
386
396struct Mailbox *mutt_mailbox_next_unread(struct Mailbox *m_cur, struct Buffer *s)
397{
399
400 struct Mailbox *m_res = find_next_mailbox(s, false);
401 if (m_res)
402 return m_res;
403
404 buf_reset(s); // no folders with new mail
405 return NULL;
406}
407
416void mailbox_restore_timestamp(const char *path, struct stat *st)
417{
418#ifdef HAVE_UTIMENSAT
419 struct timespec ts[2] = { { 0 }, { 0 } };
420#else
421 struct utimbuf ut = { 0 };
422#endif
423
424 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
425 if (c_check_mbox_size)
426 {
427 struct Mailbox *m = mailbox_find(path);
428 if (m && !m->has_new)
430 }
431 else
432 {
433 /* fix up the times so mailbox won't get confused */
434 if (st->st_mtime > st->st_atime)
435 {
436#ifdef HAVE_UTIMENSAT
437 ts[0].tv_sec = 0;
438 ts[0].tv_nsec = UTIME_OMIT;
439 ts[1].tv_sec = 0;
440 ts[1].tv_nsec = UTIME_NOW;
441 utimensat(AT_FDCWD, buf, ts, 0);
442#else
443 ut.actime = st->st_atime;
444 ut.modtime = mutt_date_now();
445 utime(path, &ut);
446#endif
447 }
448 else
449 {
450#ifdef HAVE_UTIMENSAT
451 ts[0].tv_sec = 0;
452 ts[0].tv_nsec = UTIME_NOW;
453 ts[1].tv_sec = 0;
454 ts[1].tv_nsec = UTIME_NOW;
455 utimensat(AT_FDCWD, buf, ts, 0);
456#else
457 utime(path, NULL);
458#endif
459 }
460 }
461}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
#define mutt_message(...)
Definition: logging2.h:91
void mailbox_update(struct Mailbox *m)
Get the mailbox's current size.
Definition: mailbox.c:208
struct Mailbox * mailbox_find(const char *path)
Find the mailbox with a given path.
Definition: mailbox.c:143
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
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
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:446
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
static time_t MailboxStatsTime
last time we check performed mail_check_stats
Definition: mutt_mailbox.c:42
static short MailboxCount
how many boxes with new mail
Definition: mutt_mailbox.c:43
static short MailboxNotify
Definition: mutt_mailbox.c:44
int mutt_mailbox_check(struct Mailbox *m_cur, CheckStatsFlags flags)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:165
void mutt_mailbox_set_notified(struct Mailbox *m)
Note when the user was last notified of new mail.
Definition: mutt_mailbox.c:304
struct Mailbox * mutt_mailbox_next_unread(struct Mailbox *m_cur, struct Buffer *s)
Find next mailbox with unread mail.
Definition: mutt_mailbox.c:396
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there's new mail.
Definition: mutt_mailbox.c:236
struct Mailbox * mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
Incoming folders completion routine.
Definition: mutt_mailbox.c:370
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:249
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.
Definition: mutt_mailbox.c:55
static struct Mailbox * find_next_mailbox(struct Buffer *s, bool find_new)
Find the next mailbox with new or unread mail.
Definition: mutt_mailbox.c:327
static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check, struct stat *st_cur, CheckStatsFlags flags)
Check a mailbox for new mail.
Definition: mutt_mailbox.c:80
static time_t MailboxTime
last time we started checking for mail
Definition: mutt_mailbox.c:41
void mailbox_restore_timestamp(const char *path, struct stat *st)
Restore the timestamp of a mailbox.
Definition: mutt_mailbox.c:416
Mailbox helper functions.
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:562
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:335
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:1820
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1343
API for mailboxes.
#define MUTT_MAILBOX_CHECK_NO_FLAGS
No flags are set.
Definition: mxapi.h:53
#define MUTT_MAILBOX_CHECK_FORCE_STATS
Ignore MailboxType and calculate statistics.
Definition: mxapi.h:55
#define MUTT_MAILBOX_CHECK_FORCE
Ignore MailboxTime and check for new mail.
Definition: mxapi.h:54
uint8_t CheckStatsFlags
Flags for mutt_mailbox_check.
Definition: mxapi.h:52
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:162
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:185
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
Postponed Emails.
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition: postpone.c:185
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define TAILQ_EMPTY(head)
Definition: queue.h:721
String manipulation buffer.
Definition: buffer.h:34
List of Mailboxes.
Definition: mailbox.h:153
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:154
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:111
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:114
bool notify_user
Notify the user of new mail.
Definition: mailbox.h:112
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:131
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:41
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:46
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
Time value with nanosecond precision.
Definition: file.h:50
long tv_nsec
Number of nanosecond, on top.
Definition: file.h:52
time_t tv_sec
Number of seconds since the epoch.
Definition: file.h:51