Enable constructors/destructors.
[apps/madmutt.git] / buffy.cpkg
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-lua/lib-lua.h>
18 #include <lib-ui/lib-ui.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 @import "lib-lua/base.cpkg"
27
28 static time_t BuffyTime = 0;      /* last time we started checking for mail */
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 buffy_array   Incoming;
33
34 @package mod_buffy {
35     /*
36      ** .pp
37      ** This variable configures how often (in seconds) Madmutt should look for
38      ** new mail.
39      ** .pp
40      ** \fBNote:\fP This does not apply to IMAP mailboxes, see $$imap_mail_check.
41      */
42     int mail_check = 5;
43
44     void mailboxes(const string_t s) {
45         buffy_do_mailboxes(s, 1);
46         RETURN();
47     };
48
49     void unmailboxes(const string_t s) {
50         buffy_do_mailboxes(s, 0);
51         RETURN();
52     };
53 };
54
55 void buffy_do_mailboxes(const char *s, int add)
56 {
57     char buf[_POSIX_PATH_MAX];
58     BUFFY *tmp;
59     int i;
60
61     if (!m_strcmp(s, "*")) {
62         buffy_array_wipe(&Incoming);
63         return;
64     }
65
66     if (m_strisempty(s))
67         return;
68
69     _mutt_expand_path(buf, sizeof(buf), s, 0);
70     i = buffy_lookup(buf);
71
72     if (!add) {
73         tmp = buffy_array_take(&Incoming, i);
74         buffy_delete(&tmp);
75         return;
76     }
77
78     if (i < 0) {
79         tmp = p_new(BUFFY, 1);
80         tmp->path = m_strdup(buf);
81         buffy_array_append(&Incoming, tmp);
82     } else {
83         tmp = Incoming.arr[i];
84     }
85
86     tmp->new = 0;
87     tmp->notified = 1;
88     tmp->newly_created = 0;
89 }
90
91 /* Return the index number of path in Incoming list */
92 int buffy_lookup(const char* path)
93 {
94     int i;
95
96     if (m_strisempty(path))
97         return -1;
98
99     for (i = 0; i < Incoming.len; i++) {
100         if (!m_strcmp(Incoming.arr[i]->path, path))
101             return i;
102     }
103
104     return -1;
105 }
106
107 #define STAT_CHECK (sb.st_mtime > sb.st_atime || (tmp->newly_created && sb.st_ctime == sb.st_mtime && sb.st_ctime == sb.st_atime))
108
109 /* values for force:
110  * 0    don't force any checks + update sidebar
111  * 1    force all checks + update sidebar
112  * 2    don't force any checks + _don't_ update sidebar
113  */
114 int buffy_check(int force)
115 {
116     struct stat sb;
117     struct dirent *de;
118     DIR *dirp;
119     char path[_POSIX_PATH_MAX];
120     struct stat contex_sb;
121     time_t now, last1, last2;
122     CONTEXT *ctx;
123     int i = 0, local = 0;
124
125     /* update postponed count as well, on force */
126     if (force == 1)
127         mutt_update_num_postponed ();
128
129     /* fastest return if there are no mailboxes */
130     if (!Incoming.len)
131         return 0;
132
133     now = time (NULL);
134     if (force == 0 && (now - BuffyTime < mod_buffy.mail_check)
135         && (now - ImapBuffyTime < ImapBuffyTimeout))
136         return BuffyCount;
137
138     last1 = BuffyTime;
139     if (force == 1 || now - BuffyTime >= mod_buffy.mail_check)
140         BuffyTime = now;
141     last2 = ImapBuffyTime;
142     if (force == 1 || now - ImapBuffyTime >= ImapBuffyTimeout)
143         ImapBuffyTime = now;
144     BuffyCount = 0;
145     BuffyNotify = 0;
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         BUFFY *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                 /* only check on force or $mail_check reached */
174                 if (force == 1 || (now - last1 >= mod_buffy.mail_check)) {
175                     if (!sidebar_w) {
176                         if (STAT_CHECK) {
177                             BuffyCount++;
178                             tmp->new = 1;
179                         }
180                     }
181                     else if (STAT_CHECK || tmp->msgcount == 0) {
182                         /* sidebar visible */
183                         BuffyCount++;
184                         if ((ctx =
185                              mx_open_mailbox (tmp->path, M_READONLY | M_QUIET | M_NOSORT | M_COUNT,
186                                               NULL)) != NULL) {
187                             tmp->msgcount = ctx->msgcount;
188                             tmp->new = ctx->new;
189                             tmp->msg_unread = ctx->new;       /* for sidebar, wtf? */
190                             tmp->msg_flagged = ctx->flagged;
191                             mx_close_mailbox (ctx, 0);
192                         }
193                     }
194                     if (tmp->newly_created &&
195                         (sb.st_ctime != sb.st_mtime || sb.st_ctime != sb.st_atime))
196                         tmp->newly_created = 0;
197                 }
198                 else if (tmp->new > 0)
199                     BuffyCount++;
200                 break;
201
202               case M_MAILDIR:
203                 /* only check on force or $mail_check reached */
204                 if (force == 1 || (now - last1 >= mod_buffy.mail_check)) {
205                     snprintf (path, sizeof (path), "%s/new", tmp->path);
206                     if ((dirp = opendir (path)) == NULL) {
207                         tmp->magic = 0;
208                         break;
209                     }
210                     tmp->new = 0;
211                     tmp->msg_unread = 0;
212                     tmp->msgcount = 0;
213                     while ((de = readdir (dirp)) != NULL) {
214                         char *p;
215
216                         if (*de->d_name != '.' &&
217                             (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T'))) {
218                             /* one new and undeleted message is enough */
219                             if (tmp->new == 0) {
220                                 BuffyCount++;
221                                 if (!sidebar_w) {
222                                     /* if sidebar invisible -> done */
223                                     tmp->new = 1;
224                                     break;
225                                 }
226                             }
227                             tmp->msgcount++;
228                             tmp->msg_unread++;
229                             tmp->new++;
230                         }
231                     }
232                     closedir (dirp);
233
234                     if (sidebar_w) {
235                         /* only count total mail if sidebar visible */
236                         snprintf (path, sizeof (path), "%s/cur", tmp->path);
237                         if ((dirp = opendir (path)) == NULL) {
238                             tmp->magic = 0;
239                             break;
240                         }
241                         tmp->msg_flagged = 0;
242                         while ((de = readdir (dirp)) != NULL) {
243                             char *p;
244
245                             if (*de->d_name != '.'
246                                 && (p = strstr (de->d_name, ":2,")) != NULL) {
247                                 if (!strchr (p + 3, 'T'))
248                                     tmp->msgcount++;
249                                 if (strchr (p + 3, 'F'))
250                                     tmp->msg_flagged++;
251                             }
252                         }
253                         closedir (dirp);
254                     }
255                 }
256                 else if (tmp->new > 0)
257                     /* keep current stats if !force and !$mail_check reached */
258                     BuffyCount++;
259                 break;
260
261               case M_MH:
262                 /* only check on force or $mail_check reached */
263                 if (force == 1 || (now - last1 >= mod_buffy.mail_check)) {
264                     if ((tmp->new = mh_buffy (tmp->path)) > 0)
265                         BuffyCount++;
266                     if (sidebar_w) {
267                         DIR *dp;
268
269                         if ((dp = opendir (path)) == NULL)
270                             break;
271                         tmp->new = 0;
272                         tmp->msgcount = 0;
273                         tmp->msg_unread = 0;
274                         while ((de = readdir (dp))) {
275                             if (mh_valid_message (de->d_name)) {
276                                 tmp->msgcount++;
277                                 tmp->msg_unread++;
278                                 tmp->new++;
279                             }
280                         }
281                         closedir (dp);
282                     }
283                 }
284                 else if (tmp->new > 0)
285                     /* keep current stats if !force and !$mail_check reached */
286                     BuffyCount++;
287                 break;
288
289               case M_IMAP:
290                 /* only check on force or $imap_mail_check reached */
291                 if (force == 1 || (now - last2 >= ImapBuffyTimeout)) {
292                     tmp->msgcount = imap_mailbox_check (tmp->path, 0);
293                     tmp->new = imap_mailbox_check (tmp->path, 1);
294                     tmp->msg_unread = imap_mailbox_check (tmp->path, 2);
295                     if (tmp->new > 0)
296                         BuffyCount++;
297                     else
298                         tmp->new = 0;
299                     if (tmp->msg_unread < 0)
300                         tmp->msg_unread = 0;
301                 }
302                 else if (tmp->new > 0)
303                     /* keep current stats if !force and !$imap_mail_check reached */
304                     BuffyCount++;
305                 break;
306
307             }
308         }
309
310         if (tmp->new <= 0)
311             tmp->notified = 0;
312         else if (!tmp->notified)
313             BuffyNotify++;
314         tmp->has_new = tmp->new > 0;
315     }
316     if (BuffyCount > 0 && force != 2)
317         sidebar_draw ();
318     return BuffyCount;
319 }
320
321 int buffy_list (void)
322 {
323     char buffylist[LONG_STRING];
324     int have_unnotified = BuffyNotify;
325     int pos = 0, first = 1, i;
326
327     pos = m_strcpy(buffylist, sizeof(buffylist), _("New mail in "));
328
329     for (i = 0; i < Incoming.len; i++) {
330         char path[_POSIX_PATH_MAX];
331         BUFFY *tmp = Incoming.arr[i];
332
333         /* Is there new mail in this mailbox? */
334         if (tmp->new <= 0 || (have_unnotified && tmp->notified))
335             continue;
336
337         m_strcpy(path, sizeof(path), tmp->path);
338         mutt_pretty_mailbox(path);
339
340         if (!first) {
341             if (pos + m_strlen(path) >= COLS - 7)
342                 break;
343             pos += m_strcpy(buffylist + pos, sizeof(buffylist) - pos,  ", ");
344         }
345
346         tmp->notified = 1;
347         BuffyNotify--;
348
349         pos += m_strcpy(buffylist + pos, sizeof(buffylist) - pos, path);
350         first = 0;
351     }
352
353     if (!first) {
354         /* on new mail: redraw sidebar */
355         sidebar_draw();
356
357         if (i < Incoming.len) {
358             mutt_message("%s, ...", buffylist);
359         } else {
360             mutt_message("%s", buffylist);
361         }
362         return 1;
363     }
364
365     /* there were no mailboxes needing to be notified, so clean up since
366        BuffyNotify has somehow gotten out of sync */
367     BuffyNotify = 0;
368     return 0;
369 }
370
371 int buffy_notify(void)
372 {
373     return buffy_check(0) && BuffyNotify ? buffy_list() : 0;
374 }
375
376 /*
377  * buffy_next() -- incoming folders completion routine
378  *
379  * given a folder name, this routine gives the next incoming folder with new
380  * new mail.
381  */
382 void buffy_next(char *s, size_t slen)
383 {
384     int l = 0;
385     int c = 0, i = 0;
386
387     mutt_expand_path(s, _POSIX_PATH_MAX);
388     if (!buffy_check(0)) {
389         *s = '\0';
390         return;
391     }
392
393     /*
394      * buffy_lookup returns the index,
395      * or -1 if not found (-1..Incoming.len-1);
396      * plus one --> (0..Incoming.len).
397      * Modulo mapps it into the correct range.
398      */
399     i = 1 + buffy_lookup(s);
400     for (l = 0; l < Incoming.len; l++) {
401         c = (l+i) % Incoming.len;
402
403         if (m_strcmp(Incoming.arr[c]->path, s) && Incoming.arr[c]->new > 0)
404             break;
405     }
406
407     if (l >= Incoming.len) {
408         *s = '\0';
409         /* something went wrong since we're here when buffy_check
410          * reported new mail */
411         buffy_check(1);
412     } else {
413         m_strcpy(s, slen, Incoming.arr[c]->path);
414         mutt_pretty_mailbox(s);
415     }
416 }
417
418 /* vim:set ft=c: */