mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2010,2013 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 2016-2017 Kevin J. McCarthy <kevin@8t8.us>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#if HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "mutt.h"
25#include "buffy.h"
26#include "mailbox.h"
27#include "mx.h"
28
29#include "mutt_curses.h"
30#include "mutt_menu.h"
31
32#ifdef USE_SIDEBAR
33#include "sidebar.h"
34#endif
35
36#ifdef USE_IMAP
37#include "imap.h"
38#endif
39
40#ifdef USE_INOTIFY
41#include "monitor.h"
42#endif
43
44#include <string.h>
45#include <sys/stat.h>
46#include <dirent.h>
47#include <utime.h>
48#include <ctype.h>
49#include <unistd.h>
50
51#include <stdio.h>
52
53static time_t BuffyTime = 0; /* last time we started checking for mail */
54static time_t BuffyStatsTime = 0; /* last time we check performed mail_check_stats */
55time_t BuffyDoneTime = 0; /* last time we knew for sure how much mail there was. */
56static short BuffyCount = 0; /* how many boxes with new mail */
57static short BuffyNotify = 0; /* # of unnotified new boxes */
58
59static BUFFY* buffy_get (const char *path);
60
61/* Find the last message in the file.
62 * upon success return 0. If no message found - return -1 */
63
64static int fseek_last_message (FILE * f)
65{
66 LOFF_T pos;
67 char buffer[BUFSIZ + 9]; /* 7 for "\n\nFrom " */
68 int bytes_read;
69 int i; /* Index into `buffer' for scanning. */
70
71 memset (buffer, 0, sizeof(buffer));
72 fseek (f, 0, SEEK_END);
73 pos = ftello (f);
74
75 /* Set `bytes_read' to the size of the last, probably partial, buffer; 0 <
76 * `bytes_read' <= `BUFSIZ'. */
77 bytes_read = pos % BUFSIZ;
78 if (bytes_read == 0)
79 bytes_read = BUFSIZ;
80 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
81 * reads will be on block boundaries, which might increase efficiency. */
82 while ((pos -= bytes_read) >= 0)
83 {
84 /* we save in the buffer at the end the first 7 chars from the last read */
85 strncpy (buffer + BUFSIZ, buffer, 5+2); /* 2 == 2 * mutt_strlen(CRLF) */
86 fseeko (f, pos, SEEK_SET);
87 bytes_read = fread (buffer, sizeof (char), bytes_read, f);
88 if (bytes_read == -1)
89 return -1;
90 for (i = bytes_read; --i >= 0;)
91 if (!mutt_strncmp (buffer + i, "\n\nFrom ", mutt_strlen ("\n\nFrom ")))
92 { /* found it - go to the beginning of the From */
93 fseeko (f, pos + i + 2, SEEK_SET);
94 return 0;
95 }
96 bytes_read = BUFSIZ;
97 }
98
99 /* here we are at the beginning of the file */
100 if (!mutt_strncmp ("From ", buffer, 5))
101 {
102 fseek (f, 0, 0);
103 return (0);
104 }
105
106 return (-1);
107}
108
109/* Return 1 if the last message is new */
110static int test_last_status_new (FILE * f)
111{
112 HEADER *hdr;
113 ENVELOPE* tmp_envelope;
114 int result = 0;
115
116 if (fseek_last_message (f) == -1)
117 return (0);
118
119 hdr = mutt_new_header ();
120 tmp_envelope = mutt_read_rfc822_header (f, hdr, 0, 0);
121 if (!(hdr->read || hdr->old))
122 result = 1;
123
124 mutt_free_envelope(&tmp_envelope);
125 mutt_free_header (&hdr);
126
127 return result;
128}
129
130static int test_new_folder (const char *path)
131{
132 FILE *f;
133 int rc = 0;
134 int typ;
135
136 typ = mx_get_magic (path);
137
138 if (typ != MUTT_MBOX && typ != MUTT_MMDF)
139 return 0;
140
141 if ((f = fopen (path, "rb")))
142 {
143 rc = test_last_status_new (f);
144 safe_fclose (&f);
145 }
146
147 return rc;
148}
149
150void mutt_buffy_cleanup (const char *buf, struct stat *st)
151{
152#ifdef HAVE_UTIMENSAT
153 struct timespec ts[2];
154#else
155 struct utimbuf ut;
156#endif
157 BUFFY *tmp;
158
159 if (option(OPTCHECKMBOXSIZE))
160 {
161 tmp = mutt_find_mailbox (buf);
162 if (tmp && !tmp->new)
163 mutt_update_mailbox (tmp);
164 }
165 else
166 {
167 /* fix up the times so buffy won't get confused */
168 if (st->st_mtime > st->st_atime)
169 {
170#ifdef HAVE_UTIMENSAT
171 ts[0].tv_sec = 0;
172 ts[0].tv_nsec = UTIME_OMIT;
173 ts[1].tv_sec = 0;
174 ts[1].tv_nsec = UTIME_NOW;
175 utimensat (0, buf, ts, 0);
176#else
177 ut.actime = st->st_atime;
178 ut.modtime = time (NULL);
179 utime (buf, &ut);
180#endif
181 }
182 else
183 {
184#ifdef HAVE_UTIMENSAT
185 ts[0].tv_sec = 0;
186 ts[0].tv_nsec = UTIME_NOW;
187 ts[1].tv_sec = 0;
188 ts[1].tv_nsec = UTIME_NOW;
189 utimensat (0, buf, ts, 0);
190#else
191 utime (buf, NULL);
192#endif
193 }
194 }
195}
196
197BUFFY *mutt_find_mailbox (const char *path)
198{
199 BUFFY *tmp = NULL;
200 struct stat sb;
201 struct stat tmp_sb;
202
203 if (stat (path,&sb) != 0)
204 return NULL;
205
206 for (tmp = Incoming; tmp; tmp = tmp->next)
207 {
208 if (stat (mutt_b2s (tmp->pathbuf), &tmp_sb) ==0 &&
209 sb.st_dev == tmp_sb.st_dev && sb.st_ino == tmp_sb.st_ino)
210 break;
211 }
212 return tmp;
213}
214
215void mutt_update_mailbox (BUFFY * b)
216{
217 struct stat sb;
218
219 if (!b)
220 return;
221
222 if (stat (mutt_b2s (b->pathbuf), &sb) == 0)
223 b->size = (off_t) sb.st_size;
224 else
225 b->size = 0;
226 return;
227}
228
229static BUFFY *buffy_new (const char *path)
230{
231 BUFFY* buffy;
232 char rp[PATH_MAX] = "";
233 char *r = NULL;
234
235 buffy = (BUFFY *) safe_calloc (1, sizeof (BUFFY));
236 buffy->pathbuf = mutt_buffer_new ();
237 mutt_buffer_strcpy (buffy->pathbuf, path);
238 r = realpath (path, rp);
239 buffy->realpath = safe_strdup (r ? rp : path);
240 buffy->next = NULL;
241 buffy->magic = 0;
242
243 return buffy;
244}
245
246static void buffy_free (BUFFY **mailbox)
247{
248 if (!(mailbox && *mailbox))
249 return;
250
251 mutt_buffer_free (&((*mailbox)->pathbuf));
252 FREE (&((*mailbox)->realpath));
253 FREE (mailbox); /* __FREE_CHECKED__ */
254}
255
256int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, union pointer_long_t udata, BUFFER *err)
257{
258 BUFFY **tmp,*tmp1;
259 struct stat sb;
260 char f1[PATH_MAX];
261 char *p;
262 long data = udata.l;
263
264 while (MoreArgs (s))
265 {
266 mutt_extract_token (path, s, 0);
267
268 if (data == MUTT_UNMAILBOXES && mutt_strcmp(mutt_b2s (path),"*") == 0)
269 {
270 for (tmp = &Incoming; *tmp;)
271 {
272 tmp1=(*tmp)->next;
273#ifdef USE_SIDEBAR
274 mutt_sb_notify_mailbox (*tmp, 0);
275#endif
276#ifdef USE_INOTIFY
277 mutt_monitor_remove (*tmp);
278#endif
279 buffy_free (tmp);
280 *tmp=tmp1;
281 }
282 return 0;
283 }
284
285 mutt_buffer_expand_path (path);
286
287 /* Skip empty tokens. */
288 if (!mutt_buffer_len (path)) continue;
289
290 /* avoid duplicates */
291 p = realpath (mutt_b2s (path), f1);
292 for (tmp = &Incoming; *tmp; tmp = &((*tmp)->next))
293 {
294 if (mutt_strcmp (p ? p : mutt_b2s (path), (*tmp)->realpath) == 0)
295 {
296 dprint(3,(debugfile,"mailbox '%s' already registered as '%s'\n", mutt_b2s (path),
297 mutt_b2s ((*tmp)->pathbuf)));
298 break;
299 }
300 }
301
302 if (data == MUTT_UNMAILBOXES)
303 {
304 if (*tmp)
305 {
306 tmp1=(*tmp)->next;
307#ifdef USE_SIDEBAR
308 mutt_sb_notify_mailbox (*tmp, 0);
309#endif
310#ifdef USE_INOTIFY
311 mutt_monitor_remove (*tmp);
312#endif
313 buffy_free (tmp);
314 *tmp=tmp1;
315 }
316 continue;
317 }
318
319 if (!*tmp)
320 {
321 *tmp = buffy_new (mutt_b2s (path));
322#ifdef USE_SIDEBAR
323 mutt_sb_notify_mailbox (*tmp, 1);
324#endif
325#ifdef USE_INOTIFY
326 (*tmp)->magic = mx_get_magic (mutt_b2s ((*tmp)->pathbuf));
327 mutt_monitor_add (*tmp);
328#endif
329 }
330
331 (*tmp)->new = 0;
332 (*tmp)->notified = 1;
333 (*tmp)->newly_created = 0;
334
335 /* for check_mbox_size, it is important that if the folder is new (tested by
336 * reading it), the size is set to 0 so that later when we check we see
337 * that it increased . without check_mbox_size we probably don't care.
338 */
339 if (option(OPTCHECKMBOXSIZE) &&
340 stat (mutt_b2s ((*tmp)->pathbuf), &sb) == 0 &&
341 !test_new_folder (mutt_b2s ((*tmp)->pathbuf)))
342 {
343 /* some systems out there don't have an off_t type */
344 (*tmp)->size = (off_t) sb.st_size;
345 }
346 else
347 (*tmp)->size = 0;
348 }
349 return 0;
350}
351
352/* Checks the specified maildir subdir (cur or new) for new mail or mail counts.
353 * check_new: if true, check for new mail.
354 * check_stats: if true, count total, new, and flagged messages.
355 * Returns 1 if the dir has new mail.
356 */
357static int buffy_maildir_check_dir (BUFFY* mailbox, const char *dir_name, int check_new,
358 int check_stats)
359{
360 BUFFER *path = NULL;
361 BUFFER *msgpath = NULL;
362 DIR *dirp;
363 struct dirent *de;
364 char *p;
365 int rc = 0;
366 struct stat sb;
367
368 path = mutt_buffer_pool_get ();
369 msgpath = mutt_buffer_pool_get ();
370 mutt_buffer_printf (path, "%s/%s", mutt_b2s (mailbox->pathbuf), dir_name);
371
372 /* when $mail_check_recent is set, if the new/ directory hasn't been modified since
373 * the user last exited the mailbox, then we know there is no recent mail.
374 */
375 if (check_new && option(OPTMAILCHECKRECENT))
376 {
377 if (stat(mutt_b2s (path), &sb) == 0 &&
378 mutt_stat_timespec_compare (&sb, MUTT_STAT_MTIME, &mailbox->last_visited) < 0)
379 {
380 rc = 0;
381 check_new = 0;
382 }
383 }
384
385 if (! (check_new || check_stats))
386 goto cleanup;
387
388 if ((dirp = opendir (mutt_b2s (path))) == NULL)
389 {
390 mailbox->magic = 0;
391 rc = 0;
392 goto cleanup;
393 }
394
395 while ((de = readdir (dirp)) != NULL)
396 {
397 if (*de->d_name == '.')
398 continue;
399
400 p = strstr (de->d_name, ":2,");
401 if (p && strchr (p + 3, 'T'))
402 continue;
403
404 if (check_stats)
405 {
406 mailbox->msg_count++;
407 if (p && strchr (p + 3, 'F'))
408 mailbox->msg_flagged++;
409 }
410 if (!p || !strchr (p + 3, 'S'))
411 {
412 if (check_stats)
413 mailbox->msg_unread++;
414 if (check_new)
415 {
416 if (option(OPTMAILCHECKRECENT))
417 {
418 mutt_buffer_printf (msgpath, "%s/%s", mutt_b2s (path), de->d_name);
419 /* ensure this message was received since leaving this mailbox */
420 if (stat(mutt_b2s (msgpath), &sb) == 0 &&
421 (mutt_stat_timespec_compare (&sb, MUTT_STAT_CTIME, &mailbox->last_visited) <= 0))
422 continue;
423 }
424 mailbox->new = 1;
425 rc = 1;
426 check_new = 0;
427 if (!check_stats)
428 break;
429 }
430 }
431 }
432
433 closedir (dirp);
434
435cleanup:
436 mutt_buffer_pool_release (&path);
437 mutt_buffer_pool_release (&msgpath);
438
439 return rc;
440}
441
442/* Checks new mail for a maildir mailbox.
443 * check_stats: if true, also count total, new, and flagged messages.
444 * Returns 1 if the mailbox has new mail.
445 */
446static int buffy_maildir_check (BUFFY* mailbox, int check_stats)
447{
448 int rc, check_new = 1;
449
450 if (check_stats)
451 {
452 mailbox->msg_count = 0;
453 mailbox->msg_unread = 0;
454 mailbox->msg_flagged = 0;
455 }
456
457 rc = buffy_maildir_check_dir (mailbox, "new", check_new, check_stats);
458
459 check_new = !rc && option (OPTMAILDIRCHECKCUR);
460 if (check_new || check_stats)
461 if (buffy_maildir_check_dir (mailbox, "cur", check_new, check_stats))
462 rc = 1;
463
464 return rc;
465}
466
467/* Checks new mail for an mbox mailbox
468 * check_stats: if true, also count total, new, and flagged messages.
469 * Returns 1 if the mailbox has new mail.
470 */
471static int buffy_mbox_check (BUFFY* mailbox, struct stat *sb, int check_stats)
472{
473 int rc = 0;
474 int new_or_changed;
475 CONTEXT ctx;
476
477 if (option (OPTCHECKMBOXSIZE))
478 new_or_changed = sb->st_size > mailbox->size;
479 else
480 new_or_changed = (mutt_stat_compare (sb, MUTT_STAT_MTIME, sb, MUTT_STAT_ATIME) > 0)
481 || (mailbox->newly_created &&
482 (mutt_stat_compare (sb, MUTT_STAT_CTIME, sb, MUTT_STAT_MTIME) == 0) &&
483 (mutt_stat_compare (sb, MUTT_STAT_CTIME, sb, MUTT_STAT_ATIME) == 0));
484
485 if (new_or_changed)
486 {
487 if (!option(OPTMAILCHECKRECENT) ||
488 (mutt_stat_timespec_compare (sb, MUTT_STAT_MTIME, &mailbox->last_visited) > 0))
489 {
490 rc = 1;
491 mailbox->new = 1;
492 }
493 }
494 else if (option(OPTCHECKMBOXSIZE))
495 {
496 /* some other program has deleted mail from the folder */
497 mailbox->size = (off_t) sb->st_size;
498 }
499
500 if (mailbox->newly_created &&
501 (sb->st_ctime != sb->st_mtime || sb->st_ctime != sb->st_atime))
502 mailbox->newly_created = 0;
503
504 if (check_stats &&
505 (mutt_stat_timespec_compare (sb, MUTT_STAT_MTIME, &mailbox->stats_last_checked) > 0))
506 {
507 if (mx_open_mailbox (mutt_b2s (mailbox->pathbuf),
508 MUTT_READONLY | MUTT_QUIET | MUTT_NOSORT | MUTT_PEEK,
509 &ctx) != NULL)
510 {
511 mailbox->msg_count = ctx.msgcount;
512 mailbox->msg_unread = ctx.unread;
513 mailbox->msg_flagged = ctx.flagged;
514 mailbox->stats_last_checked = ctx.mtime;
515 mx_close_mailbox (&ctx, 0);
516 }
517 }
518
519 return rc;
520}
521
522/* Check all Incoming for new mail and total/new/flagged messages
523 * The force argument may be any combination of the following values:
524 * MUTT_BUFFY_CHECK_FORCE ignore BuffyTimeout and check for new mail
525 * MUTT_BUFFY_CHECK_FORCE_STATS ignore BuffyTimeout and calculate statistics
526 */
527int mutt_buffy_check (int force)
528{
529 BUFFY *tmp;
530 struct stat sb;
531 struct stat contex_sb;
532 time_t t;
533 int check_stats = 0;
534#ifdef USE_SIDEBAR
535 short orig_new;
536 int orig_count, orig_unread, orig_flagged;
537#endif
538
539 sb.st_size=0;
540 contex_sb.st_dev=0;
541 contex_sb.st_ino=0;
542
543#ifdef USE_IMAP
544 /* update postponed count as well, on force */
545 if (force & MUTT_BUFFY_CHECK_FORCE)
546 mutt_update_num_postponed ();
547#endif
548
549 /* fastest return if there are no mailboxes */
550 if (!Incoming)
551 return 0;
552 t = time (NULL);
553 if (!force && (t - BuffyTime < BuffyTimeout))
554 return BuffyCount;
555
556 if ((force & MUTT_BUFFY_CHECK_FORCE_STATS) ||
557 (option (OPTMAILCHECKSTATS) &&
558 (t - BuffyStatsTime >= BuffyCheckStatsInterval)))
559 {
560 check_stats = 1;
561 BuffyStatsTime = t;
562 }
563
564 BuffyTime = t;
565 BuffyCount = 0;
566 BuffyNotify = 0;
567
568#ifdef USE_IMAP
569 BuffyCount += imap_buffy_check (force, check_stats);
570#endif
571
572 /* check device ID and serial number instead of comparing paths */
573 if (!Context || Context->magic == MUTT_IMAP || Context->magic == MUTT_POP
574 || stat (Context->path, &contex_sb) != 0)
575 {
576 contex_sb.st_dev=0;
577 contex_sb.st_ino=0;
578 }
579
580 for (tmp = Incoming; tmp; tmp = tmp->next)
581 {
582#ifdef USE_SIDEBAR
583 orig_new = tmp->new;
584 orig_count = tmp->msg_count;
585 orig_unread = tmp->msg_unread;
586 orig_flagged = tmp->msg_flagged;
587#endif
588
589 if (tmp->magic != MUTT_IMAP)
590 {
591 tmp->new = 0;
592#ifdef USE_POP
593 if (mx_is_pop (mutt_b2s (tmp->pathbuf)))
594 tmp->magic = MUTT_POP;
595 else
596#endif
597 if (stat (mutt_b2s (tmp->pathbuf), &sb) != 0 ||
598 (S_ISREG(sb.st_mode) && sb.st_size == 0) ||
599 (!tmp->magic &&
600 (tmp->magic = mx_get_magic (mutt_b2s (tmp->pathbuf))) <= 0))
601 {
602 /* if the mailbox still doesn't exist, set the newly created flag to
603 * be ready for when it does. */
604 tmp->newly_created = 1;
605 tmp->magic = 0;
606 tmp->size = 0;
607 continue;
608 }
609 }
610
611 /* check to see if the folder is the currently selected folder
612 * before polling */
613 if (!Context || !Context->path ||
614 (( tmp->magic == MUTT_IMAP || tmp->magic == MUTT_POP )
615 ? mutt_strcmp (mutt_b2s (tmp->pathbuf), Context->path) :
616 (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino)))
617 {
618 switch (tmp->magic)
619 {
620 case MUTT_MBOX:
621 case MUTT_MMDF:
622 if (buffy_mbox_check (tmp, &sb, check_stats) > 0)
623 BuffyCount++;
624 break;
625
626 case MUTT_MAILDIR:
627 if (buffy_maildir_check (tmp, check_stats) > 0)
628 BuffyCount++;
629 break;
630
631 case MUTT_MH:
632 if (mh_buffy (tmp, check_stats) > 0)
633 BuffyCount++;
634 break;
635 }
636 }
637 else if (option(OPTCHECKMBOXSIZE) && Context && Context->path)
638 tmp->size = (off_t) sb.st_size; /* update the size of current folder */
639
640#ifdef USE_SIDEBAR
641 if ((orig_new != tmp->new) ||
642 (orig_count != tmp->msg_count) ||
643 (orig_unread != tmp->msg_unread) ||
644 (orig_flagged != tmp->msg_flagged))
645 mutt_set_current_menu_redraw (REDRAW_SIDEBAR);
646#endif
647
648 if (!tmp->new)
649 tmp->notified = 0;
650 else if (!tmp->notified)
651 BuffyNotify++;
652 }
653
654 BuffyDoneTime = BuffyTime;
655 return (BuffyCount);
656}
657
658int mutt_buffy_list (void)
659{
660 BUFFY *tmp;
661 BUFFER *path = NULL;
662 char buffylist[2*STRING];
663 size_t pos = 0;
664 int first = 1;
665
666 int have_unnotified = BuffyNotify;
667
668 path = mutt_buffer_pool_get ();
669
670 buffylist[0] = 0;
671 pos += strlen (strncat (buffylist, _("New mail in "), sizeof (buffylist) - 1 - pos)); /* __STRNCAT_CHECKED__ */
672 for (tmp = Incoming; tmp; tmp = tmp->next)
673 {
674 /* Is there new mail in this mailbox? */
675 if (!tmp->new || (have_unnotified && tmp->notified))
676 continue;
677
678 mutt_buffer_strcpy (path, mutt_b2s (tmp->pathbuf));
679 mutt_buffer_pretty_mailbox (path);
680
681 if (!first && (MuttMessageWindow->cols >= 7) &&
682 (pos + mutt_buffer_len (path) >= (size_t)MuttMessageWindow->cols - 7))
683 break;
684
685 if (!first)
686 pos += strlen (strncat(buffylist + pos, ", ", sizeof(buffylist)-1-pos)); /* __STRNCAT_CHECKED__ */
687
688 /* Prepend an asterisk to mailboxes not already notified */
689 if (!tmp->notified)
690 {
691 /* pos += strlen (strncat(buffylist + pos, "*", sizeof(buffylist)-1-pos)); __STRNCAT_CHECKED__ */
692 tmp->notified = 1;
693 BuffyNotify--;
694 }
695 pos += strlen (strncat(buffylist + pos, mutt_b2s (path), sizeof(buffylist)-1-pos)); /* __STRNCAT_CHECKED__ */
696 first = 0;
697 }
698 if (!first && tmp)
699 {
700 strncat (buffylist + pos, ", ...", sizeof (buffylist) - 1 - pos); /* __STRNCAT_CHECKED__ */
701 }
702
703 mutt_buffer_pool_release (&path);
704
705 if (!first)
706 {
707 mutt_message ("%s", buffylist);
708 return (1);
709 }
710 else
711 {
712 /* there were no mailboxes needing to be notified, so clean up since
713 * BuffyNotify has somehow gotten out of sync
714 */
715 BuffyNotify = 0;
716 return (0);
717 }
718}
719
720void mutt_buffy_setnotified (const char *path)
721{
722 BUFFY *buffy;
723
724 buffy = buffy_get(path);
725 if (!buffy)
726 return;
727
728 buffy->notified = 1;
729#if HAVE_CLOCK_GETTIME
730 clock_gettime (CLOCK_REALTIME, &buffy->last_visited);
731#else
732 buffy->last_visited.tv_nsec = 0;
733 time(&buffy->last_visited.tv_sec);
734#endif
735}
736
737int mutt_buffy_notify (void)
738{
739 if (mutt_buffy_check (0) && BuffyNotify)
740 {
741 return (mutt_buffy_list ());
742 }
743 return (0);
744}
745
746void mutt_buffy (char *s, size_t slen)
747{
748 BUFFER *s_buf;
749
750 s_buf = mutt_buffer_pool_get ();
751
752 mutt_buffer_addstr (s_buf, NONULL (s));
753 mutt_buffer_buffy (s_buf);
754 strfcpy (s, mutt_b2s (s_buf), slen);
755
756 mutt_buffer_pool_release (&s_buf);
757}
758
759/*
760 * mutt_buffy() -- incoming folders completion routine
761 *
762 * given a folder name, this routine gives the next incoming folder with new
763 * mail.
764 */
765void mutt_buffer_buffy (BUFFER *s)
766{
767 BUFFY *tmp = Incoming;
768 int pass, found = 0;
769
770 mutt_buffer_expand_path (s);
771
772 if (mutt_buffy_check (0))
773 {
774 for (pass = 0; pass < 2; pass++)
775 for (tmp = Incoming; tmp; tmp = tmp->next)
776 {
777 mutt_buffer_expand_path (tmp->pathbuf);
778 if ((found || pass) && tmp->new)
779 {
780 mutt_buffer_strcpy (s, mutt_b2s (tmp->pathbuf));
781 mutt_buffer_pretty_mailbox (s);
782 return;
783 }
784 if (mutt_strcmp (mutt_b2s (s), mutt_b2s (tmp->pathbuf)) == 0)
785 found = 1;
786 }
787
788 mutt_buffy_check (MUTT_BUFFY_CHECK_FORCE); /* buffy was wrong - resync
789 things */
790 }
791
792 /* no folders with new mail */
793 mutt_buffer_clear (s);
794}
795
796/* fetch buffy object for given path, if present */
797static BUFFY* buffy_get (const char *path)
798{
799 BUFFY *cur;
800 BUFFER *epath;
801
802 if (!path)
803 return NULL;
804
805 epath = mutt_buffer_pool_get ();
806 mutt_buffer_strcpy (epath, NONULL (path));
807 mutt_buffer_expand_path (epath);
808
809 for (cur = Incoming; cur; cur = cur->next)
810 {
811 /* must be done late because e.g. IMAP delimiter may change */
812 mutt_buffer_expand_path (cur->pathbuf);
813 if (!mutt_strcmp (mutt_b2s (cur->pathbuf), mutt_b2s (epath)))
814 {
815 mutt_buffer_pool_release (&epath);
816 return cur;
817 }
818 }
819
820 mutt_buffer_pool_release (&epath);
821 return NULL;
822}