* Create help for explaining how encrypted password file support
[alpine.git] / pith / newmail.c
bloba79656e84fe4f8f5e4f33b27fccf2951d319911e
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: newmail.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2014 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/newmail.h"
21 #include "../pith/conf.h"
22 #include "../pith/flag.h"
23 #include "../pith/mailindx.h"
24 #include "../pith/msgno.h"
25 #include "../pith/bldaddr.h"
26 #include "../pith/stream.h"
27 #include "../pith/sort.h"
28 #include "../pith/status.h"
29 #include "../pith/util.h"
30 #include "../pith/thread.h"
31 #include "../pith/options.h"
32 #include "../pith/folder.h"
33 #include "../pith/ablookup.h"
35 #ifdef _WINDOWS
36 #include "../pico/osdep/mswin.h"
37 #endif
41 * Hooks for various stuff
43 void (*pith_opt_newmail_announce)(MAILSTREAM *, long, long);
44 void (*pith_opt_newmail_check_cue)(int);
45 void (*pith_opt_checkpoint_cue)(int);
46 void (*pith_opt_icon_text)(char *, int);
49 void fixup_flags(MAILSTREAM *, MSGNO_S *);
50 int check_point(MAILSTREAM *, CheckPointTime, int);
53 /*----------------------------------------------------------------------
54 new_mail() - check for new mail in the inbox
56 Input: force -- flag indicating we should check for new mail no matter
57 time_for_check_point -- 0: GoodTime, 1: BadTime, 2: VeryBadTime
58 flags -- whether to q a new mail status message or defer the sort
60 Result: returns -1 if there was no new mail. Otherwise returns the
61 sorted message number of the smallest message number that
62 was changed. That is the screen needs to be repainted from
63 that message down.
65 Limit frequency of checks because checks use some resources. That is
66 they generate an IMAP packet or require locking the local mailbox.
67 (Acutally the lock isn't required, a stat will do, but the current
68 c-client mail code locks before it stats.)
70 Returns >= 0 only if there is a change in the given mail stream. Otherwise
71 this returns -1. On return the message counts in the pine
72 structure are updated to reflect the current number of messages including
73 any new mail and any expunging.
75 --- */
76 long
77 new_mail(int force_arg, CheckPointTime time_for_check_point, int flags)
79 static time_t last_check_point_call = 0;
80 long since_last_input;
81 time_t expunged_reaper_to, adj_idle_timeout, interval, adj;
82 static int nexttime = 0;
83 time_t now;
84 long n, rv = 0, t_nm_count = 0, exp_count;
85 MAILSTREAM *m;
86 int force, i, started_on;
87 int new_mail_was_announced = 0;
88 int have_pinged_non_special = 0;
89 int timeo;
91 dprint((9, "new mail called (force=%d %s flags=0x%x)\n",
92 force_arg,
93 time_for_check_point == GoodTime ? "GoodTime" :
94 time_for_check_point == BadTime ? "BadTime" :
95 time_for_check_point == VeryBadTime ? "VeryBad" :
96 time_for_check_point == DoItNow ? "DoItNow" : "?",
97 flags));
99 force = force_arg;
101 now = time(0);
103 timeo = get_input_timeout();
105 if(time_for_check_point == GoodTime)
106 adrbk_maintenance();
108 if(time_for_check_point == GoodTime || force_arg)
109 folder_unseen_count_updater(UFU_ANNOUNCE | (force_arg ? UFU_FORCE : 0));
111 if(sp_need_to_rethread(ps_global->mail_stream))
112 force = 1;
114 if(!force && sp_unsorted_newmail(ps_global->mail_stream))
115 force = !(flags & NM_DEFER_SORT);
117 if(!ps_global->mail_stream
118 || !(timeo || force || sp_a_locked_stream_changed()))
119 return(-1);
121 last_check_point_call = now;
122 since_last_input = (long) now - (long) time_of_last_input();
125 * We have this for loop followed by the do-while so that we will prefer
126 * to ping the active streams before we ping the inactive ones, in cases
127 * where the pings or checks are taking a long time.
129 for(i = 0; i < ps_global->s_pool.nstream; i++){
130 m = ps_global->s_pool.streams[i];
131 if(!m || m->halfopen ||
132 (m != ps_global->mail_stream &&
133 !(force_arg && sp_flagged(m, SP_LOCKED))))
134 continue;
137 * This for() loop is only the current folder, unless the user
138 * has forced the check, in which case it is all locked folders.
142 * After some amount of inactivity on a stream, the server may
143 * close the stream. Each protocol has its own idea of how much
144 * inactivity should be allowed before the stream is closed. For
145 * example, IMAP specifies that the server should not close the
146 * stream unilaterally until at least 30 minutes of inactivity.
147 * The GET_IDLETIMEOUT call gives us that time in minutes. We
148 * want to be sure to keep the stream alive if it is about to die
149 * due to inactivity.
151 adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL);
152 if(adj_idle_timeout <= 0)
153 adj_idle_timeout = 600;
155 adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE :
156 (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE :
157 (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE;
158 adj_idle_timeout = MAX(adj_idle_timeout - adj, 120);
161 * Set interval to mail-check-interval unless
162 * mail-check-interval-noncurrent is nonzero and this is not inbox
163 * or current stream.
165 if(ps_global->check_interval_for_noncurr > 0
166 && m != ps_global->mail_stream
167 && !sp_flagged(m, SP_INBOX))
168 interval = ps_global->check_interval_for_noncurr;
169 else
170 interval = timeo;
173 * We want to make sure that we notice expunges, but we don't have
174 * to be fanatical about it. Every once in a while we'll do a ping
175 * because we haven't had a command that notices expunges for a
176 * while. It's also a larger number than interval so it gives us a
177 * convenient interval to do slower pinging than interval if we
178 * are busy.
180 if(interval <= adj_idle_timeout)
181 expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout);
182 else
183 expunged_reaper_to = interval;
186 * User may want to avoid new mail checking while composing.
187 * In this case we will only do the keepalives.
189 if(timeo == 0
190 || (flags & NM_FROM_COMPOSER
191 && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global)
192 && !sp_flagged(m, SP_INBOX))
194 (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global)
195 && sp_flagged(m, SP_INBOX))))){
196 interval = expunged_reaper_to = 0;
199 dprint((9,
200 "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n",
201 STREAMNAME(m), force_arg, (long) interval,
202 (long) expunged_reaper_to, (long) adj_idle_timeout));
203 dprint((9,
204 " since_last_ping=%ld since_last_reap=%ld\n",
205 (long) (now - sp_last_ping(m)),
206 (long) (now - sp_last_expunged_reaper(m))));
208 /* if check_point does a check it resets last_ping time */
209 if(force_arg || (timeo && expunged_reaper_to > 0))
210 (void) check_point(m, time_for_check_point, flags);
213 * Remember that unless force_arg is set, this check is really
214 * only for the current folder. This is usually going to fire
215 * on the first test, which is the interval the user set.
217 if(force_arg
219 (timeo
221 ((interval && (now - sp_last_ping(m) >= interval-1))
223 (expunged_reaper_to
224 && (now - sp_last_expunged_reaper(m)) >= expunged_reaper_to)
226 (now - sp_last_ping(m) >= adj_idle_timeout)))){
228 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
229 (*pith_opt_newmail_check_cue)(TRUE);
231 dprint((7, "Mail_Ping(%s): lastping=%ld er=%ld%s%s %s\n",
232 STREAMNAME(m),
233 (long) (now - sp_last_ping(m)),
234 (long) (now - sp_last_expunged_reaper(m)),
235 force_arg ? " [forced]" :
236 interval && (now-sp_last_ping(m) >= interval-1)
237 ? " [it's time]" :
238 expunged_reaper_to
239 && (now - sp_last_expunged_reaper(m) >= expunged_reaper_to)
240 ? " [expunged reaper]"
241 : " [keepalive]",
242 m == ps_global->mail_stream ? " [current]" : "",
243 debug_time(0,1)));
246 * We're about to ping the stream.
247 * If the stream is a #move Mail Drop there is a minimum time
248 * between re-opens of the mail drop to check for new mail.
249 * If the check is forced by the user, they want it to
250 * happen now. We use knowledge of c-client internals to
251 * make this happen.
253 if(force_arg && m && m->snarf.name)
254 m->snarf.time = 0;
256 /*-- Ping the stream to check for new mail --*/
257 if(sp_dead_stream(m)){
258 dprint((6, "no check: stream is dead\n"));
260 else if(!pine_mail_ping(m)){
261 dprint((6, "ping failed: stream is dead\n"));
262 sp_set_dead_stream(m, 1);
265 dprint((7, "Ping complete: %s\n", debug_time(0,1)));
267 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
268 (*pith_opt_newmail_check_cue)(FALSE);
272 if(nexttime < 0 || nexttime >= ps_global->s_pool.nstream)
273 nexttime = 0;
275 i = started_on = nexttime;
276 do {
277 m = ps_global->s_pool.streams[i];
279 nexttime = i;
280 if(ps_global->s_pool.nstream > 0)
281 i = (i + 1) % ps_global->s_pool.nstream;
284 * This do() loop handles all the other streams that weren't covered
285 * in the for() loop above.
287 if((!m || m->halfopen) ||
288 (m == ps_global->mail_stream ||
289 (force_arg && sp_flagged(m, SP_LOCKED)))){
290 nexttime = i;
291 continue;
295 * If it is taking an extra long time to do the pings and checks,
296 * think about skipping some of them. Always do at least one of
297 * these non-special streams (if they are due to be pinged).
298 * The nexttime variable keeps track of where we were so that we
299 * don't always ping the first one in the list and then skip out.
301 if((time(0) - now >= 5) && have_pinged_non_special){
302 dprint((7, "skipping pings due to delay: %s\n",
303 debug_time(0,1)));
304 break;
307 nexttime = i;
308 have_pinged_non_special++;
310 adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL);
311 if(adj_idle_timeout <= 0)
312 adj_idle_timeout = 600;
314 adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE :
315 (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE :
316 (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE;
317 adj_idle_timeout = MAX(adj_idle_timeout - adj, 120);
319 if(ps_global->check_interval_for_noncurr > 0
320 && m != ps_global->mail_stream
321 && !sp_flagged(m, SP_INBOX))
322 interval = ps_global->check_interval_for_noncurr;
323 else
324 interval = timeo;
326 if(interval <= adj_idle_timeout)
327 expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout);
328 else
329 expunged_reaper_to = interval;
331 if(timeo == 0
332 || (flags & NM_FROM_COMPOSER
333 && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global)
334 && !sp_flagged(m, SP_INBOX))
336 (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global)
337 && sp_flagged(m, SP_INBOX))))){
338 interval = expunged_reaper_to = 0;
341 dprint((9,
342 "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n",
343 STREAMNAME(m), force_arg, (long) interval,
344 (long) expunged_reaper_to, (long) adj_idle_timeout));
345 dprint((9,
346 " since_last_ping=%ld since_last_reap=%ld since_last_input=%ld\n",
347 (long) (now - sp_last_ping(m)),
348 (long) (now - sp_last_expunged_reaper(m)),
349 (long) since_last_input));
351 /* if check_point does a check it resets last_ping time */
352 if(force_arg || (timeo && expunged_reaper_to > 0))
353 (void) check_point(m, time_for_check_point, flags);
357 * The check here is a little bit different from the current folder
358 * check in the for() loop above. In particular, we defer our
359 * pinging for awhile if the last input was recent (except for the
360 * inbox!). We ping streams which are cached but not actively being
361 * used (that is, the non-locked streams) at a slower rate.
362 * If we don't use them for a long time we will eventually close them
363 * (in maybe_kill_old_stream()) but we do it when we want to instead
364 * of when the server wants us to by attempting to keep it alive here.
365 * The other reason to ping the cached streams is that we only get
366 * told the new mail in those streams is recent one time, the messages
367 * that weren't handled here will no longer be recent next time
368 * we open the folder.
370 if((force_arg && sp_flagged(m, SP_LOCKED))
372 (timeo
374 ((interval
375 && sp_flagged(m, SP_LOCKED)
376 && ((since_last_input >= 3
377 && (now-sp_last_ping(m) >= interval-1))
378 || (sp_flagged(m, SP_INBOX)
379 && (now-sp_last_ping(m) >= interval-1))))
381 (expunged_reaper_to
382 && sp_flagged(m, SP_LOCKED)
383 && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to))
385 (expunged_reaper_to
386 && !sp_flagged(m, SP_LOCKED)
387 && since_last_input >= 3
388 && (now-sp_last_ping(m) >= expunged_reaper_to))
390 (now - sp_last_ping(m) >= adj_idle_timeout)))){
392 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
393 (*pith_opt_newmail_check_cue)(TRUE);
395 dprint((7,
396 "Mail_Ping(%s): lastping=%ld er=%ld%s idle: %ld %s\n",
397 STREAMNAME(m),
398 (long) (now - sp_last_ping(m)),
399 (long) (now - sp_last_expunged_reaper(m)),
400 (force_arg && sp_flagged(m, SP_LOCKED)) ? " [forced]" :
401 (interval
402 && sp_flagged(m, SP_LOCKED)
403 && ((since_last_input >= 3
404 && (now-sp_last_ping(m) >= interval-1))
405 || (sp_flagged(m, SP_INBOX)
406 && (now-sp_last_ping(m) >= interval-1))))
407 ? " [it's time]" :
408 (expunged_reaper_to
409 && sp_flagged(m, SP_LOCKED)
410 && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to))
411 ? " [expunged reaper]" :
412 (expunged_reaper_to
413 && !sp_flagged(m, SP_LOCKED)
414 && since_last_input >= 3
415 && (now-sp_last_ping(m) >= expunged_reaper_to))
416 ? " [slow ping]" : " [keepalive]",
417 since_last_input,
418 debug_time(0,1)));
421 * We're about to ping the stream.
422 * If the stream is a #move Mail Drop there is a minimum time
423 * between re-opens of the mail drop to check for new mail.
424 * If the check is forced by the user, they want it to
425 * happen now. We use knowledge of c-client internals to
426 * make this happen.
428 if(force_arg && m && m->snarf.name)
429 m->snarf.time = 0;
431 /*-- Ping the stream to check for new mail --*/
432 if(sp_dead_stream(m)){
433 dprint((6, "no check: stream is dead\n"));
435 else if(!pine_mail_ping(m)){
436 dprint((6, "ping failed: stream is dead\n"));
437 sp_set_dead_stream(m, 1);
440 dprint((7, "Ping complete: %s\n", debug_time(0,1)));
442 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
443 (*pith_opt_newmail_check_cue)(FALSE);
445 } while(i != started_on);
448 * Current mail box state changed, could be additions or deletions.
449 * Also check if we need to do sorting that has been deferred.
450 * We handle the current stream separately from the rest in the
451 * similar loop that follows this paragraph.
453 m = ps_global->mail_stream;
454 if(sp_mail_box_changed(m) || sp_unsorted_newmail(m)
455 || sp_need_to_rethread(m)){
456 dprint((7,
457 "Cur new mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n",
458 (m && m->mailbox) ? m->mailbox : "?",
459 sp_new_mail_count(m),
460 sp_expunge_count(m),
461 mn_get_total(sp_msgmap(m))));
463 new_mail_was_announced = 0;
464 if(sp_mail_box_changed(m))
465 fixup_flags(m, sp_msgmap(m));
467 if(sp_new_mail_count(m))
468 process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m));
470 /* worry about sorting */
471 if((sp_new_mail_count(m) > 0L
472 || sp_unsorted_newmail(m)
473 || sp_need_to_rethread(m))
474 && !((flags & NM_DEFER_SORT)
475 || any_lflagged(sp_msgmap(m), MN_HIDE)))
476 refresh_sort(m, sp_msgmap(m),
477 (flags & NM_STATUS_MSG) ? SRT_VRB : SRT_NON);
478 else if(sp_new_mail_count(m) > 0L)
479 sp_set_unsorted_newmail(m, 1);
481 if(sp_new_mail_count(m) > 0L){
482 sp_set_mail_since_cmd(m, sp_mail_since_cmd(m)+sp_new_mail_count(m));
483 rv += (t_nm_count = sp_new_mail_count(m));
484 sp_set_new_mail_count(m, 0L);
486 if((flags & NM_STATUS_MSG) && pith_opt_newmail_announce){
487 for(n = m->nmsgs; n > 1L; n--)
488 if(!get_lflag(m, NULL, n, MN_EXLD))
489 break;
491 (*pith_opt_newmail_announce)(m, n, t_nm_count);
493 if(n)
494 new_mail_was_announced++;
498 update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE);
500 if(flags & NM_STATUS_MSG)
501 sp_set_mail_box_changed(m, 0);
504 /* the rest of the streams */
505 for(i = 0; i < ps_global->s_pool.nstream; i++){
506 m = ps_global->s_pool.streams[i];
507 if(!m || m == ps_global->mail_stream)
508 continue;
510 if(sp_mail_box_changed(m)){
511 /*-- New mail for this stream, queue up the notification --*/
512 dprint((7,
513 "New mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n",
514 (m && m->mailbox) ? m->mailbox : "?",
515 sp_new_mail_count(m),
516 sp_expunge_count(m),
517 mn_get_total(sp_msgmap(m))));
519 new_mail_was_announced = 0;
520 fixup_flags(m, sp_msgmap(m));
522 if(sp_new_mail_count(m))
523 process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m));
525 if(sp_new_mail_count(m) > 0){
526 sp_set_unsorted_newmail(m, 1);
527 sp_set_mail_since_cmd(m, sp_mail_since_cmd(m) +
528 sp_new_mail_count(m));
529 if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
530 rv += (t_nm_count = sp_new_mail_count(m));
532 sp_set_new_mail_count(m, 0L);
534 /* messages only for user's streams */
535 if(flags & NM_STATUS_MSG
536 && pith_opt_newmail_announce
537 && sp_flagged(m, SP_LOCKED)
538 && sp_flagged(m, SP_USERFLDR)){
539 for(n = m->nmsgs; n > 1L; n--)
540 if(!get_lflag(m, NULL, n, MN_EXLD))
541 break;
543 (*pith_opt_newmail_announce)(m, n, t_nm_count);
545 if(n)
546 new_mail_was_announced++;
550 update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE);
552 if(flags & NM_STATUS_MSG)
553 sp_set_mail_box_changed(m, 0);
557 /* so quit_screen can tell new mail from expunged mail */
558 exp_count = sp_expunge_count(ps_global->mail_stream);
560 /* see if we want to kill any cached streams */
561 for(i = 0; i < ps_global->s_pool.nstream; i++){
562 m = ps_global->s_pool.streams[i];
563 if(sp_last_ping(m) >= now)
564 maybe_kill_old_stream(m);
568 * This is here to prevent banging on the down arrow key (or holding
569 * it down and repeating) at the end of the index from causing
570 * a whole bunch of new mail checks. Last_nextitem_forcechk does get
571 * set at the place it is tested in mailcmd.c, but this is here to
572 * reset it to a little bit later in case it takes a second or more
573 * to check for the mail. If we didn't do this, we'd just check every
574 * keystroke as long as the checks took more than a second.
576 if(force_arg)
577 ps_global->last_nextitem_forcechk = time(0);
579 dprint((6, "******** new mail returning %ld ********\n",
580 rv ? rv : (exp_count ? 0 : -1)));
581 return(rv ? rv : (exp_count ? 0 : -1));
586 * format_new_mail_msg - actual work of generating intro,
587 * from, subject and expanded subject
589 void
590 format_new_mail_msg(char *folder, long int number, ENVELOPE *e,
591 char *intro, char *from, char *subj, char *subjex,
592 size_t buflen) /* min length of each of the 4 above if they're non-null */
594 char *p, tmp[MAILTMPLEN+1], subj_leadin[MAILTMPLEN];
595 static char *carray[] = { "regarding",
596 "concerning",
597 "about",
598 "as to",
599 "as regards",
600 "as respects",
601 "in re",
602 "re",
603 "respecting",
604 "in point of",
605 "with regard to",
606 "subject:"
609 if(buflen > 0){
610 if(intro)
611 intro[0] = '\0';
613 if(from)
614 from[0] = '\0';
616 if(subj)
617 subj[0] = '\0';
619 if(subjex)
620 subjex[0] = '\0';
623 if(from && e && e->from){
624 if(e->from->personal && e->from->personal[0]){
626 * The reason we use so many characters for tmp is because we
627 * may have multiple MIME3 chunks and we don't want to truncate
628 * in the middle of one of them before decoding.
630 snprintf(tmp, sizeof(tmp), "%s", e->from->personal);
631 p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp);
632 removing_leading_and_trailing_white_space(p);
633 if(*p)
634 snprintf(from, buflen, "%.200s", p);
637 if(!from[0]){
638 snprintf(tmp, sizeof(tmp), "%s%s%s",
639 e->from->mailbox,
640 e->from->host ? "@" : "",
641 e->from->host ? e->from->host : "");
642 snprintf(from, buflen, "%.200s", tmp);
646 if(number <= 1L){
647 if(e && e->subject && e->subject[0]){
648 snprintf(tmp, sizeof(tmp), "%s", e->subject);
649 p = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
650 if(subj)
651 snprintf(subj, buflen, "%.200s", p);
654 snprintf(subj_leadin, sizeof(subj_leadin), " %s ", carray[(unsigned)random()%12]);
655 if(!(from && from[0]))
656 subj_leadin[1] = toupper((unsigned char)subj_leadin[1]);
659 if(subj && subjex){
660 if(subj[0]){
661 snprintf(subjex, buflen, "%s%.200s%s",
662 (number <= 1L) ? (subj[0] ? subj_leadin : "")
663 : "",
664 (number <= 1L) ? (subj[0] ? subj
665 : from ? " w" : " W")
666 : "",
667 (number <= 1L) ? (subj[0] ? "" : "ith no subject")
668 : "");
670 else
671 subj[0] = subjex[0] = '\0';
674 if(intro){
675 if(!folder){
676 if(number > 1)
677 /* TRANSLATORS: The argument is the number of new messages */
678 snprintf(intro, buflen, _("%ld new messages!"), number);
679 else
680 /* TRANSLATORS: The argument is either " to you" or nothing */
681 snprintf(intro, buflen, _("New mail%s!"),
682 (e && address_is_us(e->to, ps_global)) ? _(" to you") : "");
684 else {
685 long fl, tot, newfl;
686 char *fname = folder ? (char *) folder_name_decoded((unsigned char *) folder) : "";
688 if(number > 1)
689 snprintf(intro, buflen, _("%ld messages saved to folder \"%.80s\""),
690 number, fname);
691 else
692 snprintf(intro, buflen, _("Mail saved to folder \"%.80s\""), fname);
694 if((fl=utf8_width(fname)) > 10 &&
695 (tot=utf8_width(intro) + utf8_width(from ? from : "") + utf8_width(subj ? subj : "")) >
696 ps_global->ttyo->screen_cols - 2){
697 char *f = fs_get((strlen(fname) + 1)*sizeof(char));
698 newfl = MAX(10, fl-(tot-(ps_global->ttyo->screen_cols - 2)));
699 utf8_to_width_rhs(f, fname, strlen(fname) + 1, newfl-3);
700 if(number > 1)
701 snprintf(intro, buflen, _("%ld messages saved to folder \"...%.80s\""), number, f);
702 else
703 snprintf(intro, buflen, _("Mail saved to folder \"...%.80s\""), f);
704 if(f) fs_give((void **)&f);
707 if (fname && *fname)
708 fs_give((void **)&fname);
714 /*----------------------------------------------------------------------
715 Straighten out any local flag problems here. We can't take care of
716 them in the mm_exists or mm_expunged callbacks since the flags
717 themselves are in an MESSAGECACHE and we're not allowed to reenter
718 c-client from a callback...
720 Args: stream -- mail stream to operate on
721 msgmap -- messages in that stream to fix up
723 Result: returns with local flags as they should be
725 ----*/
726 void
727 fixup_flags(MAILSTREAM *stream, MSGNO_S *msgmap)
730 * Deal with the case where expunged away all of the
731 * zoomed messages. Unhide everything in that case...
733 if(mn_get_total(msgmap) > 0L){
734 long i;
736 if(any_lflagged(msgmap, MN_HIDE) >= mn_get_total(msgmap)){
737 for(i = 1L; i <= mn_get_total(msgmap); i++)
738 set_lflag(stream, msgmap, i, MN_HIDE, 0);
740 mn_set_cur(msgmap, THREADING()
741 ? first_sorted_flagged(F_NONE, stream, 0L,
742 (THREADING() ? 0 : FSF_SKIP_CHID)
743 | FSF_LAST)
744 : mn_get_total(msgmap));
746 else if(any_lflagged(msgmap, MN_HIDE)){
748 * if we got here, there are some hidden messages and
749 * some not. Make sure the current message is one
750 * that's not...
752 for(i = mn_get_cur(msgmap); i <= mn_get_total(msgmap); i++)
753 if(!msgline_hidden(stream, msgmap, i, 0)){
754 mn_set_cur(msgmap, i);
755 break;
758 for(i = mn_get_cur(msgmap); i > 0L; i--)
759 if(!msgline_hidden(stream, msgmap, i, 0)){
760 mn_set_cur(msgmap, i);
761 break;
768 /*----------------------------------------------------------------------
769 Force write of the main file so user doesn't lose too much when
770 something bad happens. The only thing that can get lost is flags, such
771 as when new mail arrives, is read, deleted or answered.
773 Args: timing -- indicates if it's a good time for to do a checkpoint
775 Result: returns 1 if checkpoint was written,
776 0 if not.
778 NOTE: mail_check will also notice new mail arrival, so it's imperative that
779 code exist after this function is called that can deal with updating the
780 various pieces of pine's state associated with the message count and such.
782 Only need to checkpoint current stream because there are no changes going
783 on with other streams when we're not manipulating them.
784 ----*/
787 check_point(MAILSTREAM *stream, CheckPointTime timing, int flags)
789 int freq, tm, check_count, tst1 = 0, tst2 = 0, accel;
790 long since_last_input, since_first_change;
791 time_t now, then;
792 #define AT_LEAST (180)
793 #define MIN_LONG_CHK_IDLE (10)
795 if(!stream || stream->rdonly || stream->halfopen
796 || (check_count = sp_check_cnt(stream)) == 0)
797 return(0);
799 since_last_input = (long) time(0) - (long) time_of_last_input();
801 if(timing == GoodTime && since_last_input <= 0)
802 timing = VeryBadTime;
803 else if(timing == GoodTime && since_last_input <= 4)
804 timing = BadTime;
806 dprint((9, "check_point(%s: %s)\n",
807 timing == GoodTime ? "GoodTime" :
808 timing == BadTime ? "BadTime" :
809 timing == VeryBadTime ? "VeryBadTime" : "DoItNow",
810 STREAMNAME(stream)));
812 freq = CHECK_POINT_FREQ * (timing==GoodTime ? 1 : timing==BadTime ? 3 : 4);
813 tm = CHECK_POINT_TIME * (timing==GoodTime ? 2 : timing==BadTime ? 4 : 6);
814 tm = MAX(100, tm);
816 if(timing == DoItNow){
817 dprint((9, "DoItNow\n"));
819 else{
820 since_first_change = (long) (time(0) - sp_first_status_change(stream));
821 accel = MIN(3, MAX(1, (4 * since_first_change)/tm));
822 tst1 = ((check_count * since_last_input) >= (30/accel) * freq);
823 tst2 = ((since_first_change >= tm)
824 && (since_last_input >= MIN_LONG_CHK_IDLE));
825 dprint((9,
826 "Chk changes(%d) x since_last_input(%ld) (=%ld) >= freq(%d) x 30/%d (=%d) ? %s\n",
827 check_count, since_last_input,
828 check_count * since_last_input, freq, accel, (30/accel)*freq,
829 tst1 ? "Yes" : "No"));
831 dprint((9,
832 " or since_1st_change(%ld) >= tm(%d) && since_last_input >= %d ? %s\n",
833 since_first_change, tm, MIN_LONG_CHK_IDLE,
834 tst2 ? "Yes" : "No"));
837 if(timing == DoItNow || tst1 || tst2){
839 if(timing == DoItNow
840 || time(0) - sp_last_chkpnt_done(stream) >= AT_LEAST){
841 then = time(0);
842 dprint((2, "Checkpoint: %s Since 1st change: %ld secs idle: %ld secs\n",
843 debug_time(0,1),
844 (long) (then - sp_first_status_change(stream)),
845 since_last_input))
847 if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue)
848 (*pith_opt_checkpoint_cue)(TRUE);
850 pine_mail_check(stream); /* write file state */
852 now = time(0);
853 dprint((2,
854 "Checkpoint complete: %s%s%s%s\n",
855 debug_time(0,1),
856 (now-then > 0) ? " (elapsed: " : "",
857 (now-then > 0) ? comatose((long)(now-then)) : "",
858 (now-then > 0) ? " secs)" : ""));
860 if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue)
861 (*pith_opt_checkpoint_cue)(FALSE);
863 return(1);
865 else{
866 dprint((9,
867 "Skipping checkpoint since last was only %ld secs ago (< %d)\n",
868 (long) (time(0) - sp_last_chkpnt_done(stream)), AT_LEAST));
872 return(0);
876 /*----------------------------------------------------------------------
877 Call this when we need to tell the check pointing mechanism about
878 mailbox state changes.
879 ----------------------------------------------------------------------*/
880 void
881 check_point_change(MAILSTREAM *stream)
883 if(!sp_check_cnt(stream))
884 sp_set_first_status_change(stream, time(0));
886 sp_set_check_cnt(stream, sp_check_cnt(stream)+1);
887 dprint((9, "check_point_change(%s): increment to %d\n",
888 STREAMNAME(stream), sp_check_cnt(stream)));
893 /*----------------------------------------------------------------------
894 Call this when a mail file is written to reset timer and counter
895 for next check_point.
896 ----------------------------------------------------------------------*/
897 void
898 reset_check_point(MAILSTREAM *stream)
900 time_t now;
902 now = time(0);
904 sp_set_check_cnt(stream, 0);
905 sp_set_first_status_change(stream, 0);
906 sp_set_last_chkpnt_done(stream, now);
907 sp_set_last_ping(stream, now);
908 sp_set_last_expunged_reaper(stream, now);
913 changes_to_checkpoint(MAILSTREAM *stream)
915 return(sp_check_cnt(stream) > 0);
920 /*----------------------------------------------------------------------
921 Zero the counters that keep track of mail accumulated between
922 commands.
923 ----*/
924 void
925 zero_new_mail_count(void)
927 int i;
928 MAILSTREAM *m;
930 dprint((9, "New_mail_count zeroed\n"));
932 for(i = 0; i < ps_global->s_pool.nstream; i++){
933 m = ps_global->s_pool.streams[i];
934 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
935 && sp_mail_since_cmd(m)){
936 if(pith_opt_icon_text)
937 (*pith_opt_icon_text)(NULL, IT_NEWMAIL);
939 break;
943 for(i = 0; i < ps_global->s_pool.nstream; i++){
944 m = ps_global->s_pool.streams[i];
945 if(m && sp_flagged(m, SP_LOCKED))
946 sp_set_mail_since_cmd(m, 0L);