NeoMutt  2020-11-20
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 <sys/time.h>
32 #include <time.h>
33 #include <utime.h>
34 #include "mutt/lib.h"
35 #include "core/lib.h"
36 #include "gui/lib.h"
37 #include "mutt_mailbox.h"
38 #include "mbox/lib.h"
39 #include "mutt_globals.h"
40 #include "mutt_menu.h"
41 #include "muttlib.h"
42 #include "mx.h"
43 #include "protos.h"
44 
45 static time_t MailboxTime = 0;
46 static time_t MailboxStatsTime = 0;
47 static short MailboxCount = 0;
48 static short MailboxNotify = 0;
49 
50 /* These Config Variables are only used in mutt_mailbox.c */
51 short C_MailCheck;
54 
62 static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check,
63  struct stat *ctx_sb, bool check_stats)
64 {
65  struct stat sb = { 0 };
66 
67  enum MailboxType mb_type = mx_path_probe(mailbox_path(m_check));
68 
69  if ((m_cur == m_check) && C_MailCheckRecent)
70  m_check->has_new = false;
71 
72  switch (mb_type)
73  {
74  case MUTT_POP:
75  case MUTT_NNTP:
76  case MUTT_NOTMUCH:
77  case MUTT_IMAP:
78  m_check->type = mb_type;
79  break;
80  default:
81  if ((stat(mailbox_path(m_check), &sb) != 0) ||
82  ((m_check->type == MUTT_UNKNOWN) && S_ISREG(sb.st_mode) && (sb.st_size == 0)) ||
83  ((m_check->type == MUTT_UNKNOWN) &&
84  ((m_check->type = mx_path_probe(mailbox_path(m_check))) <= 0)))
85  {
86  /* if the mailbox still doesn't exist, set the newly created flag to be
87  * ready for when it does. */
88  m_check->newly_created = true;
89  m_check->type = MUTT_UNKNOWN;
90  m_check->size = 0;
91  return;
92  }
93  break; // kept for consistency.
94  }
95 
96  /* check to see if the folder is the currently selected folder before polling */
97  if (!m_cur || mutt_buffer_is_empty(&m_cur->pathbuf) ||
98  (((m_check->type == MUTT_IMAP) || (m_check->type == MUTT_NNTP) ||
99  (m_check->type == MUTT_NOTMUCH) || (m_check->type == MUTT_POP)) ?
100  !mutt_str_equal(mailbox_path(m_check), mailbox_path(m_cur)) :
101  ((sb.st_dev != ctx_sb->st_dev) || (sb.st_ino != ctx_sb->st_ino))))
102  {
103  switch (m_check->type)
104  {
105  case MUTT_IMAP:
106  case MUTT_MBOX:
107  case MUTT_MMDF:
108  case MUTT_MAILDIR:
109  case MUTT_MH:
110  case MUTT_NOTMUCH:
111  if ((mx_mbox_check_stats(m_check, check_stats) > 0) && m_check->has_new)
112  MailboxCount++;
113  break;
114  default:; /* do nothing */
115  }
116  }
117  else if (C_CheckMboxSize && m_cur && mutt_buffer_is_empty(&m_cur->pathbuf))
118  m_check->size = (off_t) sb.st_size; /* update the size of current folder */
119 
120  if (!m_check->has_new)
121  m_check->notified = false;
122  else if (!m_check->notified)
123  MailboxNotify++;
124 }
125 
138 int mutt_mailbox_check(struct Mailbox *m_cur, int force)
139 {
140  struct stat contex_sb;
141  time_t t;
142  bool check_stats = false;
143  contex_sb.st_dev = 0;
144  contex_sb.st_ino = 0;
145 
146 #ifdef USE_IMAP
147  /* update postponed count as well, on force */
148  if (force & MUTT_MAILBOX_CHECK_FORCE)
150 #endif
151 
152  /* fastest return if there are no mailboxes */
154  return 0;
155 
156  t = mutt_date_epoch();
157  if (!force && (t - MailboxTime < C_MailCheck))
158  return MailboxCount;
159 
160  if ((force & MUTT_MAILBOX_CHECK_FORCE_STATS) ||
162  {
163  check_stats = true;
164  MailboxStatsTime = t;
165  }
166 
167  MailboxTime = t;
168  MailboxCount = 0;
169  MailboxNotify = 0;
170 
171  /* check device ID and serial number instead of comparing paths */
172  if (!m_cur || (m_cur->type == MUTT_IMAP) || (m_cur->type == MUTT_POP)
173 #ifdef USE_NNTP
174  || (m_cur->type == MUTT_NNTP)
175 #endif
176  || stat(mailbox_path(m_cur), &contex_sb) != 0)
177  {
178  contex_sb.st_dev = 0;
179  contex_sb.st_ino = 0;
180  }
181 
182  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
184  struct MailboxNode *np = NULL;
185  STAILQ_FOREACH(np, &ml, entries)
186  {
187  if (np->mailbox->flags & MB_HIDDEN)
188  continue;
189 
190  mailbox_check(m_cur, np->mailbox, &contex_sb,
191  check_stats || (!np->mailbox->first_check_stats_done && C_MailCheckStats));
192  np->mailbox->first_check_stats_done = true;
193  }
195 
196  return MailboxCount;
197 }
198 
204 bool mutt_mailbox_notify(struct Mailbox *m_cur)
205 {
206  if ((mutt_mailbox_check(m_cur, 0) > 0) && MailboxNotify)
207  {
208  return mutt_mailbox_list();
209  }
210  return false;
211 }
212 
218 {
219  char mailboxlist[512];
220  size_t pos = 0;
221  int first = 1;
222 
223  int have_unnotified = MailboxNotify;
224 
225  struct Buffer *path = mutt_buffer_pool_get();
226 
227  mailboxlist[0] = '\0';
228  pos += strlen(strncat(mailboxlist, _("New mail in "), sizeof(mailboxlist) - 1 - pos));
229  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
231  struct MailboxNode *np = NULL;
232  STAILQ_FOREACH(np, &ml, entries)
233  {
234  /* Is there new mail in this mailbox? */
235  if (!np->mailbox->has_new || (have_unnotified && np->mailbox->notified))
236  continue;
237 
240 
241  if (!first && (MessageWindow->state.cols >= 7) &&
242  ((pos + mutt_buffer_len(path)) >= ((size_t) MessageWindow->state.cols - 7)))
243  {
244  break;
245  }
246 
247  if (!first)
248  pos += strlen(strncat(mailboxlist + pos, ", ", sizeof(mailboxlist) - 1 - pos));
249 
250  /* Prepend an asterisk to mailboxes not already notified */
251  if (!np->mailbox->notified)
252  {
253  /* pos += strlen (strncat(mailboxlist + pos, "*", sizeof(mailboxlist)-1-pos)); */
254  np->mailbox->notified = true;
255  MailboxNotify--;
256  }
257  pos += strlen(strncat(mailboxlist + pos, mutt_b2s(path), sizeof(mailboxlist) - 1 - pos));
258  first = 0;
259  }
261 
262  if (!first && np)
263  {
264  strncat(mailboxlist + pos, ", ...", sizeof(mailboxlist) - 1 - pos);
265  }
266 
268 
269  if (!first)
270  {
271  mutt_message("%s", mailboxlist);
272  return true;
273  }
274 
275  /* there were no mailboxes needing to be notified, so clean up since
276  * MailboxNotify has somehow gotten out of sync */
277  MailboxNotify = 0;
278  return false;
279 }
280 
286 {
287  if (!m)
288  return;
289 
290  m->notified = true;
291 #ifdef HAVE_CLOCK_GETTIME
292  clock_gettime(CLOCK_REALTIME, &m->last_visited);
293 #else
295  m->last_visited.tv_nsec = 0;
296 #endif
297 }
298 
308 struct Mailbox *mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
309 {
311 
312  if (mutt_mailbox_check(m_cur, 0) > 0)
313  {
314  bool found = false;
315  for (int pass = 0; pass < 2; pass++)
316  {
317  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
319  struct MailboxNode *np = NULL;
320  STAILQ_FOREACH(np, &ml, entries)
321  {
322  if (np->mailbox->type == MUTT_NOTMUCH) /* only match real mailboxes */
323  continue;
325  if ((found || (pass > 0)) && np->mailbox->has_new)
326  {
329  struct Mailbox *m_result = np->mailbox;
331  return m_result;
332  }
334  found = true;
335  }
337  }
338 
339  mutt_mailbox_check(m_cur, MUTT_MAILBOX_CHECK_FORCE); /* mailbox was wrong - resync things */
340  }
341 
342  mutt_buffer_reset(s); // no folders with new mail
343  return NULL;
344 }
345 
354 void mutt_mailbox_cleanup(const char *path, struct stat *st)
355 {
356 #ifdef HAVE_UTIMENSAT
357  struct timespec ts[2];
358 #else
359  struct utimbuf ut;
360 #endif
361 
362  if (C_CheckMboxSize)
363  {
364  struct Mailbox *m = mailbox_find(path);
365  if (m && !m->has_new)
366  mailbox_update(m);
367  }
368  else
369  {
370  /* fix up the times so mailbox won't get confused */
371  if (st->st_mtime > st->st_atime)
372  {
373 #ifdef HAVE_UTIMENSAT
374  ts[0].tv_sec = 0;
375  ts[0].tv_nsec = UTIME_OMIT;
376  ts[1].tv_sec = 0;
377  ts[1].tv_nsec = UTIME_NOW;
378  utimensat(AT_FDCWD, buf, ts, 0);
379 #else
380  ut.actime = st->st_atime;
381  ut.modtime = mutt_date_epoch();
382  utime(path, &ut);
383 #endif
384  }
385  else
386  {
387 #ifdef HAVE_UTIMENSAT
388  ts[0].tv_sec = 0;
389  ts[0].tv_nsec = UTIME_NOW;
390  ts[1].tv_sec = 0;
391  ts[1].tv_nsec = UTIME_NOW;
392  utimensat(AT_FDCWD, buf, ts, 0);
393 #else
394  utime(path, NULL);
395 #endif
396  }
397  }
398 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
Convenience wrapper for the gui headers.
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:416
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:203
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:217
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
off_t size
Size of the Mailbox.
Definition: mailbox.h:87
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
void mutt_mailbox_cleanup(const char *path, struct stat *st)
Restore the timestamp of a mailbox.
Definition: mutt_mailbox.c:354
#define mutt_message(...)
Definition: logging.h:83
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:40
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:137
struct Mailbox * mailbox_find(const char *path)
Find the mailbox with a given path.
Definition: mailbox.c:94
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
Match any Mailbox type.
Definition: mailbox.h:45
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:160
String manipulation buffer.
Definition: buffer.h:33
bool first_check_stats_done
True when the check have been done at least on time.
Definition: mailbox.h:116
static time_t MailboxStatsTime
last time we check performed mail_check_stats
Definition: mutt_mailbox.c:46
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
struct Mailbox * mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
incoming folders completion routine
Definition: mutt_mailbox.c:308
static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check, struct stat *ctx_sb, bool check_stats)
Check a mailbox for new mail.
Definition: mutt_mailbox.c:62
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:598
time_t tv_sec
Definition: file.h:48
Some miscellaneous functions.
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:108
API for mailboxes.
Convenience wrapper for the core headers.
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there&#39;s new mail.
Definition: mutt_mailbox.c:204
static short MailboxCount
how many boxes with new mail
Definition: mutt_mailbox.c:47
long tv_nsec
Definition: file.h:49
&#39;Maildir&#39; Mailbox type
Definition: mailbox.h:51
int flags
e.g. MB_NORMAL
Definition: mailbox.h:134
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:57
#define mutt_b2s(buf)
Definition: buffer.h:41
Prototypes for many functions.
static short MailboxNotify
of unnotified new boxes
Definition: mutt_mailbox.c:48
short C_MailCheckStatsInterval
Config: How often to check for new mail.
Definition: mutt_mailbox.c:53
#define MB_HIDDEN
Definition: mailbox.h:38
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:120
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
struct MuttWindow * MessageWindow
Message Window, ":set", etc.
Definition: mutt_window.c:47
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
A mailbox.
Definition: mailbox.h:81
int mutt_mailbox_check(struct Mailbox *m_cur, int force)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:138
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:49
static time_t MailboxTime
last time we started checking for mail
Definition: mutt_mailbox.c:45
GUI present the user with a selectable list.
&#39;MH&#39; Mailbox type
Definition: mailbox.h:50
bool C_CheckMboxSize
Config: (mbox,mmdf) Use mailbox size as an indicator of new mail.
Definition: config.c:36
short C_MailCheck
Config: Number of seconds before NeoMutt checks for new mail.
Definition: mutt_mailbox.c:51
WHERE bool C_MailCheckRecent
Config: Notify the user about new mail since the last time the mailbox was opened.
Definition: mutt_globals.h:150
void mailbox_update(struct Mailbox *m)
Get the mailbox&#39;s current size.
Definition: mailbox.c:159
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition: postpone.c:202
bool newly_created
Mbox or mmdf just popped into existence.
Definition: mailbox.h:106
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
#define MUTT_MAILBOX_CHECK_FORCE
Definition: mutt_mailbox.h:16
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
&#39;mbox&#39; Mailbox type
Definition: mailbox.h:48
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1335
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:323
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
int mx_mbox_check_stats(struct Mailbox *m, int flags)
Check the statistics for a mailbox - Wrapper for MxOps::mbox_check_stats()
Definition: mx.c:1802
bool notified
User has been notified.
Definition: mailbox.h:104
Time value with nanosecond precision.
Definition: file.h:46
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
List of Mailboxes.
Definition: mailbox.h:152
Hundreds of global variables to back the user variables.
void mutt_mailbox_set_notified(struct Mailbox *m)
Note when the user was last notified of new mail.
Definition: mutt_mailbox.c:285
bool C_MailCheckStats
Config: Periodically check for new mail.
Definition: mutt_mailbox.c:52
#define TAILQ_EMPTY(head)
Definition: queue.h:714
struct Buffer pathbuf
Definition: mailbox.h:83
Convenience wrapper for the library headers.
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:154
#define MUTT_MAILBOX_CHECK_FORCE_STATS
Definition: mutt_mailbox.h:17