]> granicus.if.org Git - neomutt/blob - mutt_mailbox.c
Convert mutt_attach_reply() to use buffer pool
[neomutt] / mutt_mailbox.c
1 #include "config.h"
2 #include <string.h>
3 #include <sys/stat.h>
4 #include <sys/time.h>
5 #include <time.h>
6 #include <utime.h>
7 #include "mutt/mutt.h"
8 #include "core/lib.h"
9 #include "mutt_mailbox.h"
10 #include "globals.h"
11 #include "mutt_menu.h"
12 #include "mutt_window.h"
13 #include "muttlib.h"
14 #include "mx.h"
15 #include "protos.h"
16
17 static time_t MailboxTime = 0; /**< last time we started checking for mail */
18 static time_t MailboxStatsTime = 0; /**< last time we check performed mail_check_stats */
19 static short MailboxCount = 0;  /**< how many boxes with new mail */
20 static short MailboxNotify = 0; /**< # of unnotified new boxes */
21
22 /* These Config Variables are only used in mutt_mailbox.c */
23 short C_MailCheck; ///< Config: Number of seconds before NeoMutt checks for new mail
24 bool C_MailCheckStats;          ///< Config: Periodically check for new mail
25 short C_MailCheckStatsInterval; ///< Config: How often to check for new mail
26
27 /**
28  * mailbox_check - Check a mailbox for new mail
29  * @param m_cur       Current Mailbox
30  * @param m_check     Mailbox to check
31  * @param ctx_sb      stat() info for the current Mailbox
32  * @param check_stats If true, also count the total, new and flagged messages
33  */
34 static void mailbox_check(struct Mailbox *m_cur, struct Mailbox *m_check,
35                           struct stat *ctx_sb, bool check_stats)
36 {
37   struct stat sb = { 0 };
38
39 #ifdef USE_SIDEBAR
40   short orig_new = m_check->has_new;
41   int orig_count = m_check->msg_count;
42   int orig_unread = m_check->msg_unread;
43   int orig_flagged = m_check->msg_flagged;
44 #endif
45
46   enum MailboxType mb_magic = mx_path_probe(mailbox_path(m_check), NULL);
47
48   switch (mb_magic)
49   {
50     case MUTT_POP:
51     case MUTT_NNTP:
52     case MUTT_NOTMUCH:
53     case MUTT_IMAP:
54       if (mb_magic != MUTT_IMAP)
55         m_check->has_new = false;
56       m_check->magic = mb_magic;
57       break;
58     default:
59       if (m_cur == m_check)
60         m_check->has_new = false;
61
62       if ((stat(mailbox_path(m_check), &sb) != 0) ||
63           (S_ISREG(sb.st_mode) && (sb.st_size == 0)) ||
64           ((m_check->magic == MUTT_UNKNOWN) &&
65            ((m_check->magic = mx_path_probe(mailbox_path(m_check), NULL)) <= 0)))
66       {
67         /* if the mailbox still doesn't exist, set the newly created flag to be
68          * ready for when it does. */
69         m_check->newly_created = true;
70         m_check->magic = MUTT_UNKNOWN;
71         m_check->size = 0;
72         return;
73       }
74       break; // kept for consistency.
75   }
76
77   /* check to see if the folder is the currently selected folder before polling */
78   if (!m_cur || mutt_buffer_is_empty(&m_cur->pathbuf) ||
79       (((m_check->magic == MUTT_IMAP) || (m_check->magic == MUTT_NNTP) ||
80         (m_check->magic == MUTT_NOTMUCH) || (m_check->magic == MUTT_POP)) ?
81            (mutt_str_strcmp(mailbox_path(m_check), mailbox_path(m_cur)) != 0) :
82            ((sb.st_dev != ctx_sb->st_dev) || (sb.st_ino != ctx_sb->st_ino))))
83   {
84     switch (m_check->magic)
85     {
86       case MUTT_IMAP:
87       case MUTT_MBOX:
88       case MUTT_MMDF:
89       case MUTT_MAILDIR:
90       case MUTT_MH:
91       case MUTT_NOTMUCH:
92         if ((mx_mbox_check_stats(m_check, check_stats) > 0) && m_check->has_new)
93           MailboxCount++;
94         break;
95       default:; /* do nothing */
96     }
97   }
98   else if (C_CheckMboxSize && m_cur && mutt_buffer_is_empty(&m_cur->pathbuf))
99     m_check->size = (off_t) sb.st_size; /* update the size of current folder */
100
101 #ifdef USE_SIDEBAR
102   if ((orig_new != m_check->has_new) || (orig_count != m_check->msg_count) ||
103       (orig_unread != m_check->msg_unread) || (orig_flagged != m_check->msg_flagged))
104   {
105     mutt_menu_set_current_redraw(REDRAW_SIDEBAR);
106   }
107 #endif
108
109   if (!m_check->has_new)
110     m_check->notified = false;
111   else if (!m_check->notified)
112     MailboxNotify++;
113 }
114
115 /**
116  * mutt_mailbox_check - Check all all Mailboxes for new mail
117  * @param m_cur Current Mailbox
118  * @param force Force flags, see below
119  * @retval num Number of mailboxes with new mail
120  *
121  * The force argument may be any combination of the following values:
122  * - MUTT_MAILBOX_CHECK_FORCE        ignore MailboxTime and check for new mail
123  * - MUTT_MAILBOX_CHECK_FORCE_STATS  ignore MailboxTime and calculate statistics
124  *
125  * Check all all Mailboxes for new mail and total/new/flagged messages
126  */
127 int mutt_mailbox_check(struct Mailbox *m_cur, int force)
128 {
129   struct stat contex_sb;
130   time_t t;
131   bool check_stats = false;
132   contex_sb.st_dev = 0;
133   contex_sb.st_ino = 0;
134
135 #ifdef USE_IMAP
136   /* update postponed count as well, on force */
137   if (force & MUTT_MAILBOX_CHECK_FORCE)
138     mutt_update_num_postponed();
139 #endif
140
141   /* fastest return if there are no mailboxes */
142   if (TAILQ_EMPTY(&NeoMutt->accounts))
143     return 0;
144
145   t = mutt_date_epoch();
146   if (!force && (t - MailboxTime < C_MailCheck))
147     return MailboxCount;
148
149   if ((force & MUTT_MAILBOX_CHECK_FORCE_STATS) ||
150       (C_MailCheckStats && ((t - MailboxStatsTime) >= C_MailCheckStatsInterval)))
151   {
152     check_stats = true;
153     MailboxStatsTime = t;
154   }
155
156   MailboxTime = t;
157   MailboxCount = 0;
158   MailboxNotify = 0;
159
160   /* check device ID and serial number instead of comparing paths */
161   if (!m_cur || (m_cur->magic == MUTT_IMAP) || (m_cur->magic == MUTT_POP)
162 #ifdef USE_NNTP
163       || (m_cur->magic == MUTT_NNTP)
164 #endif
165       || stat(mailbox_path(m_cur), &contex_sb) != 0)
166   {
167     contex_sb.st_dev = 0;
168     contex_sb.st_ino = 0;
169   }
170
171   struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
172   struct MailboxNode *np = NULL;
173   STAILQ_FOREACH(np, &ml, entries)
174   {
175     mailbox_check(m_cur, np->mailbox, &contex_sb,
176                   check_stats || (!np->mailbox->first_check_stats_done && C_MailCheckStats));
177     np->mailbox->first_check_stats_done = true;
178   }
179   neomutt_mailboxlist_clear(&ml);
180
181   return MailboxCount;
182 }
183
184 /**
185  * mutt_mailbox_notify - Notify the user if there's new mail
186  * @param m_cur Current Mailbox
187  * @retval true If there is new mail
188  */
189 bool mutt_mailbox_notify(struct Mailbox *m_cur)
190 {
191   if ((mutt_mailbox_check(m_cur, 0) > 0) && MailboxNotify)
192   {
193     return mutt_mailbox_list();
194   }
195   return false;
196 }
197
198 /**
199  * mutt_mailbox_list - List the mailboxes with new mail
200  * @retval true If there is new mail
201  */
202 bool mutt_mailbox_list(void)
203 {
204   char mailboxlist[512];
205   size_t pos = 0;
206   int first = 1;
207
208   int have_unnotified = MailboxNotify;
209
210   struct Buffer *path = mutt_buffer_pool_get();
211
212   mailboxlist[0] = '\0';
213   pos += strlen(strncat(mailboxlist, _("New mail in "), sizeof(mailboxlist) - 1 - pos));
214   struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
215   struct MailboxNode *np = NULL;
216   STAILQ_FOREACH(np, &ml, entries)
217   {
218     /* Is there new mail in this mailbox? */
219     if (!np->mailbox->has_new || (have_unnotified && np->mailbox->notified))
220       continue;
221
222     mutt_buffer_strcpy(path, mailbox_path(np->mailbox));
223     mutt_buffer_pretty_mailbox(path);
224
225     if (!first && (MuttMessageWindow->cols >= 7) &&
226         ((pos + mutt_buffer_len(path)) >= ((size_t) MuttMessageWindow->cols - 7)))
227     {
228       break;
229     }
230
231     if (!first)
232       pos += strlen(strncat(mailboxlist + pos, ", ", sizeof(mailboxlist) - 1 - pos));
233
234     /* Prepend an asterisk to mailboxes not already notified */
235     if (!np->mailbox->notified)
236     {
237       /* pos += strlen (strncat(mailboxlist + pos, "*", sizeof(mailboxlist)-1-pos)); */
238       np->mailbox->notified = true;
239       MailboxNotify--;
240     }
241     pos += strlen(strncat(mailboxlist + pos, mutt_b2s(path), sizeof(mailboxlist) - 1 - pos));
242     first = 0;
243   }
244   neomutt_mailboxlist_clear(&ml);
245
246   if (!first && np)
247   {
248     strncat(mailboxlist + pos, ", ...", sizeof(mailboxlist) - 1 - pos);
249   }
250
251   mutt_buffer_pool_release(&path);
252
253   if (!first)
254   {
255     mutt_message("%s", mailboxlist);
256     return true;
257   }
258   else
259   {
260     /* there were no mailboxes needing to be notified, so clean up since
261      * MailboxNotify has somehow gotten out of sync */
262     MailboxNotify = 0;
263     return false;
264   }
265 }
266
267 /**
268  * mutt_mailbox_set_notified - Note when the user was last notified of new mail
269  * @param m Mailbox
270  */
271 void mutt_mailbox_set_notified(struct Mailbox *m)
272 {
273   if (!m)
274     return;
275
276   m->notified = true;
277 #if HAVE_CLOCK_GETTIME
278   clock_gettime(CLOCK_REALTIME, &m->last_visited);
279 #else
280   m->last_visited.tv_sec = mutt_date_epoch();
281   m->last_visited.tv_nsec = 0;
282 #endif
283 }
284
285 /**
286  * mutt_mailbox_next_buffer - incoming folders completion routine
287  * @param m_cur Current Mailbox
288  * @param s     Buffer containing name of current mailbox
289  *
290  * Given a folder name, find the next incoming folder with new mail.
291  */
292 void mutt_mailbox_next_buffer(struct Mailbox *m_cur, struct Buffer *s)
293 {
294   mutt_buffer_expand_path(s);
295
296   if (mutt_mailbox_check(m_cur, 0) > 0)
297   {
298     bool found = false;
299     for (int pass = 0; pass < 2; pass++)
300     {
301       struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
302       struct MailboxNode *np = NULL;
303       STAILQ_FOREACH(np, &ml, entries)
304       {
305         if (np->mailbox->magic == MUTT_NOTMUCH) /* only match real mailboxes */
306           continue;
307         mutt_buffer_expand_path(&np->mailbox->pathbuf);
308         if ((found || (pass > 0)) && np->mailbox->has_new)
309         {
310           mutt_buffer_strcpy(s, mailbox_path(np->mailbox));
311           mutt_buffer_pretty_mailbox(s);
312           return;
313         }
314         if (mutt_str_strcmp(mutt_b2s(s), mailbox_path(np->mailbox)) == 0)
315           found = true;
316       }
317       neomutt_mailboxlist_clear(&ml);
318     }
319
320     mutt_mailbox_check(m_cur, MUTT_MAILBOX_CHECK_FORCE); /* mailbox was wrong - resync things */
321   }
322
323   /* no folders with new mail */
324   mutt_buffer_reset(s);
325 }
326
327 /**
328  * mutt_mailbox_next - incoming folders completion routine
329  * @param m_cur Current Mailbox
330  * @param s     Buffer containing name of current mailbox
331  * @param slen  Buffer length
332  *
333  * Given a folder name, find the next incoming folder with new mail.
334  */
335 void mutt_mailbox_next(struct Mailbox *m_cur, char *s, size_t slen)
336 {
337   struct Buffer *s_buf = mutt_buffer_pool_get();
338
339   mutt_buffer_addstr(s_buf, NONULL(s));
340   mutt_mailbox_next_buffer(m_cur, s_buf);
341   mutt_str_strfcpy(s, mutt_b2s(s_buf), slen);
342
343   mutt_buffer_pool_release(&s_buf);
344 }
345
346 /**
347  * mutt_mailbox_cleanup - Restore the timestamp of a mailbox
348  * @param path Path to the mailbox
349  * @param st   Timestamp info from stat()
350  *
351  * Fix up the atime and mtime after mbox/mmdf mailbox was modified according to
352  * stat() info taken before a modification.
353  */
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(0, 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(0, buf, ts, 0);
393 #else
394       utime(path, NULL);
395 #endif
396     }
397   }
398 }