root/buffy.c

Revision 5443:8b783c916893, 12.0 kB (checked in by Rocco Rutte <pdmef@…>, 8 weeks ago)

Use realpath() in mutt_pretty_mailbox() for paths that may need it.
This requires to add the buffer size as parameter since the result may
be longer than the original but still fit in the buffer. Closes #2948.

Line 
1/*
2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3 *
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.
8 *
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.
13 *
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.
17 */
18
19#if HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "mutt.h"
24#include "buffy.h"
25#include "mailbox.h"
26#include "mx.h"
27
28#include "mutt_curses.h"
29
30#ifdef USE_IMAP
31#include "imap.h"
32#endif
33
34#include <string.h>
35#include <sys/stat.h>
36#include <dirent.h>
37#include <utime.h>
38#include <ctype.h>
39#include <unistd.h>
40
41#include <stdio.h>
42
43static time_t BuffyTime = 0;    /* last time we started checking for mail */
44time_t BuffyDoneTime = 0;       /* last time we knew for sure how much mail there was. */
45static short BuffyCount = 0;    /* how many boxes with new mail */
46static short BuffyNotify = 0;   /* # of unnotified new boxes */
47
48/* Find the last message in the file.
49 * upon success return 0. If no message found - return -1 */
50
51static int fseek_last_message (FILE * f)
52{
53  LOFF_T pos;
54  char buffer[BUFSIZ + 9];      /* 7 for "\n\nFrom " */
55  int bytes_read;
56  int i;                        /* Index into `buffer' for scanning.  */
57
58  memset (buffer, 0, sizeof(buffer));
59  fseek (f, 0, SEEK_END);
60  pos = ftello (f);
61
62  /* Set `bytes_read' to the size of the last, probably partial, buffer; 0 <
63   * `bytes_read' <= `BUFSIZ'.  */
64  bytes_read = pos % BUFSIZ;
65  if (bytes_read == 0)
66    bytes_read = BUFSIZ;
67  /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
68   * reads will be on block boundaries, which might increase efficiency.  */
69  while ((pos -= bytes_read) >= 0)
70  {
71    /* we save in the buffer at the end the first 7 chars from the last read */
72    strncpy (buffer + BUFSIZ, buffer, 5+2); /* 2 == 2 * mutt_strlen(CRLF) */
73    fseeko (f, pos, SEEK_SET);
74    bytes_read = fread (buffer, sizeof (char), bytes_read, f);
75    if (bytes_read == -1)
76      return -1;
77    for (i = bytes_read; --i >= 0;)
78      if (!mutt_strncmp (buffer + i, "\n\nFrom ", mutt_strlen ("\n\nFrom ")))
79      {                         /* found it - go to the beginning of the From */
80        fseeko (f, pos + i + 2, SEEK_SET);
81        return 0;
82      }
83    bytes_read = BUFSIZ;
84  }
85
86  /* here we are at the beginning of the file */
87  if (!mutt_strncmp ("From ", buffer, 5))
88  {
89    fseek (f, 0, 0);
90    return (0);
91  }
92
93  return (-1);
94}
95
96/* Return 1 if the last message is new */
97static int test_last_status_new (FILE * f)
98{
99  HEADER *hdr;
100  ENVELOPE* tmp_envelope;
101  int result = 0;
102
103  if (fseek_last_message (f) == -1)
104    return (0);
105
106  hdr = mutt_new_header ();
107  tmp_envelope = mutt_read_rfc822_header (f, hdr, 0, 0);
108  if (!(hdr->read || hdr->old))
109    result = 1;
110
111  mutt_free_envelope(&tmp_envelope);
112  mutt_free_header (&hdr);
113
114  return result;
115}
116
117static int test_new_folder (const char *path)
118{
119  FILE *f;
120  int rc = 0;
121  int typ;
122
123  typ = mx_get_magic (path);
124
125  if (typ != M_MBOX && typ != M_MMDF)
126    return 0;
127
128  if ((f = fopen (path, "rb")))
129  {
130    rc = test_last_status_new (f);
131    fclose (f);
132  }
133
134  return rc;
135}
136
137BUFFY *mutt_find_mailbox (const char *path)
138{
139  BUFFY *tmp = NULL;
140  struct stat sb;
141  struct stat tmp_sb;
142 
143  if (stat (path,&sb) != 0)
144    return NULL;
145
146  for (tmp = Incoming; tmp; tmp = tmp->next)
147  {
148    if (stat (tmp->path,&tmp_sb) ==0 && 
149        sb.st_dev == tmp_sb.st_dev && sb.st_ino == tmp_sb.st_ino)
150      break;
151  }
152  return tmp;
153}
154
155void mutt_update_mailbox (BUFFY * b)
156{
157  struct stat sb;
158
159  if (!b)
160    return;
161
162  if (stat (b->path, &sb) == 0)
163    b->size = (off_t) sb.st_size;
164  else
165    b->size = 0;
166  return;
167}
168
169int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *err)
170{
171  BUFFY **tmp,*tmp1;
172  char buf[_POSIX_PATH_MAX];
173  struct stat sb;
174
175  while (MoreArgs (s))
176  {
177    mutt_extract_token (path, s, 0);
178    strfcpy (buf, path->data, sizeof (buf));
179
180    if(data == M_UNMAILBOXES && mutt_strcmp(buf,"*") == 0)
181    {
182      for (tmp = &Incoming; *tmp;)
183      {
184        FREE (&((*tmp)->path));
185        tmp1=(*tmp)->next;
186        FREE (tmp);             /* __FREE_CHECKED__ */
187        *tmp=tmp1;
188      }
189      return 0;
190    }
191
192    mutt_expand_path (buf, sizeof (buf));
193
194    /* Skip empty tokens. */
195    if(!*buf) continue;
196
197    /* simple check to avoid duplicates */
198    for (tmp = &Incoming; *tmp; tmp = &((*tmp)->next))
199    {
200      if (mutt_strcmp (buf, (*tmp)->path) == 0)
201        break;
202    }
203
204    if(data == M_UNMAILBOXES)
205    {
206      if(*tmp)
207      {
208        FREE (&((*tmp)->path));
209        tmp1=(*tmp)->next;
210        FREE (tmp);             /* __FREE_CHECKED__ */
211        *tmp=tmp1;
212      }
213      continue;
214    }
215
216    if (!*tmp)
217    {
218      *tmp = (BUFFY *) safe_calloc (1, sizeof (BUFFY));
219      (*tmp)->path = safe_strdup (buf);
220      (*tmp)->next = NULL;
221      /* it is tempting to set magic right here */
222      (*tmp)->magic = 0;
223     
224    }
225
226    (*tmp)->new = 0;
227    (*tmp)->notified = 1;
228    (*tmp)->newly_created = 0;
229
230    /* for check_mbox_size, it is important that if the folder is new (tested by
231     * reading it), the size is set to 0 so that later when we check we see
232     * that it increased .  without check_mbox_size we probably don't care.
233     */
234    if (option(OPTCHECKMBOXSIZE) &&
235        stat ((*tmp)->path, &sb) == 0 && !test_new_folder ((*tmp)->path))
236    {
237      /* some systems out there don't have an off_t type */
238      (*tmp)->size = (off_t) sb.st_size;
239    }
240    else
241      (*tmp)->size = 0;
242  }
243  return 0;
244}
245
246/* people use check_mbox_size on systems where modified time attributes are
247 * BADLY broken. Ignore them.
248 */
249#define STAT_CHECK_SIZE (sb.st_size > tmp->size)
250#define STAT_CHECK_TIME (sb.st_mtime > sb.st_atime || (tmp->newly_created && sb.st_ctime == sb.st_mtime && sb.st_ctime == sb.st_atime))
251#define STAT_CHECK (option(OPTCHECKMBOXSIZE) ? STAT_CHECK_SIZE : STAT_CHECK_TIME)
252
253int mutt_buffy_check (int force)
254{
255  BUFFY *tmp;
256  struct stat sb;
257  struct dirent *de;
258  DIR *dirp;
259  char path[_POSIX_PATH_MAX];
260  struct stat contex_sb;
261  time_t t;
262
263#ifdef USE_IMAP
264  /* update postponed count as well, on force */
265  if (force)
266    mutt_update_num_postponed ();
267#endif
268
269  /* fastest return if there are no mailboxes */
270  if (!Incoming)
271    return 0;
272  t = time (NULL);
273  if (!force && (t - BuffyTime < BuffyTimeout))
274    return BuffyCount;
275 
276  BuffyTime = t;
277  BuffyCount = 0;
278  BuffyNotify = 0;
279
280#ifdef USE_IMAP
281  BuffyCount += imap_buffy_check (force);
282
283  if (!Context || Context->magic != M_IMAP)
284#endif
285#ifdef USE_POP
286  if (!Context || Context->magic != M_POP)
287#endif
288  /* check device ID and serial number instead of comparing paths */
289  if (!Context || !Context->path || stat (Context->path, &contex_sb) != 0)
290  {
291    contex_sb.st_dev=0;
292    contex_sb.st_ino=0;
293  }
294 
295  for (tmp = Incoming; tmp; tmp = tmp->next)
296  {
297#ifdef USE_IMAP
298    if (tmp->magic != M_IMAP)
299#endif
300    tmp->new = 0;
301
302#ifdef USE_IMAP
303    if (tmp->magic != M_IMAP)
304    {
305#endif
306#ifdef USE_POP
307    if (mx_is_pop (tmp->path))
308      tmp->magic = M_POP;
309    else
310#endif
311    if (stat (tmp->path, &sb) != 0 || sb.st_size == 0 ||
312        (!tmp->magic && (tmp->magic = mx_get_magic (tmp->path)) <= 0))
313    {
314      /* if the mailbox still doesn't exist, set the newly created flag to
315       * be ready for when it does. */
316      tmp->newly_created = 1;
317      tmp->magic = 0;
318      tmp->size = 0;
319      continue;
320    }
321#ifdef USE_IMAP
322    }
323#endif
324
325    /* check to see if the folder is the currently selected folder
326     * before polling */
327    if (!Context || !Context->path ||
328#if defined USE_IMAP || defined USE_POP
329        ((
330#ifdef USE_IMAP
331        tmp->magic == M_IMAP
332#endif
333#ifdef USE_POP
334#ifdef USE_IMAP
335        ||
336#endif
337        tmp->magic == M_POP
338#endif
339        ) ? mutt_strcmp (tmp->path, Context->path) :
340#endif
341         (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino)
342#if defined USE_IMAP || defined USE_POP 
343            )
344#endif
345        )
346       
347    {
348      switch (tmp->magic)
349      {
350      case M_MBOX:
351      case M_MMDF:
352
353        if (STAT_CHECK)
354        {
355          BuffyCount++;
356          tmp->new = 1;
357        }
358        else if (option(OPTCHECKMBOXSIZE))
359        {
360          /* some other program has deleted mail from the folder */
361          tmp->size = (off_t) sb.st_size;
362        }
363        if (tmp->newly_created &&
364            (sb.st_ctime != sb.st_mtime || sb.st_ctime != sb.st_atime))
365          tmp->newly_created = 0;
366
367        break;
368
369      case M_MAILDIR:
370
371        snprintf (path, sizeof (path), "%s/new", tmp->path);
372        if ((dirp = opendir (path)) == NULL)
373        {
374          tmp->magic = 0;
375          break;
376        }
377        while ((de = readdir (dirp)) != NULL)
378        {
379          char *p;
380          if (*de->d_name != '.' && 
381              (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T')))
382          {
383            /* one new and undeleted message is enough */
384            BuffyCount++;
385            tmp->new = 1;
386            break;
387          }
388        }
389        closedir (dirp);
390        break;
391
392      case M_MH:
393        if ((tmp->new = mh_buffy (tmp->path)) > 0)
394          BuffyCount++;
395        break;
396      }
397    }
398    else if (option(OPTCHECKMBOXSIZE) && Context && Context->path)
399      tmp->size = (off_t) sb.st_size;   /* update the size of current folder */
400
401    if (!tmp->new)
402      tmp->notified = 0;
403    else if (!tmp->notified)
404      BuffyNotify++;
405  }
406
407  BuffyDoneTime = BuffyTime;
408  return (BuffyCount);
409}
410
411int mutt_buffy_list (void)
412{
413  BUFFY *tmp;
414  char path[_POSIX_PATH_MAX];
415  char buffylist[2*STRING];
416  int pos;
417  int first;
418
419  int have_unnotified = BuffyNotify;
420 
421  pos = 0;
422  first = 1;
423  buffylist[0] = 0;
424  pos += strlen (strncat (buffylist, _("New mail in "), sizeof (buffylist) - 1 - pos)); /* __STRNCAT_CHECKED__ */
425  for (tmp = Incoming; tmp; tmp = tmp->next)
426  {
427    /* Is there new mail in this mailbox? */
428    if (!tmp->new || (have_unnotified && tmp->notified))
429      continue;
430
431    strfcpy (path, tmp->path, sizeof (path));
432    mutt_pretty_mailbox (path, sizeof (path));
433   
434    if (!first && pos + strlen (path) >= COLS - 7)
435      break;
436   
437    if (!first)
438      pos += strlen (strncat(buffylist + pos, ", ", sizeof(buffylist)-1-pos)); /* __STRNCAT_CHECKED__ */
439
440    /* Prepend an asterisk to mailboxes not already notified */
441    if (!tmp->notified)
442    {
443      /* pos += strlen (strncat(buffylist + pos, "*", sizeof(buffylist)-1-pos));  __STRNCAT_CHECKED__ */
444      tmp->notified = 1;
445      BuffyNotify--;
446    }
447    pos += strlen (strncat(buffylist + pos, path, sizeof(buffylist)-1-pos)); /* __STRNCAT_CHECKED__ */
448    first = 0;
449  }
450  if (!first && tmp)
451  {
452    strncat (buffylist + pos, ", ...", sizeof (buffylist) - 1 - pos); /* __STRNCAT_CHECKED__ */
453  }
454  if (!first)
455  {
456    mutt_message ("%s", buffylist);
457    return (1);
458  }
459  /* there were no mailboxes needing to be notified, so clean up since
460   * BuffyNotify has somehow gotten out of sync
461   */
462  BuffyNotify = 0;
463  return (0);
464}
465
466int mutt_buffy_notify (void)
467{
468  if (mutt_buffy_check (0) && BuffyNotify)
469  {
470    return (mutt_buffy_list ());
471  }
472  return (0);
473}
474
475/*
476 * mutt_buffy() -- incoming folders completion routine
477 *
478 * given a folder name, this routine gives the next incoming folder with new
479 * new mail.
480 */
481void mutt_buffy (char *s, size_t slen)
482{
483  int count;
484  BUFFY *tmp = Incoming;
485
486  mutt_expand_path (s, _POSIX_PATH_MAX);
487  switch (mutt_buffy_check (0))
488  {
489  case 0:
490
491    *s = '\0';
492    break;
493
494  case 1:
495
496    while (tmp && !tmp->new)
497      tmp = tmp->next;
498    if (!tmp)
499    {
500      *s = '\0';
501      mutt_buffy_check (1); /* buffy was wrong - resync things */
502      break;
503    }
504    strfcpy (s, tmp->path, slen);
505    mutt_pretty_mailbox (s, slen);
506    break;
507
508  default:
509   
510    count = 0;
511    while (count < 3)
512    {
513      if (mutt_strcmp (s, tmp->path) == 0)
514        count++;
515      else if (count && tmp->new)
516        break;
517      tmp = tmp->next;
518      if (!tmp)
519      {
520        tmp = Incoming;
521        count++;
522      }
523    }
524    if (count >= 3)
525    {
526      *s = '\0';
527      mutt_buffy_check (1); /* buffy was wrong - resync things */
528      break;
529    }
530    strfcpy (s, tmp->path, slen);
531    mutt_pretty_mailbox (s, slen);
532    break;
533  }
534}
Note: See TracBrowser for help on using the browser.