Add support for tab-completion when selecting by rule
[alpine.git] / pith / newmail.c
blobb17f4c4f7f71b38918c6c1fde5a2643c04dd5739
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/newmail.h"
17 #include "../pith/conf.h"
18 #include "../pith/flag.h"
19 #include "../pith/mailindx.h"
20 #include "../pith/msgno.h"
21 #include "../pith/bldaddr.h"
22 #include "../pith/stream.h"
23 #include "../pith/sort.h"
24 #include "../pith/status.h"
25 #include "../pith/util.h"
26 #include "../pith/thread.h"
27 #include "../pith/options.h"
28 #include "../pith/folder.h"
29 #include "../pith/ablookup.h"
30 #include "../pith/init.h"
32 #ifdef _WINDOWS
33 #include "../pico/osdep/mswin.h"
34 #endif
38 * Hooks for various stuff
40 void (*pith_opt_newmail_announce)(MAILSTREAM *, long, long);
41 void (*pith_opt_newmail_check_cue)(int);
42 void (*pith_opt_checkpoint_cue)(int);
43 void (*pith_opt_icon_text)(char *, int);
46 void fixup_flags(MAILSTREAM *, MSGNO_S *);
47 int check_point(MAILSTREAM *, CheckPointTime, int);
50 /*----------------------------------------------------------------------
51 new_mail() - check for new mail in the inbox
53 Input: force -- flag indicating we should check for new mail no matter
54 time_for_check_point -- 0: GoodTime, 1: BadTime, 2: VeryBadTime
55 flags -- whether to q a new mail status message or defer the sort
57 Result: returns -1 if there was no new mail. Otherwise returns the
58 sorted message number of the smallest message number that
59 was changed. That is the screen needs to be repainted from
60 that message down.
62 Limit frequency of checks because checks use some resources. That is
63 they generate an IMAP packet or require locking the local mailbox.
64 (Actually the lock isn't required, a stat will do, but the current
65 c-client mail code locks before it stats.)
67 Returns >= 0 only if there is a change in the given mail stream. Otherwise
68 this returns -1. On return the message counts in the pine
69 structure are updated to reflect the current number of messages including
70 any new mail and any expunging.
72 --- */
73 long
74 new_mail(int force_arg, CheckPointTime time_for_check_point, int flags)
76 static time_t last_check_point_call = 0;
77 long since_last_input;
78 time_t expunged_reaper_to, adj_idle_timeout, interval, adj;
79 static int nexttime = 0;
80 time_t now;
81 long n, rv = 0, t_nm_count = 0, exp_count;
82 MAILSTREAM *m;
83 int force, i, started_on;
84 int new_mail_was_announced = 0;
85 int have_pinged_non_special = 0;
86 int timeo;
88 dprint((9, "new mail called (force=%d %s flags=0x%x)\n",
89 force_arg,
90 time_for_check_point == GoodTime ? "GoodTime" :
91 time_for_check_point == BadTime ? "BadTime" :
92 time_for_check_point == VeryBadTime ? "VeryBad" :
93 time_for_check_point == DoItNow ? "DoItNow" : "?",
94 flags));
96 force = force_arg;
98 now = time(0);
100 timeo = get_input_timeout();
102 if(time_for_check_point == GoodTime){
103 adrbk_maintenance();
104 html_dir_clean(0);
107 if(time_for_check_point == GoodTime || force_arg)
108 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 /* Some servers prefer to close the connection when the access token expires,
118 * while some others prefer to keep the connection alive. This means we
119 * need to check if the access token is about to expire, and if so renew
120 * the access token and the stream. Under normal circumstances this is done
121 * invisible to the user. Hide error messages here, in case there are any.
122 * The worst thing that could happen is that the user will SEE the error
123 * later, when the connection is closed by the server (and the error will
124 * be seen then.) Sending error messages at this time will confuse users.
125 * Avoid it now.
127 for(i = 0; i < ps_global->s_pool.nstream; i++){
128 m = ps_global->s_pool.streams[i];
129 if(m && m->auth.name
130 && (!strucmp(m->auth.name, OA2NAME) || !strucmp(m->auth.name, BEARERNAME))
131 && now + 60 > m->auth.expiration){ /* procastinate doing this */
132 int skip = m->auth.expiration == 0 ? 1 : 0;
133 dprint((9, "renew_accesstoken: %s: now = %lu, auth = %s, expiration = %lu\n", STREAMNAME(m), now, m->auth.name, m->auth.expiration));
134 ps_global->noshow_error = 1; /* make this invisible to the user */
135 renew_accesstoken(m);
136 if(skip == 0) mail_renew_stream(m);
137 ps_global->noshow_error = 0; /* return to normal status */
138 dprint((9, "renew_accesstoken: %s: result: expiration = %lu\n", STREAMNAME(m), m->auth.expiration));
142 if(!ps_global->mail_stream
143 || !(timeo || force || sp_a_locked_stream_changed()))
144 return(-1);
146 last_check_point_call = now;
147 since_last_input = (long) now - (long) time_of_last_input();
150 * We have this for loop followed by the do-while so that we will prefer
151 * to ping the active streams before we ping the inactive ones, in cases
152 * where the pings or checks are taking a long time.
154 for(i = 0; i < ps_global->s_pool.nstream; i++){
155 m = ps_global->s_pool.streams[i];
156 if(!m || m->halfopen ||
157 (m != ps_global->mail_stream &&
158 !(force_arg && sp_flagged(m, SP_LOCKED))))
159 continue;
162 * This for() loop is only the current folder, unless the user
163 * has forced the check, in which case it is all locked folders.
167 * After some amount of inactivity on a stream, the server may
168 * close the stream. Each protocol has its own idea of how much
169 * inactivity should be allowed before the stream is closed. For
170 * example, IMAP specifies that the server should not close the
171 * stream unilaterally until at least 30 minutes of inactivity.
172 * The GET_IDLETIMEOUT call gives us that time in minutes. We
173 * want to be sure to keep the stream alive if it is about to die
174 * due to inactivity.
176 adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL);
177 if(adj_idle_timeout <= 0)
178 adj_idle_timeout = 600;
180 adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE :
181 (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE :
182 (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE;
183 adj_idle_timeout = MAX(adj_idle_timeout - adj, 120);
186 * Set interval to mail-check-interval unless
187 * mail-check-interval-noncurrent is nonzero and this is not inbox
188 * or current stream.
190 if(ps_global->check_interval_for_noncurr > 0
191 && m != ps_global->mail_stream
192 && !sp_flagged(m, SP_INBOX))
193 interval = ps_global->check_interval_for_noncurr;
194 else
195 interval = timeo;
198 * We want to make sure that we notice expunges, but we don't have
199 * to be fanatical about it. Every once in a while we'll do a ping
200 * because we haven't had a command that notices expunges for a
201 * while. It's also a larger number than interval so it gives us a
202 * convenient interval to do slower pinging than interval if we
203 * are busy.
205 if(interval <= adj_idle_timeout)
206 expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout);
207 else
208 expunged_reaper_to = interval;
211 * User may want to avoid new mail checking while composing.
212 * In this case we will only do the keepalives.
214 if(timeo == 0
215 || (flags & NM_FROM_COMPOSER
216 && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global)
217 && !sp_flagged(m, SP_INBOX))
219 (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global)
220 && sp_flagged(m, SP_INBOX))))){
221 interval = expunged_reaper_to = 0;
224 dprint((9,
225 "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n",
226 STREAMNAME(m), force_arg, (long) interval,
227 (long) expunged_reaper_to, (long) adj_idle_timeout));
228 dprint((9,
229 " since_last_ping=%ld since_last_reap=%ld\n",
230 (long) (now - sp_last_ping(m)),
231 (long) (now - sp_last_expunged_reaper(m))));
233 /* if check_point does a check it resets last_ping time */
234 if(force_arg || (timeo && expunged_reaper_to > 0))
235 (void) check_point(m, time_for_check_point, flags);
238 * Remember that unless force_arg is set, this check is really
239 * only for the current folder. This is usually going to fire
240 * on the first test, which is the interval the user set.
242 if(force_arg
244 (timeo
246 ((interval && (now - sp_last_ping(m) >= interval-1))
248 (expunged_reaper_to
249 && (now - sp_last_expunged_reaper(m)) >= expunged_reaper_to)
251 (now - sp_last_ping(m) >= adj_idle_timeout)))){
253 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
254 (*pith_opt_newmail_check_cue)(TRUE);
256 dprint((7, "Mail_Ping(%s): lastping=%ld er=%ld%s%s %s\n",
257 STREAMNAME(m),
258 (long) (now - sp_last_ping(m)),
259 (long) (now - sp_last_expunged_reaper(m)),
260 force_arg ? " [forced]" :
261 interval && (now-sp_last_ping(m) >= interval-1)
262 ? " [it's time]" :
263 expunged_reaper_to
264 && (now - sp_last_expunged_reaper(m) >= expunged_reaper_to)
265 ? " [expunged reaper]"
266 : " [keepalive]",
267 m == ps_global->mail_stream ? " [current]" : "",
268 debug_time(0,1,ps_global->signal_in_progress)));
271 * We're about to ping the stream.
272 * If the stream is a #move Mail Drop there is a minimum time
273 * between re-opens of the mail drop to check for new mail.
274 * If the check is forced by the user, they want it to
275 * happen now. We use knowledge of c-client internals to
276 * make this happen.
278 if(force_arg && m && m->snarf.name)
279 m->snarf.time = 0;
281 /*-- Ping the stream to check for new mail --*/
282 if(sp_dead_stream(m)){
283 dprint((6, "no check: stream is dead\n"));
285 else if(!pine_mail_ping(m)){
286 dprint((6, "ping failed: stream is dead\n"));
287 sp_set_dead_stream(m, 1);
290 dprint((7, "Ping complete: %s\n", debug_time(0,1,ps_global->signal_in_progress)));
292 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
293 (*pith_opt_newmail_check_cue)(FALSE);
297 if(nexttime < 0 || nexttime >= ps_global->s_pool.nstream)
298 nexttime = 0;
300 i = started_on = nexttime;
301 do {
302 m = ps_global->s_pool.streams[i];
304 nexttime = i;
305 if(ps_global->s_pool.nstream > 0)
306 i = (i + 1) % ps_global->s_pool.nstream;
309 * This do() loop handles all the other streams that weren't covered
310 * in the for() loop above.
312 if((!m || m->halfopen) ||
313 (m == ps_global->mail_stream ||
314 (force_arg && sp_flagged(m, SP_LOCKED)))){
315 nexttime = i;
316 continue;
320 * If it is taking an extra long time to do the pings and checks,
321 * think about skipping some of them. Always do at least one of
322 * these non-special streams (if they are due to be pinged).
323 * The nexttime variable keeps track of where we were so that we
324 * don't always ping the first one in the list and then skip out.
326 if((time(0) - now >= 5) && have_pinged_non_special){
327 dprint((7, "skipping pings due to delay: %s\n",
328 debug_time(0,1,ps_global->signal_in_progress)));
329 break;
332 nexttime = i;
333 have_pinged_non_special++;
335 adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL);
336 if(adj_idle_timeout <= 0)
337 adj_idle_timeout = 600;
339 adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE :
340 (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE :
341 (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE;
342 adj_idle_timeout = MAX(adj_idle_timeout - adj, 120);
344 if(ps_global->check_interval_for_noncurr > 0
345 && m != ps_global->mail_stream
346 && !sp_flagged(m, SP_INBOX))
347 interval = ps_global->check_interval_for_noncurr;
348 else
349 interval = timeo;
351 if(interval <= adj_idle_timeout)
352 expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout);
353 else
354 expunged_reaper_to = interval;
356 if(timeo == 0
357 || (flags & NM_FROM_COMPOSER
358 && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global)
359 && !sp_flagged(m, SP_INBOX))
361 (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global)
362 && sp_flagged(m, SP_INBOX))))){
363 interval = expunged_reaper_to = 0;
366 dprint((9,
367 "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n",
368 STREAMNAME(m), force_arg, (long) interval,
369 (long) expunged_reaper_to, (long) adj_idle_timeout));
370 dprint((9,
371 " since_last_ping=%ld since_last_reap=%ld since_last_input=%ld\n",
372 (long) (now - sp_last_ping(m)),
373 (long) (now - sp_last_expunged_reaper(m)),
374 (long) since_last_input));
376 /* if check_point does a check it resets last_ping time */
377 if(force_arg || (timeo && expunged_reaper_to > 0))
378 (void) check_point(m, time_for_check_point, flags);
382 * The check here is a little bit different from the current folder
383 * check in the for() loop above. In particular, we defer our
384 * pinging for awhile if the last input was recent (except for the
385 * inbox!). We ping streams which are cached but not actively being
386 * used (that is, the non-locked streams) at a slower rate.
387 * If we don't use them for a long time we will eventually close them
388 * (in maybe_kill_old_stream()) but we do it when we want to instead
389 * of when the server wants us to by attempting to keep it alive here.
390 * The other reason to ping the cached streams is that we only get
391 * told the new mail in those streams is recent one time, the messages
392 * that weren't handled here will no longer be recent next time
393 * we open the folder.
395 if((force_arg && sp_flagged(m, SP_LOCKED))
397 (timeo
399 ((interval
400 && sp_flagged(m, SP_LOCKED)
401 && ((since_last_input >= 3
402 && (now-sp_last_ping(m) >= interval-1))
403 || (sp_flagged(m, SP_INBOX)
404 && (now-sp_last_ping(m) >= interval-1))))
406 (expunged_reaper_to
407 && sp_flagged(m, SP_LOCKED)
408 && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to))
410 (expunged_reaper_to
411 && !sp_flagged(m, SP_LOCKED)
412 && since_last_input >= 3
413 && (now-sp_last_ping(m) >= expunged_reaper_to))
415 (now - sp_last_ping(m) >= adj_idle_timeout)))){
417 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
418 (*pith_opt_newmail_check_cue)(TRUE);
420 dprint((7,
421 "Mail_Ping(%s): lastping=%ld er=%ld%s idle: %ld %s\n",
422 STREAMNAME(m),
423 (long) (now - sp_last_ping(m)),
424 (long) (now - sp_last_expunged_reaper(m)),
425 (force_arg && sp_flagged(m, SP_LOCKED)) ? " [forced]" :
426 (interval
427 && sp_flagged(m, SP_LOCKED)
428 && ((since_last_input >= 3
429 && (now-sp_last_ping(m) >= interval-1))
430 || (sp_flagged(m, SP_INBOX)
431 && (now-sp_last_ping(m) >= interval-1))))
432 ? " [it's time]" :
433 (expunged_reaper_to
434 && sp_flagged(m, SP_LOCKED)
435 && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to))
436 ? " [expunged reaper]" :
437 (expunged_reaper_to
438 && !sp_flagged(m, SP_LOCKED)
439 && since_last_input >= 3
440 && (now-sp_last_ping(m) >= expunged_reaper_to))
441 ? " [slow ping]" : " [keepalive]",
442 since_last_input,
443 debug_time(0,1,ps_global->signal_in_progress)));
446 * We're about to ping the stream.
447 * If the stream is a #move Mail Drop there is a minimum time
448 * between re-opens of the mail drop to check for new mail.
449 * If the check is forced by the user, they want it to
450 * happen now. We use knowledge of c-client internals to
451 * make this happen.
453 if(force_arg && m && m->snarf.name)
454 m->snarf.time = 0;
456 /*-- Ping the stream to check for new mail --*/
457 if(sp_dead_stream(m)){
458 dprint((6, "no check: stream is dead\n"));
460 else if(!pine_mail_ping(m)){
461 dprint((6, "ping failed: stream is dead\n"));
462 sp_set_dead_stream(m, 1);
465 dprint((7, "Ping complete: %s\n", debug_time(0,1,ps_global->signal_in_progress)));
467 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
468 (*pith_opt_newmail_check_cue)(FALSE);
470 } while(i != started_on);
473 * Current mail box state changed, could be additions or deletions.
474 * Also check if we need to do sorting that has been deferred.
475 * We handle the current stream separately from the rest in the
476 * similar loop that follows this paragraph.
478 m = ps_global->mail_stream;
479 if(sp_mail_box_changed(m) || sp_unsorted_newmail(m)
480 || sp_need_to_rethread(m)){
481 dprint((7,
482 "Cur new mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n",
483 (m && m->mailbox) ? m->mailbox : "?",
484 sp_new_mail_count(m),
485 sp_expunge_count(m),
486 mn_get_total(sp_msgmap(m))));
488 new_mail_was_announced = 0;
489 if(sp_mail_box_changed(m))
490 fixup_flags(m, sp_msgmap(m));
492 if(sp_new_mail_count(m))
493 process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m));
495 /* worry about sorting */
496 if((sp_new_mail_count(m) > 0L
497 || sp_unsorted_newmail(m)
498 || sp_need_to_rethread(m))
499 && !((flags & NM_DEFER_SORT)
500 || any_lflagged(sp_msgmap(m), MN_HIDE)))
501 refresh_sort(m, sp_msgmap(m),
502 (flags & NM_STATUS_MSG) ? SRT_VRB : SRT_NON);
503 else if(sp_new_mail_count(m) > 0L)
504 sp_set_unsorted_newmail(m, 1);
506 if(sp_new_mail_count(m) > 0L){
507 sp_set_mail_since_cmd(m, sp_mail_since_cmd(m)+sp_new_mail_count(m));
508 rv += (t_nm_count = sp_new_mail_count(m));
509 sp_set_new_mail_count(m, 0L);
511 if((flags & NM_STATUS_MSG) && pith_opt_newmail_announce){
512 for(n = m->nmsgs; n > 1L; n--)
513 if(!get_lflag(m, NULL, n, MN_EXLD))
514 break;
516 (*pith_opt_newmail_announce)(m, n, t_nm_count);
518 if(n)
519 new_mail_was_announced++;
523 update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE);
525 if(flags & NM_STATUS_MSG)
526 sp_set_mail_box_changed(m, 0);
529 /* the rest of the streams */
530 for(i = 0; i < ps_global->s_pool.nstream; i++){
531 m = ps_global->s_pool.streams[i];
532 if(!m || m == ps_global->mail_stream)
533 continue;
535 if(sp_mail_box_changed(m)){
536 /*-- New mail for this stream, queue up the notification --*/
537 dprint((7,
538 "New mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n",
539 (m && m->mailbox) ? m->mailbox : "?",
540 sp_new_mail_count(m),
541 sp_expunge_count(m),
542 mn_get_total(sp_msgmap(m))));
544 new_mail_was_announced = 0;
545 fixup_flags(m, sp_msgmap(m));
547 if(sp_new_mail_count(m))
548 process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m));
550 if(sp_new_mail_count(m) > 0){
551 sp_set_unsorted_newmail(m, 1);
552 sp_set_mail_since_cmd(m, sp_mail_since_cmd(m) +
553 sp_new_mail_count(m));
554 if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
555 rv += (t_nm_count = sp_new_mail_count(m));
557 sp_set_new_mail_count(m, 0L);
559 /* messages only for user's streams */
560 if(flags & NM_STATUS_MSG
561 && pith_opt_newmail_announce
562 && sp_flagged(m, SP_LOCKED)
563 && sp_flagged(m, SP_USERFLDR)){
564 for(n = m->nmsgs; n > 1L; n--)
565 if(!get_lflag(m, NULL, n, MN_EXLD))
566 break;
568 (*pith_opt_newmail_announce)(m, n, t_nm_count);
570 if(n)
571 new_mail_was_announced++;
575 update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE);
577 if(flags & NM_STATUS_MSG)
578 sp_set_mail_box_changed(m, 0);
582 /* so quit_screen can tell new mail from expunged mail */
583 exp_count = sp_expunge_count(ps_global->mail_stream);
585 /* see if we want to kill any cached streams */
586 for(i = 0; i < ps_global->s_pool.nstream; i++){
587 m = ps_global->s_pool.streams[i];
588 if(sp_last_ping(m) >= now)
589 maybe_kill_old_stream(m);
593 * This is here to prevent banging on the down arrow key (or holding
594 * it down and repeating) at the end of the index from causing
595 * a whole bunch of new mail checks. Last_nextitem_forcechk does get
596 * set at the place it is tested in mailcmd.c, but this is here to
597 * reset it to a little bit later in case it takes a second or more
598 * to check for the mail. If we didn't do this, we'd just check every
599 * keystroke as long as the checks took more than a second.
601 if(force_arg)
602 ps_global->last_nextitem_forcechk = time(0);
604 dprint((6, "******** new mail returning %ld ********\n",
605 rv ? rv : (exp_count ? 0 : -1)));
606 return(rv ? rv : (exp_count ? 0 : -1));
611 * format_new_mail_msg - actual work of generating intro,
612 * from, subject and expanded subject
614 void
615 format_new_mail_msg(char *folder, long int number, ENVELOPE *e,
616 char *intro, char *from, char *subj, char *subjex,
617 size_t buflen) /* min length of each of the 4 above if they're non-null */
619 char *p, tmp[MAILTMPLEN+1], subj_leadin[MAILTMPLEN];
620 static char *carray[] = { "regarding",
621 "concerning",
622 "about",
623 "as to",
624 "as regards",
625 "as respects",
626 "in re",
627 "re",
628 "respecting",
629 "in point of",
630 "with regard to",
631 "subject:"
634 if(buflen > 0){
635 if(intro)
636 intro[0] = '\0';
638 if(from)
639 from[0] = '\0';
641 if(subj)
642 subj[0] = '\0';
644 if(subjex)
645 subjex[0] = '\0';
648 if(from && e && e->from){
649 if(e->from->personal && e->from->personal[0]){
651 * The reason we use so many characters for tmp is because we
652 * may have multiple MIME3 chunks and we don't want to truncate
653 * in the middle of one of them before decoding.
655 snprintf(tmp, sizeof(tmp), "%s", e->from->personal);
656 p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp);
657 removing_leading_and_trailing_white_space(p);
658 if(*p)
659 snprintf(from, buflen, "%.200s", p);
662 if(!from[0]){
663 snprintf(tmp, sizeof(tmp), "%s%s%s",
664 e->from->mailbox,
665 e->from->host ? "@" : "",
666 e->from->host ? e->from->host : "");
667 snprintf(from, buflen, "%.200s", tmp);
671 if(number <= 1L){
672 if(e && e->subject && e->subject[0]){
673 snprintf(tmp, sizeof(tmp), "%s", e->subject);
674 p = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
675 if(subj)
676 snprintf(subj, buflen, "%.200s", p);
679 snprintf(subj_leadin, sizeof(subj_leadin), " %s ", carray[(unsigned)random()%12]);
680 if(!(from && from[0]))
681 subj_leadin[1] = toupper((unsigned char)subj_leadin[1]);
684 if(subj && subjex){
685 if(subj[0]){
686 snprintf(subjex, buflen, "%s%.200s%s",
687 (number <= 1L) ? (subj[0] ? subj_leadin : "")
688 : "",
689 (number <= 1L) ? (subj[0] ? subj
690 : from ? " w" : " W")
691 : "",
692 (number <= 1L) ? (subj[0] ? "" : "ith no subject")
693 : "");
695 else
696 subj[0] = subjex[0] = '\0';
699 if(intro){
700 if(!folder){
701 if(number > 1)
702 /* TRANSLATORS: The argument is the number of new messages */
703 snprintf(intro, buflen, _("%ld new messages!"), number);
704 else
705 /* TRANSLATORS: The argument is either " to you" or nothing */
706 snprintf(intro, buflen, _("New mail%s!"),
707 (e && address_is_us(e->to, ps_global)) ? _(" to you") : "");
709 else {
710 long fl, tot, newfl;
711 char *fname = folder ? (char *) folder_name_decoded((unsigned char *) folder) : "";
713 if(number > 1)
714 snprintf(intro, buflen, _("%ld messages saved to folder \"%.80s\""),
715 number, fname);
716 else
717 snprintf(intro, buflen, _("Mail saved to folder \"%.80s\""), fname);
719 if((fl=utf8_width(fname)) > 10 &&
720 (tot=utf8_width(intro) + utf8_width(from ? from : "") + utf8_width(subj ? subj : "")) >
721 ps_global->ttyo->screen_cols - 2){
722 char *f = fs_get((strlen(fname) + 1)*sizeof(char));
723 newfl = MAX(10, fl-(tot-(ps_global->ttyo->screen_cols - 2)));
724 utf8_to_width_rhs(f, fname, strlen(fname) + 1, newfl-3);
725 if(number > 1)
726 snprintf(intro, buflen, _("%ld messages saved to folder \"...%.80s\""), number, f);
727 else
728 snprintf(intro, buflen, _("Mail saved to folder \"...%.80s\""), f);
729 if(f) fs_give((void **)&f);
732 if (fname && *fname)
733 fs_give((void **)&fname);
739 /*----------------------------------------------------------------------
740 Straighten out any local flag problems here. We can't take care of
741 them in the mm_exists or mm_expunged callbacks since the flags
742 themselves are in an MESSAGECACHE and we're not allowed to reenter
743 c-client from a callback...
745 Args: stream -- mail stream to operate on
746 msgmap -- messages in that stream to fix up
748 Result: returns with local flags as they should be
750 ----*/
751 void
752 fixup_flags(MAILSTREAM *stream, MSGNO_S *msgmap)
755 * Deal with the case where expunged away all of the
756 * zoomed messages. Unhide everything in that case...
758 if(mn_get_total(msgmap) > 0L){
759 long i;
761 if(any_lflagged(msgmap, MN_HIDE) >= mn_get_total(msgmap)){
762 for(i = 1L; i <= mn_get_total(msgmap); i++)
763 set_lflag(stream, msgmap, i, MN_HIDE, 0);
765 mn_set_cur(msgmap, THREADING()
766 ? first_sorted_flagged(F_NONE, stream, 0L,
767 (THREADING() ? 0 : FSF_SKIP_CHID)
768 | FSF_LAST)
769 : mn_get_total(msgmap));
771 else if(any_lflagged(msgmap, MN_HIDE)){
773 * if we got here, there are some hidden messages and
774 * some not. Make sure the current message is one
775 * that's not...
777 for(i = mn_get_cur(msgmap); i <= mn_get_total(msgmap); i++)
778 if(!msgline_hidden(stream, msgmap, i, 0)){
779 mn_set_cur(msgmap, i);
780 break;
783 for(i = mn_get_cur(msgmap); i > 0L; i--)
784 if(!msgline_hidden(stream, msgmap, i, 0)){
785 mn_set_cur(msgmap, i);
786 break;
793 /*----------------------------------------------------------------------
794 Force write of the main file so user doesn't lose too much when
795 something bad happens. The only thing that can get lost is flags, such
796 as when new mail arrives, is read, deleted or answered.
798 Args: timing -- indicates if it's a good time for to do a checkpoint
800 Result: returns 1 if checkpoint was written,
801 0 if not.
803 NOTE: mail_check will also notice new mail arrival, so it's imperative that
804 code exist after this function is called that can deal with updating the
805 various pieces of pine's state associated with the message count and such.
807 Only need to checkpoint current stream because there are no changes going
808 on with other streams when we're not manipulating them.
809 ----*/
812 check_point(MAILSTREAM *stream, CheckPointTime timing, int flags)
814 int freq, tm, check_count, tst1 = 0, tst2 = 0, accel;
815 long since_last_input, since_first_change;
816 time_t now, then;
817 #define AT_LEAST (180)
818 #define MIN_LONG_CHK_IDLE (10)
820 if(!stream || stream->rdonly || stream->halfopen
821 || (check_count = sp_check_cnt(stream)) == 0)
822 return(0);
824 since_last_input = (long) time(0) - (long) time_of_last_input();
826 if(timing == GoodTime && since_last_input <= 0)
827 timing = VeryBadTime;
828 else if(timing == GoodTime && since_last_input <= 4)
829 timing = BadTime;
831 dprint((9, "check_point(%s: %s)\n",
832 timing == GoodTime ? "GoodTime" :
833 timing == BadTime ? "BadTime" :
834 timing == VeryBadTime ? "VeryBadTime" : "DoItNow",
835 STREAMNAME(stream)));
837 freq = CHECK_POINT_FREQ * (timing==GoodTime ? 1 : timing==BadTime ? 3 : 4);
838 tm = CHECK_POINT_TIME * (timing==GoodTime ? 2 : timing==BadTime ? 4 : 6);
839 tm = MAX(100, tm);
841 if(timing == DoItNow){
842 dprint((9, "DoItNow\n"));
844 else{
845 since_first_change = (long) (time(0) - sp_first_status_change(stream));
846 accel = MIN(3, MAX(1, (4 * since_first_change)/tm));
847 tst1 = ((check_count * since_last_input) >= (30/accel) * freq);
848 tst2 = ((since_first_change >= tm)
849 && (since_last_input >= MIN_LONG_CHK_IDLE));
850 dprint((9,
851 "Chk changes(%d) x since_last_input(%ld) (=%ld) >= freq(%d) x 30/%d (=%d) ? %s\n",
852 check_count, since_last_input,
853 check_count * since_last_input, freq, accel, (30/accel)*freq,
854 tst1 ? "Yes" : "No"));
856 dprint((9,
857 " or since_1st_change(%ld) >= tm(%d) && since_last_input >= %d ? %s\n",
858 since_first_change, tm, MIN_LONG_CHK_IDLE,
859 tst2 ? "Yes" : "No"));
862 if(timing == DoItNow || tst1 || tst2){
864 if(timing == DoItNow
865 || time(0) - sp_last_chkpnt_done(stream) >= AT_LEAST){
866 then = time(0);
867 dprint((2, "Checkpoint: %s Since 1st change: %ld secs idle: %ld secs\n",
868 debug_time(0,1,ps_global->signal_in_progress),
869 (long) (then - sp_first_status_change(stream)),
870 since_last_input))
872 if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue)
873 (*pith_opt_checkpoint_cue)(TRUE);
875 pine_mail_check(stream); /* write file state */
877 now = time(0);
878 dprint((2,
879 "Checkpoint complete: %s%s%s%s\n",
880 debug_time(0,1,ps_global->signal_in_progress),
881 (now-then > 0) ? " (elapsed: " : "",
882 (now-then > 0) ? comatose((long)(now-then)) : "",
883 (now-then > 0) ? " secs)" : ""));
885 if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue)
886 (*pith_opt_checkpoint_cue)(FALSE);
888 return(1);
890 else{
891 dprint((9,
892 "Skipping checkpoint since last was only %ld secs ago (< %d)\n",
893 (long) (time(0) - sp_last_chkpnt_done(stream)), AT_LEAST));
897 return(0);
901 /*----------------------------------------------------------------------
902 Call this when we need to tell the check pointing mechanism about
903 mailbox state changes.
904 ----------------------------------------------------------------------*/
905 void
906 check_point_change(MAILSTREAM *stream)
908 if(!sp_check_cnt(stream))
909 sp_set_first_status_change(stream, time(0));
911 sp_set_check_cnt(stream, sp_check_cnt(stream)+1);
912 dprint((9, "check_point_change(%s): increment to %d\n",
913 STREAMNAME(stream), sp_check_cnt(stream)));
918 /*----------------------------------------------------------------------
919 Call this when a mail file is written to reset timer and counter
920 for next check_point.
921 ----------------------------------------------------------------------*/
922 void
923 reset_check_point(MAILSTREAM *stream)
925 time_t now;
927 now = time(0);
929 sp_set_check_cnt(stream, 0);
930 sp_set_first_status_change(stream, 0);
931 sp_set_last_chkpnt_done(stream, now);
932 sp_set_last_ping(stream, now);
933 sp_set_last_expunged_reaper(stream, now);
938 changes_to_checkpoint(MAILSTREAM *stream)
940 return(sp_check_cnt(stream) > 0);
945 /*----------------------------------------------------------------------
946 Zero the counters that keep track of mail accumulated between
947 commands.
948 ----*/
949 void
950 zero_new_mail_count(void)
952 int i;
953 MAILSTREAM *m;
955 dprint((9, "New_mail_count zeroed\n"));
957 for(i = 0; i < ps_global->s_pool.nstream; i++){
958 m = ps_global->s_pool.streams[i];
959 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
960 && sp_mail_since_cmd(m)){
961 if(pith_opt_icon_text)
962 (*pith_opt_icon_text)(NULL, IT_NEWMAIL);
964 break;
968 for(i = 0; i < ps_global->s_pool.nstream; i++){
969 m = ps_global->s_pool.streams[i];
970 if(m && sp_flagged(m, SP_LOCKED))
971 sp_set_mail_since_cmd(m, 0L);