NeoMutt  2022-04-29-145-g9b6a0e
Teaching an old dog new tricks
DOXYGEN
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 "gui/lib.h"
37 #include "mutt_mailbox.h"
38 #include "postpone/lib.h"
39 #include "muttlib.h"
40 #include "mx.h"
41 
42 static time_t MailboxTime = 0;
43 static time_t MailboxStatsTime = 0;
44 static short MailboxCount = 0;
45 static short MailboxNotify = 0;
46 
56 static bool is_same_mailbox(struct Mailbox *m1, struct Mailbox *m2,
57  struct stat *st1, struct stat *st2)
58 {
59  if (!m1 || mutt_buffer_is_empty(&m1->pathbuf) || !m2 ||
60  mutt_buffer_is_empty(&m2->pathbuf) || (m1->type != m2->type))
61  {
62  return false;
63  }
64 
65  const bool uses_protocol = (m2->type == MUTT_IMAP) || (m2->type == MUTT_NNTP) ||
66  (m2->type == MUTT_NOTMUCH) || (m2->type == MUTT_POP);
67 
68  if (uses_protocol)
69  return mutt_str_equal(mailbox_path(m1), mailbox_path(m2));
70  else
71  return ((st1->st_dev == st2->st_dev) && (st1->st_ino == st2->st_ino));
72 }
73 
81 static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check,
82  struct stat *st_ctx, CheckStatsFlags flags)
83 {
84  struct stat st = { 0 };
85 
86  enum MailboxType mb_type = mx_path_probe(mailbox_path(m_check));
87 
88  const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
89  if ((m_cur == m_check) && c_mail_check_recent)
90  m_check->has_new = false;
91 
92  switch (mb_type)
93  {
94  case MUTT_POP:
95  case MUTT_NNTP:
96  case MUTT_NOTMUCH:
97  case MUTT_IMAP:
98  m_check->type = mb_type;
99  break;
100  default:
101  if ((stat(mailbox_path(m_check), &st) != 0) ||
102  ((m_check->type == MUTT_UNKNOWN) && S_ISREG(st.st_mode) && (st.st_size == 0)) ||
103  ((m_check->type == MUTT_UNKNOWN) &&
104  ((m_check->type = mx_path_probe(mailbox_path(m_check))) <= 0)))
105  {
106  /* if the mailbox still doesn't exist, set the newly created flag to be
107  * ready for when it does. */
108  m_check->newly_created = true;
109  m_check->type = MUTT_UNKNOWN;
110  m_check->size = 0;
111  return;
112  }
113  break; // kept for consistency.
114  }
115 
116  const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
117 
118  /* check to see if the folder is the currently selected folder before polling */
119  if (!is_same_mailbox(m_cur, m_check, st_ctx, &st))
120  {
121  switch (m_check->type)
122  {
123  case MUTT_NOTMUCH:
124  // Remove this when non-notmuch backends only check unread, flagged,
125  // and total counts per 'mbox_check_stats' docs.
126  if ((flags & MUTT_MAILBOX_CHECK_FORCE_STATS) == 0)
127  break;
128  /* fall through */
129  case MUTT_IMAP:
130  case MUTT_MBOX:
131  case MUTT_MMDF:
132  case MUTT_MAILDIR:
133  case MUTT_MH:
134  mx_mbox_check_stats(m_check, flags);
135  break;
136  default:; /* do nothing */
137  }
138  }
139  else if (c_check_mbox_size && m_cur && mutt_buffer_is_empty(&m_cur->pathbuf))
140  m_check->size = (off_t) st.st_size; /* update the size of current folder */
141 
142  if (!m_check->has_new)
143  m_check->notified = false;
144  else if (!m_check->notified)
145  MailboxNotify++;
146 }
147 
156 int mutt_mailbox_check(struct Mailbox *m_cur, CheckStatsFlags flags)
157 {
158  struct stat st_ctx = { 0 };
159  time_t t;
160  st_ctx.st_dev = 0;
161  st_ctx.st_ino = 0;
162 
163 #ifdef USE_IMAP
164  if (flags & MUTT_MAILBOX_CHECK_FORCE)
166 #endif
167 
168  /* fastest return if there are no mailboxes */
170  return 0;
171 
172  const short c_mail_check = cs_subset_number(NeoMutt->sub, "mail_check");
173  const bool c_mail_check_stats = cs_subset_bool(NeoMutt->sub, "mail_check_stats");
174  const short c_mail_check_stats_interval = cs_subset_number(NeoMutt->sub, "mail_check_stats_interval");
175 
176  t = mutt_date_epoch();
177  if ((flags == MUTT_MAILBOX_CHECK_NO_FLAGS) && (t - MailboxTime < c_mail_check))
178  return MailboxCount;
179 
180  if ((flags & MUTT_MAILBOX_CHECK_FORCE_STATS) ||
181  (c_mail_check_stats && ((t - MailboxStatsTime) >= c_mail_check_stats_interval)))
182  {
184  MailboxStatsTime = t;
185  }
186 
187  MailboxTime = t;
188  MailboxCount = 0;
189  MailboxNotify = 0;
190 
191  /* check device ID and serial number instead of comparing paths */
192  if (!m_cur || (m_cur->type == MUTT_IMAP) || (m_cur->type == MUTT_POP)
193 #ifdef USE_NNTP
194  || (m_cur->type == MUTT_NNTP)
195 #endif
196  || stat(mailbox_path(m_cur), &st_ctx) != 0)
197  {
198  st_ctx.st_dev = 0;
199  st_ctx.st_ino = 0;
200  }
201 
202  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
204  struct MailboxNode *np = NULL;
205  STAILQ_FOREACH(np, &ml, entries)
206  {
207  if (!np->mailbox->visible)
208  continue;
209 
210  CheckStatsFlags m_flags = flags;
211  if (!np->mailbox->first_check_stats_done && c_mail_check_stats)
212  {
214  }
215  mailbox_check(m_cur, np->mailbox, &st_ctx, m_flags);
216  if (np->mailbox->has_new)
217  MailboxCount++;
218  np->mailbox->first_check_stats_done = true;
219  }
221 
222  return MailboxCount;
223 }
224 
230 bool mutt_mailbox_notify(struct Mailbox *m_cur)
231 {
233  {
234  return mutt_mailbox_list();
235  }
236  return false;
237 }
238 
244 {
245  char mailboxlist[512];
246  size_t pos = 0;
247  int first = 1;
248 
249  int have_unnotified = MailboxNotify;
250 
251  struct Buffer *path = mutt_buffer_pool_get();
252 
253  mailboxlist[0] = '\0';
254  pos += strlen(strncat(mailboxlist, _("New mail in "), sizeof(mailboxlist) - 1 - pos));
255  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
257  struct MailboxNode *np = NULL;
258  STAILQ_FOREACH(np, &ml, entries)
259  {
260  /* Is there new mail in this mailbox? */
261  if (!np->mailbox->has_new || (have_unnotified && np->mailbox->notified))
262  continue;
263 
266 
267  const size_t width = msgwin_get_width();
268  if (!first && (width >= 7) && ((pos + mutt_buffer_len(path)) >= (width - 7)))
269  {
270  break;
271  }
272 
273  if (!first)
274  pos += strlen(strncat(mailboxlist + pos, ", ", sizeof(mailboxlist) - 1 - pos));
275 
276  /* Prepend an asterisk to mailboxes not already notified */
277  if (!np->mailbox->notified)
278  {
279  /* pos += strlen (strncat(mailboxlist + pos, "*", sizeof(mailboxlist)-1-pos)); */
280  np->mailbox->notified = true;
281  MailboxNotify--;
282  }
283  pos += strlen(strncat(mailboxlist + pos, mutt_buffer_string(path),
284  sizeof(mailboxlist) - 1 - pos));
285  first = 0;
286  }
288 
289  if (!first && np)
290  {
291  strncat(mailboxlist + pos, ", ...", sizeof(mailboxlist) - 1 - pos);
292  }
293 
295 
296  if (!first)
297  {
298  mutt_message("%s", mailboxlist);
299  return true;
300  }
301 
302  /* there were no mailboxes needing to be notified, so clean up since
303  * MailboxNotify has somehow gotten out of sync */
304  MailboxNotify = 0;
305  return false;
306 }
307 
313 {
314  if (!m)
315  return;
316 
317  m->notified = true;
318 #ifdef HAVE_CLOCK_GETTIME
319  clock_gettime(CLOCK_REALTIME, &m->last_visited);
320 #else
322  m->last_visited.tv_nsec = 0;
323 #endif
324 }
325 
335 static struct Mailbox *find_next_mailbox(struct Buffer *s, bool find_new)
336 {
337  bool found = false;
338  for (int pass = 0; pass < 2; pass++)
339  {
340  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
342  struct MailboxNode *np = NULL;
343  STAILQ_FOREACH(np, &ml, entries)
344  {
345  // Match only real mailboxes if looking for new mail.
346  if (find_new && np->mailbox->type == MUTT_NOTMUCH)
347  continue;
348 
350  struct Mailbox *m_cur = np->mailbox;
351 
352  if ((found || (pass > 0)) && (find_new ? m_cur->has_new : m_cur->msg_unread > 0))
353  {
356  struct Mailbox *m_result = np->mailbox;
358  return m_result;
359  }
361  found = true;
362  }
364  }
365 
366  return NULL;
367 }
368 
378 struct Mailbox *mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
379 {
381 
383  {
384  struct Mailbox *m_res = find_next_mailbox(s, true);
385  if (m_res)
386  return m_res;
387 
388  mutt_mailbox_check(m_cur, MUTT_MAILBOX_CHECK_FORCE); /* mailbox was wrong - resync things */
389  }
390 
391  mutt_buffer_reset(s); // no folders with new mail
392  return NULL;
393 }
394 
404 struct Mailbox *mutt_mailbox_next_unread(struct Mailbox *m_cur, struct Buffer *s)
405 {
407 
408  struct Mailbox *m_res = find_next_mailbox(s, false);
409  if (m_res)
410  return m_res;
411 
412  mutt_buffer_reset(s); // no folders with new mail
413  return NULL;
414 }
415 
424 void mutt_mailbox_cleanup(const char *path, struct stat *st)
425 {
426 #ifdef HAVE_UTIMENSAT
427  struct timespec ts[2];
428 #else
429  struct utimbuf ut;
430 #endif
431 
432  const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
433  if (c_check_mbox_size)
434  {
435  struct Mailbox *m = mailbox_find(path);
436  if (m && !m->has_new)
437  mailbox_update(m);
438  }
439  else
440  {
441  /* fix up the times so mailbox won't get confused */
442  if (st->st_mtime > st->st_atime)
443  {
444 #ifdef HAVE_UTIMENSAT
445  ts[0].tv_sec = 0;
446  ts[0].tv_nsec = UTIME_OMIT;
447  ts[1].tv_sec = 0;
448  ts[1].tv_nsec = UTIME_NOW;
449  utimensat(AT_FDCWD, buf, ts, 0);
450 #else
451  ut.actime = st->st_atime;
452  ut.modtime = mutt_date_epoch();
453  utime(path, &ut);
454 #endif
455  }
456  else
457  {
458 #ifdef HAVE_UTIMENSAT
459  ts[0].tv_sec = 0;
460  ts[0].tv_nsec = UTIME_NOW;
461  ts[1].tv_sec = 0;
462  ts[1].tv_nsec = UTIME_NOW;
463  utimensat(AT_FDCWD, buf, ts, 0);
464 #else
465  utime(path, NULL);
466 #endif
467  }
468  }
469 }
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:250
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:354
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:81
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
#define mutt_message(...)
Definition: logging.h:86
Convenience wrapper for the gui headers.
void mailbox_update(struct Mailbox *m)
Get the mailbox's current size.
Definition: mailbox.c:204
struct Mailbox * mailbox_find(const char *path)
Find the mailbox with a given path.
Definition: mailbox.c:139
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:211
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
size_t msgwin_get_width(void)
Get the width of the Message Window.
Definition: msgwin.c:267
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:784
static time_t MailboxStatsTime
last time we check performed mail_check_stats
Definition: mutt_mailbox.c:43
static short MailboxCount
how many boxes with new mail
Definition: mutt_mailbox.c:44
static short MailboxNotify
Definition: mutt_mailbox.c:45
int mutt_mailbox_check(struct Mailbox *m_cur, CheckStatsFlags flags)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:156
struct Mailbox * mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
Incoming folders completion routine.
Definition: mutt_mailbox.c:378
static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check, struct stat *st_ctx, CheckStatsFlags flags)
Check a mailbox for new mail.
Definition: mutt_mailbox.c:81
void mutt_mailbox_set_notified(struct Mailbox *m)
Note when the user was last notified of new mail.
Definition: mutt_mailbox.c:312
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:335
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there's new mail.
Definition: mutt_mailbox.c:230
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:243
void mutt_mailbox_cleanup(const char *path, struct stat *st)
Restore the timestamp of a mailbox.
Definition: mutt_mailbox.c:424
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:56
struct Mailbox * mutt_mailbox_next_unread(struct Mailbox *m_cur, struct Buffer *s)
Find next mailbox with unread mail.
Definition: mutt_mailbox.c:404
static time_t MailboxTime
last time we started checking for mail
Definition: mutt_mailbox.c:42
Mailbox helper functions.
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:599
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
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:1800
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1327
API for mailboxes.
#define MUTT_MAILBOX_CHECK_NO_FLAGS
No flags are set.
Definition: mxapi.h:74
#define MUTT_MAILBOX_CHECK_FORCE_STATS
Ignore MailboxType and calculate statistics.
Definition: mxapi.h:76
#define MUTT_MAILBOX_CHECK_FORCE
Ignore MailboxTime and check for new mail.
Definition: mxapi.h:75
uint8_t CheckStatsFlags
Flags for mutt_mailbox_check.
Definition: mxapi.h:73
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:141
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:164
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
Postponed Emails.
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition: postpone.c:183
#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:154
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:155
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 on time.
Definition: mailbox.h:113
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
bool newly_created
Mbox or mmdf just popped into existence.
Definition: mailbox.h:103
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:105
int msg_unread
Number of unread messages.
Definition: mailbox.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:40
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Time value with nanosecond precision.
Definition: file.h:49
long tv_nsec
Number of nanosecond, on top.
Definition: file.h:51
time_t tv_sec
Number of seconds since the epoch.
Definition: file.h:50