0c3b4b364c4c2ab9b605c3c773321a1d4f1ac7f0
[apps/madmutt.git] / buffy.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * Parts were written/modified by:
6  * Rocco Rutte <pdmef@cs.tu-berlin.de>
7  *
8  * This file is part of mutt-ng, see http://www.muttng.org/.
9  * It's licensed under the GNU General Public License,
10  * please see the file GPL in the top level source directory.
11  */
12
13 #include <lib-lib/lib-lib.h>
14 #include <dirent.h>
15 #include <utime.h>
16
17 #include <lib-ui/curses.h>
18 #include <lib-ui/sidebar.h>
19 #include <lib-mx/mx.h>
20 #include <lib-mx/mh.h>
21
22 #include <imap/imap.h>
23
24 #include "mutt.h"
25 #include "buffy.h"
26
27 static time_t BuffyTime = 0;    /* last time we started checking for mail */
28
29 static time_t ImapBuffyTime = 0;        /* last time we started checking for mail */
30 static short BuffyCount = 0;    /* how many boxes with new mail */
31 static short BuffyNotify = 0;   /* # of unnotified new boxes */
32
33 static inline void buffy_delete(BUFFY** p)
34 {
35     p_delete(&(*p)->path);
36     p_delete(p);
37 }
38
39 DO_ARRAY_FUNCS(BUFFY, buffy, buffy_delete);
40
41 /* Return the index number of path in Incoming list */
42 int buffy_lookup (const char* path) {
43   int i = 0;
44
45   if (!Incoming.len || !path || !*path)
46     return (-1);
47
48   for (i = 0; i < Incoming.len; i++) {
49     if (!m_strcmp(Incoming.arr[i]->path, path))
50       return (i);
51   }
52
53   return (-1);
54 }
55
56 int buffy_parse_mailboxes (BUFFER * path, BUFFER * s, unsigned long data,
57                           BUFFER * err __attribute__ ((unused)))
58 {
59   BUFFY* tmp;
60   char buf[_POSIX_PATH_MAX];
61   int i = 0;
62
63   while (MoreArgs (s)) {
64     mutt_extract_token (path, s, 0);
65     m_strcpy(buf, sizeof(buf), path->data);
66
67     if (data == M_UNMAILBOXES && !strcmp(buf, "*")) {
68       buffy_array_wipe(&Incoming);
69       return 0;
70     }
71
72     /* Skip empty tokens. */
73     if (!*buf)
74       continue;
75
76     mutt_expand_path (buf, sizeof (buf));
77     i = buffy_lookup (buf);
78
79     if (data == M_UNMAILBOXES) {
80       tmp = buffy_array_take(&Incoming, i);
81       buffy_delete(&tmp);
82       continue;
83     }
84
85     if (i < 0) {
86       tmp = p_new(BUFFY, 1);
87       tmp->path = m_strdup(buf);
88       tmp->magic = 0;
89       buffy_array_append(&Incoming, tmp);
90       i = Incoming.len-1;
91     } else {
92       tmp = Incoming.arr[i];
93     }
94
95     tmp->new = 0;
96     tmp->notified = 1;
97     tmp->newly_created = 0;
98
99   }
100   return 0;
101 }
102
103 #define STAT_CHECK (sb.st_mtime > sb.st_atime || (tmp->newly_created && sb.st_ctime == sb.st_mtime && sb.st_ctime == sb.st_atime))
104
105 /* values for force:
106  * 0    don't force any checks + update sidebar
107  * 1    force all checks + update sidebar
108  * 2    don't force any checks + _don't_ update sidebar
109  */
110 int buffy_check (int force)
111 {
112   BUFFY *tmp;
113   struct stat sb;
114   struct dirent *de;
115   DIR *dirp;
116   char path[_POSIX_PATH_MAX];
117   struct stat contex_sb;
118   time_t now, last1;
119   CONTEXT *ctx;
120   int i = 0;
121   int local = 0, count = 0;
122   time_t last2;
123
124   /* update postponed count as well, on force */
125   if (force == 1)
126     mutt_update_num_postponed ();
127
128   /* fastest return if there are no mailboxes */
129   if (!Incoming.len)
130     return 0;
131   now = time (NULL);
132   if (force == 0 && (now - BuffyTime < BuffyTimeout)
133       && (now - ImapBuffyTime < ImapBuffyTimeout))
134     return BuffyCount;
135
136   last1 = BuffyTime;
137   if (force == 1 || now - BuffyTime >= BuffyTimeout)
138     BuffyTime = now;
139   last2 = ImapBuffyTime;
140   if (force == 1 || now - ImapBuffyTime >= ImapBuffyTimeout)
141     ImapBuffyTime = now;
142   BuffyCount = 0;
143   BuffyNotify = 0;
144
145   count = sidebar_need_count ();
146
147   if (!Context || !Context->path || 
148       (mx_is_local (Context->magic-1) && stat (Context->path, &contex_sb) != 0)) {
149     /* check device ID and serial number instead of comparing paths */
150     contex_sb.st_dev = 0;
151     contex_sb.st_ino = 0;
152   }
153
154   for (i = 0; i < Incoming.len; i++) {
155     tmp = Incoming.arr[i];
156     tmp->magic = mx_get_magic (tmp->path);
157     local = mx_is_local (tmp->magic-1);
158     if ((tmp->magic <= 0 || local) && (stat (tmp->path, &sb) != 0 || sb.st_size == 0)) {
159       /* if the mailbox still doesn't exist, set the newly created flag to
160        * be ready for when it does. */
161       tmp->newly_created = 1;
162       tmp->magic = -1;
163       continue;
164     }
165
166     /* check to see if the folder is the currently selected folder
167      * before polling */
168     if (!Context || !Context->path || (local ? (sb.st_dev != contex_sb.st_dev ||
169                                                 sb.st_ino != contex_sb.st_ino) : 
170                                        m_strcmp(tmp->path, Context->path))) {
171       switch (tmp->magic) {
172       case M_MBOX:
173       case M_MMDF:
174         /* only check on force or $mail_check reached */
175         if (force == 1 || (now - last1 >= BuffyTimeout)) {
176           if (!count) {
177             if (STAT_CHECK) {
178               BuffyCount++;
179               tmp->new = 1;
180             }
181           }
182           else if (STAT_CHECK || tmp->msgcount == 0) {
183             /* sidebar visible */
184             BuffyCount++;
185             if ((ctx =
186                  mx_open_mailbox (tmp->path, M_READONLY | M_QUIET | M_NOSORT | M_COUNT,
187                                   NULL)) != NULL) {
188               tmp->msgcount = ctx->msgcount;
189               tmp->new = ctx->new;
190               tmp->msg_unread = ctx->new;       /* for sidebar, wtf? */
191               tmp->msg_flagged = ctx->flagged;
192               mx_close_mailbox (ctx, 0);
193             }
194           }
195           if (tmp->newly_created &&
196               (sb.st_ctime != sb.st_mtime || sb.st_ctime != sb.st_atime))
197             tmp->newly_created = 0;
198         }
199         else if (tmp->new > 0)
200           BuffyCount++;
201         break;
202
203       case M_MAILDIR:
204         /* only check on force or $mail_check reached */
205         if (force == 1 || (now - last1 >= BuffyTimeout)) {
206           snprintf (path, sizeof (path), "%s/new", tmp->path);
207           if ((dirp = opendir (path)) == NULL) {
208             tmp->magic = 0;
209             break;
210           }
211           tmp->new = 0;
212           tmp->msg_unread = 0;
213           tmp->msgcount = 0;
214           while ((de = readdir (dirp)) != NULL) {
215             char *p;
216
217             if (*de->d_name != '.' &&
218                 (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T'))) {
219               /* one new and undeleted message is enough */
220               if (tmp->new == 0) {
221                 BuffyCount++;
222                 if (!count) {
223                   /* if sidebar invisible -> done */
224                   tmp->new = 1;
225                   break;
226                 }
227               }
228               tmp->msgcount++;
229               tmp->msg_unread++;
230               tmp->new++;
231             }
232           }
233           closedir (dirp);
234
235           if (count) {
236             /* only count total mail if sidebar visible */
237             snprintf (path, sizeof (path), "%s/cur", tmp->path);
238             if ((dirp = opendir (path)) == NULL) {
239               tmp->magic = 0;
240               break;
241             }
242             tmp->msg_flagged = 0;
243             while ((de = readdir (dirp)) != NULL) {
244               char *p;
245
246               if (*de->d_name != '.'
247                   && (p = strstr (de->d_name, ":2,")) != NULL) {
248                 if (!strchr (p + 3, 'T'))
249                   tmp->msgcount++;
250                 if (strchr (p + 3, 'F'))
251                   tmp->msg_flagged++;
252               }
253             }
254             closedir (dirp);
255           }
256         }
257         else if (tmp->new > 0)
258           /* keep current stats if !force and !$mail_check reached */
259           BuffyCount++;
260         break;
261
262       case M_MH:
263         /* only check on force or $mail_check reached */
264         if (force == 1 || (now - last1 >= BuffyTimeout)) {
265           if ((tmp->new = mh_buffy (tmp->path)) > 0)
266             BuffyCount++;
267           if (count) {
268             DIR *dp;
269
270             if ((dp = opendir (path)) == NULL)
271               break;
272             tmp->new = 0;
273             tmp->msgcount = 0;
274             tmp->msg_unread = 0;
275             while ((de = readdir (dp))) {
276               if (mh_valid_message (de->d_name)) {
277                 tmp->msgcount++;
278                 tmp->msg_unread++;
279                 tmp->new++;
280               }
281             }
282             closedir (dp);
283           }
284         }
285         else if (tmp->new > 0)
286           /* keep current stats if !force and !$mail_check reached */
287           BuffyCount++;
288         break;
289
290       case M_IMAP:
291         /* only check on force or $imap_mail_check reached */
292         if (force == 1 || (now - last2 >= ImapBuffyTimeout)) {
293           tmp->msgcount = imap_mailbox_check (tmp->path, 0);
294           tmp->new = imap_mailbox_check (tmp->path, 1);
295           tmp->msg_unread = imap_mailbox_check (tmp->path, 2);
296           if (tmp->new > 0)
297             BuffyCount++;
298           else
299             tmp->new = 0;
300           if (tmp->msg_unread < 0)
301             tmp->msg_unread = 0;
302         }
303         else if (tmp->new > 0)
304           /* keep current stats if !force and !$imap_mail_check reached */
305           BuffyCount++;
306         break;
307
308       }
309     }
310
311     if (tmp->new <= 0)
312       tmp->notified = 0;
313     else if (!tmp->notified)
314       BuffyNotify++;
315     tmp->has_new = tmp->new > 0;
316   }
317   if (BuffyCount > 0 && force != 2)
318     sidebar_draw ();
319   return (BuffyCount);
320 }
321
322 int buffy_list (void)
323 {
324   BUFFY *tmp;
325   char path[_POSIX_PATH_MAX];
326   char buffylist[160];
327   int pos;
328   int first;
329   int have_unnotified = BuffyNotify;
330   int i = 0;
331
332   pos = 0;
333   first = 1;
334   buffylist[0] = 0;
335   pos += m_strlen(strncat (buffylist, _("New mail in "), sizeof (buffylist) - 1 - pos)); /* __STRNCAT_CHECKED__ */
336   for (i = 0; i < Incoming.len; i++) {
337     tmp = Incoming.arr[i];
338     /* Is there new mail in this mailbox? */
339     if (tmp->new <= 0 || (have_unnotified && tmp->notified))
340       continue;
341
342     m_strcpy(path, sizeof(path), tmp->path);
343     mutt_pretty_mailbox (path);
344
345     if (!first && pos + m_strlen(path) >= COLS - 7)
346       break;
347
348     if (!first)
349       pos += m_strlen(strncat (buffylist + pos, ", ", sizeof (buffylist) - 1 - pos));    /* __STRNCAT_CHECKED__ */
350
351     tmp->notified = 1;
352     BuffyNotify--;
353
354     pos += m_strlen(strncat (buffylist + pos, path, sizeof (buffylist) - 1 - pos));      /* __STRNCAT_CHECKED__ */
355     first = 0;
356   }
357
358   if (!first && i < Incoming.len) {
359     strncat (buffylist + pos, ", ...", sizeof (buffylist) - 1 - pos);   /* __STRNCAT_CHECKED__ */
360   }
361   if (!first) {
362     /* on new mail: redraw sidebar */
363     sidebar_draw ();
364     mutt_message ("%s", buffylist);
365     return (1);
366   }
367   /* there were no mailboxes needing to be notified, so clean up since 
368    * BuffyNotify has somehow gotten out of sync
369    */
370   BuffyNotify = 0;
371   return (0);
372 }
373
374 int buffy_notify (void)
375 {
376   if (buffy_check (0) && BuffyNotify) {
377     return (buffy_list ());
378   }
379   return (0);
380 }
381
382 /* 
383  * buffy_next() -- incoming folders completion routine
384  *
385  * given a folder name, this routine gives the next incoming folder with new
386  * new mail.
387  */
388 void buffy_next (char *s, size_t slen)
389 {
390   int l = 0;
391   int c = 0, i = 0;
392
393   mutt_expand_path (s, _POSIX_PATH_MAX);
394   if (!buffy_check (0)) {
395       *s = '\0';
396       return;
397     }
398
399   /*
400    * buffy_lookup returns the index,
401    * or -1 if not found (-1..Incoming.len-1);
402    * plus one --> (0..Incoming.len).
403    * Modulo mapps it into the correct range.
404    */
405   i = 1 + buffy_lookup (s);
406   for (l = 0; l < Incoming.len; l++) {
407     c = (l+i) % Incoming.len;
408
409     if (m_strcmp(Incoming.arr[c]->path, s) && Incoming.arr[c]->new > 0)
410       break;
411   }
412   if (l >= Incoming.len) {
413     *s = '\0';
414     /* something went wrong since we're here when buffy_check
415      * reported new mail */
416     buffy_check (1);
417   } else {
418     m_strcpy(s, slen, Incoming.arr[c]->path);
419     mutt_pretty_mailbox (s);
420   }
421 }