2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include "mutt_curses.h"
43 static time_t BuffyTime = 0; /* last time we started checking for mail */
44 time_t BuffyDoneTime = 0; /* last time we knew for sure how much mail there was. */
45 static short BuffyCount = 0; /* how many boxes with new mail */
46 static short BuffyNotify = 0; /* # of unnotified new boxes */
48 static BUFFY* buffy_get (const char *path);
50 /* Find the last message in the file.
51 * upon success return 0. If no message found - return -1 */
53 static int fseek_last_message (FILE * f)
56 char buffer[BUFSIZ + 9]; /* 7 for "\n\nFrom " */
58 int i; /* Index into `buffer' for scanning. */
60 memset (buffer, 0, sizeof(buffer));
61 fseek (f, 0, SEEK_END);
64 /* Set `bytes_read' to the size of the last, probably partial, buffer; 0 <
65 * `bytes_read' <= `BUFSIZ'. */
66 bytes_read = pos % BUFSIZ;
69 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
70 * reads will be on block boundaries, which might increase efficiency. */
71 while ((pos -= bytes_read) >= 0)
73 /* we save in the buffer at the end the first 7 chars from the last read */
74 strncpy (buffer + BUFSIZ, buffer, 5+2); /* 2 == 2 * mutt_strlen(CRLF) */
75 fseeko (f, pos, SEEK_SET);
76 bytes_read = fread (buffer, sizeof (char), bytes_read, f);
79 for (i = bytes_read; --i >= 0;)
80 if (!mutt_strncmp (buffer + i, "\n\nFrom ", mutt_strlen ("\n\nFrom ")))
81 { /* found it - go to the beginning of the From */
82 fseeko (f, pos + i + 2, SEEK_SET);
88 /* here we are at the beginning of the file */
89 if (!mutt_strncmp ("From ", buffer, 5))
98 /* Return 1 if the last message is new */
99 static int test_last_status_new (FILE * f)
102 ENVELOPE* tmp_envelope;
105 if (fseek_last_message (f) == -1)
108 hdr = mutt_new_header ();
109 tmp_envelope = mutt_read_rfc822_header (f, hdr, 0, 0);
110 if (!(hdr->read || hdr->old))
113 mutt_free_envelope(&tmp_envelope);
114 mutt_free_header (&hdr);
119 static int test_new_folder (const char *path)
125 typ = mx_get_magic (path);
127 if (typ != M_MBOX && typ != M_MMDF)
130 if ((f = fopen (path, "rb")))
132 rc = test_last_status_new (f);
139 void mutt_buffy_cleanup (const char *buf, struct stat *st)
144 if (option(OPTCHECKMBOXSIZE))
146 tmp = mutt_find_mailbox (buf);
147 if (tmp && !tmp->new)
148 mutt_update_mailbox (tmp);
152 /* fix up the times so buffy won't get confused */
153 if (st->st_mtime > st->st_atime)
155 ut.actime = st->st_atime;
156 ut.modtime = time (NULL);
164 BUFFY *mutt_find_mailbox (const char *path)
170 if (stat (path,&sb) != 0)
173 for (tmp = Incoming; tmp; tmp = tmp->next)
175 if (stat (tmp->path,&tmp_sb) ==0 &&
176 sb.st_dev == tmp_sb.st_dev && sb.st_ino == tmp_sb.st_ino)
182 void mutt_update_mailbox (BUFFY * b)
189 if (stat (b->path, &sb) == 0)
190 b->size = (off_t) sb.st_size;
196 static BUFFY *buffy_new (const char *path)
200 buffy = (BUFFY *) safe_calloc (1, sizeof (BUFFY));
201 strfcpy (buffy->path, path, sizeof (buffy->path));
208 static void buffy_free (BUFFY **mailbox)
210 FREE (mailbox); /* __FREE_CHECKED__ */
213 int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
216 char buf[_POSIX_PATH_MAX];
218 char f1[PATH_MAX], f2[PATH_MAX];
223 mutt_extract_token (path, s, 0);
224 strfcpy (buf, path->data, sizeof (buf));
226 if(data == M_UNMAILBOXES && mutt_strcmp(buf,"*") == 0)
228 for (tmp = &Incoming; *tmp;)
237 mutt_expand_path (buf, sizeof (buf));
239 /* Skip empty tokens. */
242 /* avoid duplicates */
243 p = realpath (buf, f1);
244 for (tmp = &Incoming; *tmp; tmp = &((*tmp)->next))
246 q = realpath ((*tmp)->path, f2);
247 if (mutt_strcmp (p ? p : buf, q ? q : (*tmp)->path) == 0)
249 dprint(3,(debugfile,"mailbox '%s' already registered as '%s'\n", buf, (*tmp)->path));
254 if(data == M_UNMAILBOXES)
266 *tmp = buffy_new (buf);
269 (*tmp)->notified = 1;
270 (*tmp)->newly_created = 0;
272 /* for check_mbox_size, it is important that if the folder is new (tested by
273 * reading it), the size is set to 0 so that later when we check we see
274 * that it increased . without check_mbox_size we probably don't care.
276 if (option(OPTCHECKMBOXSIZE) &&
277 stat ((*tmp)->path, &sb) == 0 && !test_new_folder ((*tmp)->path))
279 /* some systems out there don't have an off_t type */
280 (*tmp)->size = (off_t) sb.st_size;
288 /* returns 1 if maildir has new mail */
289 static int buffy_maildir_hasnew (BUFFY* mailbox)
291 char path[_POSIX_PATH_MAX];
298 snprintf (path, sizeof (path), "%s/new", mailbox->path);
300 /* when $mail_check_recent is set, if the new/ directory hasn't been modified since
301 * the user last exited the mailbox, then we know there is no recent mail.
303 if (option(OPTMAILCHECKRECENT))
305 if (stat(path, &sb) == 0 && sb.st_mtime < mailbox->last_visited)
309 if ((dirp = opendir (path)) == NULL)
315 while ((de = readdir (dirp)) != NULL)
317 if (*de->d_name == '.')
320 if (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T'))
322 if (option(OPTMAILCHECKRECENT))
324 char msgpath[_POSIX_PATH_MAX];
326 snprintf(msgpath, sizeof(msgpath), "%s/%s", path, de->d_name);
327 /* ensure this message was received since leaving this mailbox */
328 if (stat(msgpath, &sb) == 0 && (sb.st_ctime <= mailbox->last_visited))
331 /* one new and undeleted message is enough */
343 /* returns 1 if mailbox has new mail */
344 static int buffy_mbox_hasnew (BUFFY* mailbox, struct stat *sb)
349 if (option (OPTCHECKMBOXSIZE))
350 statcheck = sb->st_size > mailbox->size;
352 statcheck = sb->st_mtime > sb->st_atime
353 || (mailbox->newly_created && sb->st_ctime == sb->st_mtime && sb->st_ctime == sb->st_atime);
356 if (!option(OPTMAILCHECKRECENT) || sb->st_mtime > mailbox->last_visited)
362 else if (option(OPTCHECKMBOXSIZE))
364 /* some other program has deleted mail from the folder */
365 mailbox->size = (off_t) sb->st_size;
367 if (mailbox->newly_created &&
368 (sb->st_ctime != sb->st_mtime || sb->st_ctime != sb->st_atime))
369 mailbox->newly_created = 0;
374 int mutt_buffy_check (int force)
378 struct stat contex_sb;
386 /* update postponed count as well, on force */
388 mutt_update_num_postponed ();
391 /* fastest return if there are no mailboxes */
395 if (!force && (t - BuffyTime < BuffyTimeout))
403 BuffyCount += imap_buffy_check (force);
406 /* check device ID and serial number instead of comparing paths */
407 if (!Context || Context->magic == M_IMAP || Context->magic == M_POP
408 || stat (Context->path, &contex_sb) != 0)
414 for (tmp = Incoming; tmp; tmp = tmp->next)
416 if (tmp->magic != M_IMAP)
420 if (mx_is_pop (tmp->path))
424 if (stat (tmp->path, &sb) != 0 || (S_ISREG(sb.st_mode) && sb.st_size == 0) ||
425 (!tmp->magic && (tmp->magic = mx_get_magic (tmp->path)) <= 0))
427 /* if the mailbox still doesn't exist, set the newly created flag to
428 * be ready for when it does. */
429 tmp->newly_created = 1;
436 /* check to see if the folder is the currently selected folder
438 if (!Context || !Context->path ||
439 (( tmp->magic == M_IMAP || tmp->magic == M_POP )
440 ? mutt_strcmp (tmp->path, Context->path) :
441 (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino)))
447 if (buffy_mbox_hasnew (tmp, &sb) > 0)
452 if (buffy_maildir_hasnew (tmp) > 0)
457 if ((tmp->new = mh_buffy (tmp->path)) > 0)
462 else if (option(OPTCHECKMBOXSIZE) && Context && Context->path)
463 tmp->size = (off_t) sb.st_size; /* update the size of current folder */
467 else if (!tmp->notified)
471 BuffyDoneTime = BuffyTime;
475 int mutt_buffy_list (void)
478 char path[_POSIX_PATH_MAX];
479 char buffylist[2*STRING];
483 int have_unnotified = BuffyNotify;
486 pos += strlen (strncat (buffylist, _("New mail in "), sizeof (buffylist) - 1 - pos)); /* __STRNCAT_CHECKED__ */
487 for (tmp = Incoming; tmp; tmp = tmp->next)
489 /* Is there new mail in this mailbox? */
490 if (!tmp->new || (have_unnotified && tmp->notified))
493 strfcpy (path, tmp->path, sizeof (path));
494 mutt_pretty_mailbox (path, sizeof (path));
496 if (!first && (COLS - 7 >= 0) && (pos + strlen (path) >= (size_t)COLS - 7))
500 pos += strlen (strncat(buffylist + pos, ", ", sizeof(buffylist)-1-pos)); /* __STRNCAT_CHECKED__ */
502 /* Prepend an asterisk to mailboxes not already notified */
505 /* pos += strlen (strncat(buffylist + pos, "*", sizeof(buffylist)-1-pos)); __STRNCAT_CHECKED__ */
509 pos += strlen (strncat(buffylist + pos, path, sizeof(buffylist)-1-pos)); /* __STRNCAT_CHECKED__ */
514 strncat (buffylist + pos, ", ...", sizeof (buffylist) - 1 - pos); /* __STRNCAT_CHECKED__ */
518 mutt_message ("%s", buffylist);
521 /* there were no mailboxes needing to be notified, so clean up since
522 * BuffyNotify has somehow gotten out of sync
528 void mutt_buffy_setnotified (const char *path)
532 buffy = buffy_get(path);
537 time(&buffy->last_visited);
540 int mutt_buffy_notify (void)
542 if (mutt_buffy_check (0) && BuffyNotify)
544 return (mutt_buffy_list ());
550 * mutt_buffy() -- incoming folders completion routine
552 * given a folder name, this routine gives the next incoming folder with new
555 void mutt_buffy (char *s, size_t slen)
557 BUFFY *tmp = Incoming;
560 mutt_expand_path (s, slen);
562 if (mutt_buffy_check (0))
564 for (pass = 0; pass < 2; pass++)
565 for (tmp = Incoming; tmp; tmp = tmp->next)
567 mutt_expand_path (tmp->path, sizeof (tmp->path));
568 if ((found || pass) && tmp->new)
570 strfcpy (s, tmp->path, slen);
571 mutt_pretty_mailbox (s, slen);
574 if (mutt_strcmp (s, tmp->path) == 0)
578 mutt_buffy_check (1); /* buffy was wrong - resync things */
581 /* no folders with new mail */
585 /* fetch buffy object for given path, if present */
586 static BUFFY* buffy_get (const char *path)
594 epath = safe_strdup(path);
595 mutt_expand_path(epath, mutt_strlen(epath));
597 for (cur = Incoming; cur; cur = cur->next)
599 /* must be done late because e.g. IMAP delimiter may change */
600 mutt_expand_path (cur->path, sizeof (cur->path));
601 if (!mutt_strcmp(cur->path, path))