* Bug fix: Tcp and http debug information is not printed unless the
[alpine.git] / pith / newmail.c
blobca29df89cd630a2954278224d6f38e0fb9298a95
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 2013-2021 Eduardo Chappa
8 * Copyright 2006-2007 University of Washington
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"
34 #include "../pith/init.h"
36 #ifdef _WINDOWS
37 #include "../pico/osdep/mswin.h"
38 #endif
42 * Hooks for various stuff
44 void (*pith_opt_newmail_announce)(MAILSTREAM *, long, long);
45 void (*pith_opt_newmail_check_cue)(int);
46 void (*pith_opt_checkpoint_cue)(int);
47 void (*pith_opt_icon_text)(char *, int);
50 void fixup_flags(MAILSTREAM *, MSGNO_S *);
51 int check_point(MAILSTREAM *, CheckPointTime, int);
54 /*----------------------------------------------------------------------
55 new_mail() - check for new mail in the inbox
57 Input: force -- flag indicating we should check for new mail no matter
58 time_for_check_point -- 0: GoodTime, 1: BadTime, 2: VeryBadTime
59 flags -- whether to q a new mail status message or defer the sort
61 Result: returns -1 if there was no new mail. Otherwise returns the
62 sorted message number of the smallest message number that
63 was changed. That is the screen needs to be repainted from
64 that message down.
66 Limit frequency of checks because checks use some resources. That is
67 they generate an IMAP packet or require locking the local mailbox.
68 (Actually the lock isn't required, a stat will do, but the current
69 c-client mail code locks before it stats.)
71 Returns >= 0 only if there is a change in the given mail stream. Otherwise
72 this returns -1. On return the message counts in the pine
73 structure are updated to reflect the current number of messages including
74 any new mail and any expunging.
76 --- */
77 long
78 new_mail(int force_arg, CheckPointTime time_for_check_point, int flags)
80 static time_t last_check_point_call = 0;
81 long since_last_input;
82 time_t expunged_reaper_to, adj_idle_timeout, interval, adj;
83 static int nexttime = 0;
84 time_t now;
85 long n, rv = 0, t_nm_count = 0, exp_count;
86 MAILSTREAM *m;
87 int force, i, started_on;
88 int new_mail_was_announced = 0;
89 int have_pinged_non_special = 0;
90 int timeo;
92 dprint((9, "new mail called (force=%d %s flags=0x%x)\n",
93 force_arg,
94 time_for_check_point == GoodTime ? "GoodTime" :
95 time_for_check_point == BadTime ? "BadTime" :
96 time_for_check_point == VeryBadTime ? "VeryBad" :
97 time_for_check_point == DoItNow ? "DoItNow" : "?",
98 flags));
100 force = force_arg;
102 now = time(0);
104 timeo = get_input_timeout();
106 if(time_for_check_point == GoodTime){
107 adrbk_maintenance();
108 html_dir_clean(0);
111 if(time_for_check_point == GoodTime || force_arg)
112 folder_unseen_count_updater(UFU_ANNOUNCE | (force_arg ? UFU_FORCE : 0));
115 if(sp_need_to_rethread(ps_global->mail_stream))
116 force = 1;
118 if(!force && sp_unsorted_newmail(ps_global->mail_stream))
119 force = !(flags & NM_DEFER_SORT);
121 /* Some servers prefer to close the connection when the access token expires,
122 * while some others prefer to keep the connection alive. This means we
123 * need to check if the access token is about to expire, and if so renew
124 * the access token and the stream. Under normal circumstances this is done
125 * invisible to the user. Hide error messages here, in case there are any.
126 * The worst thing that could happen is that the user will SEE the error
127 * later, when the connection is closed by the server (and the error will
128 * be seen then.) Sending error messages at this time will confuse users.
129 * Avoid it now.
131 for(i = 0; i < ps_global->s_pool.nstream; i++){
132 m = ps_global->s_pool.streams[i];
133 if(m && m->auth.name
134 && (!strucmp(m->auth.name, OA2NAME) || !strucmp(m->auth.name, BEARERNAME))
135 && now + 60 > m->auth.expiration){ /* procastinate doing this */
136 int skip = m->auth.expiration == 0 ? 1 : 0;
137 dprint((9, "renew_accesstoken: %s: now = %lu, auth = %s, expiration = %lu\n", STREAMNAME(m), now, m->auth.name, m->auth.expiration));
138 ps_global->noshow_error = 1; /* make this invisible to the user */
139 renew_accesstoken(m);
140 if(skip == 0) mail_renew_stream(m);
141 ps_global->noshow_error = 0; /* return to normal status */
142 dprint((9, "renew_accesstoken: %s: result: expiration = %lu\n", STREAMNAME(m), m->auth.expiration));
146 if(!ps_global->mail_stream
147 || !(timeo || force || sp_a_locked_stream_changed()))
148 return(-1);
150 last_check_point_call = now;
151 since_last_input = (long) now - (long) time_of_last_input();
154 * We have this for loop followed by the do-while so that we will prefer
155 * to ping the active streams before we ping the inactive ones, in cases
156 * where the pings or checks are taking a long time.
158 for(i = 0; i < ps_global->s_pool.nstream; i++){
159 m = ps_global->s_pool.streams[i];
160 if(!m || m->halfopen ||
161 (m != ps_global->mail_stream &&
162 !(force_arg && sp_flagged(m, SP_LOCKED))))
163 continue;
166 * This for() loop is only the current folder, unless the user
167 * has forced the check, in which case it is all locked folders.
171 * After some amount of inactivity on a stream, the server may
172 * close the stream. Each protocol has its own idea of how much
173 * inactivity should be allowed before the stream is closed. For
174 * example, IMAP specifies that the server should not close the
175 * stream unilaterally until at least 30 minutes of inactivity.
176 * The GET_IDLETIMEOUT call gives us that time in minutes. We
177 * want to be sure to keep the stream alive if it is about to die
178 * due to inactivity.
180 adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL);
181 if(adj_idle_timeout <= 0)
182 adj_idle_timeout = 600;
184 adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE :
185 (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE :
186 (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE;
187 adj_idle_timeout = MAX(adj_idle_timeout - adj, 120);
190 * Set interval to mail-check-interval unless
191 * mail-check-interval-noncurrent is nonzero and this is not inbox
192 * or current stream.
194 if(ps_global->check_interval_for_noncurr > 0
195 && m != ps_global->mail_stream
196 && !sp_flagged(m, SP_INBOX))
197 interval = ps_global->check_interval_for_noncurr;
198 else
199 interval = timeo;
202 * We want to make sure that we notice expunges, but we don't have
203 * to be fanatical about it. Every once in a while we'll do a ping
204 * because we haven't had a command that notices expunges for a
205 * while. It's also a larger number than interval so it gives us a
206 * convenient interval to do slower pinging than interval if we
207 * are busy.
209 if(interval <= adj_idle_timeout)
210 expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout);
211 else
212 expunged_reaper_to = interval;
215 * User may want to avoid new mail checking while composing.
216 * In this case we will only do the keepalives.
218 if(timeo == 0
219 || (flags & NM_FROM_COMPOSER
220 && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global)
221 && !sp_flagged(m, SP_INBOX))
223 (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global)
224 && sp_flagged(m, SP_INBOX))))){
225 interval = expunged_reaper_to = 0;
228 dprint((9,
229 "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n",
230 STREAMNAME(m), force_arg, (long) interval,
231 (long) expunged_reaper_to, (long) adj_idle_timeout));
232 dprint((9,
233 " since_last_ping=%ld since_last_reap=%ld\n",
234 (long) (now - sp_last_ping(m)),
235 (long) (now - sp_last_expunged_reaper(m))));
237 /* if check_point does a check it resets last_ping time */
238 if(force_arg || (timeo && expunged_reaper_to > 0))
239 (void) check_point(m, time_for_check_point, flags);
242 * Remember that unless force_arg is set, this check is really
243 * only for the current folder. This is usually going to fire
244 * on the first test, which is the interval the user set.
246 if(force_arg
248 (timeo
250 ((interval && (now - sp_last_ping(m) >= interval-1))
252 (expunged_reaper_to
253 && (now - sp_last_expunged_reaper(m)) >= expunged_reaper_to)
255 (now - sp_last_ping(m) >= adj_idle_timeout)))){
257 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
258 (*pith_opt_newmail_check_cue)(TRUE);
260 dprint((7, "Mail_Ping(%s): lastping=%ld er=%ld%s%s %s\n",
261 STREAMNAME(m),
262 (long) (now - sp_last_ping(m)),
263 (long) (now - sp_last_expunged_reaper(m)),
264 force_arg ? " [forced]" :
265 interval && (now-sp_last_ping(m) >= interval-1)
266 ? " [it's time]" :
267 expunged_reaper_to
268 && (now - sp_last_expunged_reaper(m) >= expunged_reaper_to)
269 ? " [expunged reaper]"
270 : " [keepalive]",
271 m == ps_global->mail_stream ? " [current]" : "",
272 debug_time(0,1,ps_global->signal_in_progress)));
275 * We're about to ping the stream.
276 * If the stream is a #move Mail Drop there is a minimum time
277 * between re-opens of the mail drop to check for new mail.
278 * If the check is forced by the user, they want it to
279 * happen now. We use knowledge of c-client internals to
280 * make this happen.
282 if(force_arg && m && m->snarf.name)
283 m->snarf.time = 0;
285 /*-- Ping the stream to check for new mail --*/
286 if(sp_dead_stream(m)){
287 dprint((6, "no check: stream is dead\n"));
289 else if(!pine_mail_ping(m)){
290 dprint((6, "ping failed: stream is dead\n"));
291 sp_set_dead_stream(m, 1);
294 dprint((7, "Ping complete: %s\n", debug_time(0,1,ps_global->signal_in_progress)));
296 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
297 (*pith_opt_newmail_check_cue)(FALSE);
301 if(nexttime < 0 || nexttime >= ps_global->s_pool.nstream)
302 nexttime = 0;
304 i = started_on = nexttime;
305 do {
306 m = ps_global->s_pool.streams[i];
308 nexttime = i;
309 if(ps_global->s_pool.nstream > 0)
310 i = (i + 1) % ps_global->s_pool.nstream;
313 * This do() loop handles all the other streams that weren't covered
314 * in the for() loop above.
316 if((!m || m->halfopen) ||
317 (m == ps_global->mail_stream ||
318 (force_arg && sp_flagged(m, SP_LOCKED)))){
319 nexttime = i;
320 continue;
324 * If it is taking an extra long time to do the pings and checks,
325 * think about skipping some of them. Always do at least one of
326 * these non-special streams (if they are due to be pinged).
327 * The nexttime variable keeps track of where we were so that we
328 * don't always ping the first one in the list and then skip out.
330 if((time(0) - now >= 5) && have_pinged_non_special){
331 dprint((7, "skipping pings due to delay: %s\n",
332 debug_time(0,1,ps_global->signal_in_progress)));
333 break;
336 nexttime = i;
337 have_pinged_non_special++;
339 adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL);
340 if(adj_idle_timeout <= 0)
341 adj_idle_timeout = 600;
343 adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE :
344 (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE :
345 (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE;
346 adj_idle_timeout = MAX(adj_idle_timeout - adj, 120);
348 if(ps_global->check_interval_for_noncurr > 0
349 && m != ps_global->mail_stream
350 && !sp_flagged(m, SP_INBOX))
351 interval = ps_global->check_interval_for_noncurr;
352 else
353 interval = timeo;
355 if(interval <= adj_idle_timeout)
356 expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout);
357 else
358 expunged_reaper_to = interval;
360 if(timeo == 0
361 || (flags & NM_FROM_COMPOSER
362 && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global)
363 && !sp_flagged(m, SP_INBOX))
365 (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global)
366 && sp_flagged(m, SP_INBOX))))){
367 interval = expunged_reaper_to = 0;
370 dprint((9,
371 "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n",
372 STREAMNAME(m), force_arg, (long) interval,
373 (long) expunged_reaper_to, (long) adj_idle_timeout));
374 dprint((9,
375 " since_last_ping=%ld since_last_reap=%ld since_last_input=%ld\n",
376 (long) (now - sp_last_ping(m)),
377 (long) (now - sp_last_expunged_reaper(m)),
378 (long) since_last_input));
380 /* if check_point does a check it resets last_ping time */
381 if(force_arg || (timeo && expunged_reaper_to > 0))
382 (void) check_point(m, time_for_check_point, flags);
386 * The check here is a little bit different from the current folder
387 * check in the for() loop above. In particular, we defer our
388 * pinging for awhile if the last input was recent (except for the
389 * inbox!). We ping streams which are cached but not actively being
390 * used (that is, the non-locked streams) at a slower rate.
391 * If we don't use them for a long time we will eventually close them
392 * (in maybe_kill_old_stream()) but we do it when we want to instead
393 * of when the server wants us to by attempting to keep it alive here.
394 * The other reason to ping the cached streams is that we only get
395 * told the new mail in those streams is recent one time, the messages
396 * that weren't handled here will no longer be recent next time
397 * we open the folder.
399 if((force_arg && sp_flagged(m, SP_LOCKED))
401 (timeo
403 ((interval
404 && sp_flagged(m, SP_LOCKED)
405 && ((since_last_input >= 3
406 && (now-sp_last_ping(m) >= interval-1))
407 || (sp_flagged(m, SP_INBOX)
408 && (now-sp_last_ping(m) >= interval-1))))
410 (expunged_reaper_to
411 && sp_flagged(m, SP_LOCKED)
412 && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to))
414 (expunged_reaper_to
415 && !sp_flagged(m, SP_LOCKED)
416 && since_last_input >= 3
417 && (now-sp_last_ping(m) >= expunged_reaper_to))
419 (now - sp_last_ping(m) >= adj_idle_timeout)))){
421 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
422 (*pith_opt_newmail_check_cue)(TRUE);
424 dprint((7,
425 "Mail_Ping(%s): lastping=%ld er=%ld%s idle: %ld %s\n",
426 STREAMNAME(m),
427 (long) (now - sp_last_ping(m)),
428 (long) (now - sp_last_expunged_reaper(m)),
429 (force_arg && sp_flagged(m, SP_LOCKED)) ? " [forced]" :
430 (interval
431 && sp_flagged(m, SP_LOCKED)
432 && ((since_last_input >= 3
433 && (now-sp_last_ping(m) >= interval-1))
434 || (sp_flagged(m, SP_INBOX)
435 && (now-sp_last_ping(m) >= interval-1))))
436 ? " [it's time]" :
437 (expunged_reaper_to
438 && sp_flagged(m, SP_LOCKED)
439 && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to))
440 ? " [expunged reaper]" :
441 (expunged_reaper_to
442 && !sp_flagged(m, SP_LOCKED)
443 && since_last_input >= 3
444 && (now-sp_last_ping(m) >= expunged_reaper_to))
445 ? " [slow ping]" : " [keepalive]",
446 since_last_input,
447 debug_time(0,1,ps_global->signal_in_progress)));
450 * We're about to ping the stream.
451 * If the stream is a #move Mail Drop there is a minimum time
452 * between re-opens of the mail drop to check for new mail.
453 * If the check is forced by the user, they want it to
454 * happen now. We use knowledge of c-client internals to
455 * make this happen.
457 if(force_arg && m && m->snarf.name)
458 m->snarf.time = 0;
460 /*-- Ping the stream to check for new mail --*/
461 if(sp_dead_stream(m)){
462 dprint((6, "no check: stream is dead\n"));
464 else if(!pine_mail_ping(m)){
465 dprint((6, "ping failed: stream is dead\n"));
466 sp_set_dead_stream(m, 1);
469 dprint((7, "Ping complete: %s\n", debug_time(0,1,ps_global->signal_in_progress)));
471 if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
472 (*pith_opt_newmail_check_cue)(FALSE);
474 } while(i != started_on);
477 * Current mail box state changed, could be additions or deletions.
478 * Also check if we need to do sorting that has been deferred.
479 * We handle the current stream separately from the rest in the
480 * similar loop that follows this paragraph.
482 m = ps_global->mail_stream;
483 if(sp_mail_box_changed(m) || sp_unsorted_newmail(m)
484 || sp_need_to_rethread(m)){
485 dprint((7,
486 "Cur new mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n",
487 (m && m->mailbox) ? m->mailbox : "?",
488 sp_new_mail_count(m),
489 sp_expunge_count(m),
490 mn_get_total(sp_msgmap(m))));
492 new_mail_was_announced = 0;
493 if(sp_mail_box_changed(m))
494 fixup_flags(m, sp_msgmap(m));
496 if(sp_new_mail_count(m))
497 process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m));
499 /* worry about sorting */
500 if((sp_new_mail_count(m) > 0L
501 || sp_unsorted_newmail(m)
502 || sp_need_to_rethread(m))
503 && !((flags & NM_DEFER_SORT)
504 || any_lflagged(sp_msgmap(m), MN_HIDE)))
505 refresh_sort(m, sp_msgmap(m),
506 (flags & NM_STATUS_MSG) ? SRT_VRB : SRT_NON);
507 else if(sp_new_mail_count(m) > 0L)
508 sp_set_unsorted_newmail(m, 1);
510 if(sp_new_mail_count(m) > 0L){
511 sp_set_mail_since_cmd(m, sp_mail_since_cmd(m)+sp_new_mail_count(m));
512 rv += (t_nm_count = sp_new_mail_count(m));
513 sp_set_new_mail_count(m, 0L);
515 if((flags & NM_STATUS_MSG) && pith_opt_newmail_announce){
516 for(n = m->nmsgs; n > 1L; n--)
517 if(!get_lflag(m, NULL, n, MN_EXLD))
518 break;
520 (*pith_opt_newmail_announce)(m, n, t_nm_count);
522 if(n)
523 new_mail_was_announced++;
527 update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE);
529 if(flags & NM_STATUS_MSG)
530 sp_set_mail_box_changed(m, 0);
533 /* the rest of the streams */
534 for(i = 0; i < ps_global->s_pool.nstream; i++){
535 m = ps_global->s_pool.streams[i];
536 if(!m || m == ps_global->mail_stream)
537 continue;
539 if(sp_mail_box_changed(m)){
540 /*-- New mail for this stream, queue up the notification --*/
541 dprint((7,
542 "New mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n",
543 (m && m->mailbox) ? m->mailbox : "?",
544 sp_new_mail_count(m),
545 sp_expunge_count(m),
546 mn_get_total(sp_msgmap(m))));
548 new_mail_was_announced = 0;
549 fixup_flags(m, sp_msgmap(m));
551 if(sp_new_mail_count(m))
552 process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m));
554 if(sp_new_mail_count(m) > 0){
555 sp_set_unsorted_newmail(m, 1);
556 sp_set_mail_since_cmd(m, sp_mail_since_cmd(m) +
557 sp_new_mail_count(m));
558 if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
559 rv += (t_nm_count = sp_new_mail_count(m));
561 sp_set_new_mail_count(m, 0L);
563 /* messages only for user's streams */
564 if(flags & NM_STATUS_MSG
565 && pith_opt_newmail_announce
566 && sp_flagged(m, SP_LOCKED)
567 && sp_flagged(m, SP_USERFLDR)){
568 for(n = m->nmsgs; n > 1L; n--)
569 if(!get_lflag(m, NULL, n, MN_EXLD))
570 break;
572 (*pith_opt_newmail_announce)(m, n, t_nm_count);
574 if(n)
575 new_mail_was_announced++;
579 update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE);
581 if(flags & NM_STATUS_MSG)
582 sp_set_mail_box_changed(m, 0);
586 /* so quit_screen can tell new mail from expunged mail */
587 exp_count = sp_expunge_count(ps_global->mail_stream);
589 /* see if we want to kill any cached streams */
590 for(i = 0; i < ps_global->s_pool.nstream; i++){
591 m = ps_global->s_pool.streams[i];
592 if(sp_last_ping(m) >= now)
593 maybe_kill_old_stream(m);
597 * This is here to prevent banging on the down arrow key (or holding
598 * it down and repeating) at the end of the index from causing
599 * a whole bunch of new mail checks. Last_nextitem_forcechk does get
600 * set at the place it is tested in mailcmd.c, but this is here to
601 * reset it to a little bit later in case it takes a second or more
602 * to check for the mail. If we didn't do this, we'd just check every
603 * keystroke as long as the checks took more than a second.
605 if(force_arg)
606 ps_global->last_nextitem_forcechk = time(0);
608 dprint((6, "******** new mail returning %ld ********\n",
609 rv ? rv : (exp_count ? 0 : -1)));
610 return(rv ? rv : (exp_count ? 0 : -1));
615 * format_new_mail_msg - actual work of generating intro,
616 * from, subject and expanded subject
618 void
619 format_new_mail_msg(char *folder, long int number, ENVELOPE *e,
620 char *intro, char *from, char *subj, char *subjex,
621 size_t buflen) /* min length of each of the 4 above if they're non-null */
623 char *p, tmp[MAILTMPLEN+1], subj_leadin[MAILTMPLEN];
624 static char *carray[] = { "regarding",
625 "concerning",
626 "about",
627 "as to",
628 "as regards",
629 "as respects",
630 "in re",
631 "re",
632 "respecting",
633 "in point of",
634 "with regard to",
635 "subject:"
638 if(buflen > 0){
639 if(intro)
640 intro[0] = '\0';
642 if(from)
643 from[0] = '\0';
645 if(subj)
646 subj[0] = '\0';
648 if(subjex)
649 subjex[0] = '\0';
652 if(from && e && e->from){
653 if(e->from->personal && e->from->personal[0]){
655 * The reason we use so many characters for tmp is because we
656 * may have multiple MIME3 chunks and we don't want to truncate
657 * in the middle of one of them before decoding.
659 snprintf(tmp, sizeof(tmp), "%s", e->from->personal);
660 p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp);
661 removing_leading_and_trailing_white_space(p);
662 if(*p)
663 snprintf(from, buflen, "%.200s", p);
666 if(!from[0]){
667 snprintf(tmp, sizeof(tmp), "%s%s%s",
668 e->from->mailbox,
669 e->from->host ? "@" : "",
670 e->from->host ? e->from->host : "");
671 snprintf(from, buflen, "%.200s", tmp);
675 if(number <= 1L){
676 if(e && e->subject && e->subject[0]){
677 snprintf(tmp, sizeof(tmp), "%s", e->subject);
678 p = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
679 if(subj)
680 snprintf(subj, buflen, "%.200s", p);
683 snprintf(subj_leadin, sizeof(subj_leadin), " %s ", carray[(unsigned)random()%12]);
684 if(!(from && from[0]))
685 subj_leadin[1] = toupper((unsigned char)subj_leadin[1]);
688 if(subj && subjex){
689 if(subj[0]){
690 snprintf(subjex, buflen, "%s%.200s%s",
691 (number <= 1L) ? (subj[0] ? subj_leadin : "")
692 : "",
693 (number <= 1L) ? (subj[0] ? subj
694 : from ? " w" : " W")
695 : "",
696 (number <= 1L) ? (subj[0] ? "" : "ith no subject")
697 : "");
699 else
700 subj[0] = subjex[0] = '\0';
703 if(intro){
704 if(!folder){
705 if(number > 1)
706 /* TRANSLATORS: The argument is the number of new messages */
707 snprintf(intro, buflen, _("%ld new messages!"), number);
708 else
709 /* TRANSLATORS: The argument is either " to you" or nothing */
710 snprintf(intro, buflen, _("New mail%s!"),
711 (e && address_is_us(e->to, ps_global)) ? _(" to you") : "");
713 else {
714 long fl, tot, newfl;
715 char *fname = folder ? (char *) folder_name_decoded((unsigned char *) folder) : "";
717 if(number > 1)
718 snprintf(intro, buflen, _("%ld messages saved to folder \"%.80s\""),
719 number, fname);
720 else
721 snprintf(intro, buflen, _("Mail saved to folder \"%.80s\""), fname);
723 if((fl=utf8_width(fname)) > 10 &&
724 (tot=utf8_width(intro) + utf8_width(from ? from : "") + utf8_width(subj ? subj : "")) >
725 ps_global->ttyo->screen_cols - 2){
726 char *f = fs_get((strlen(fname) + 1)*sizeof(char));
727 newfl = MAX(10, fl-(tot-(ps_global->ttyo->screen_cols - 2)));
728 utf8_to_width_rhs(f, fname, strlen(fname) + 1, newfl-3);
729 if(number > 1)
730 snprintf(intro, buflen, _("%ld messages saved to folder \"...%.80s\""), number, f);
731 else
732 snprintf(intro, buflen, _("Mail saved to folder \"...%.80s\""), f);
733 if(f) fs_give((void **)&f);
736 if (fname && *fname)
737 fs_give((void **)&fname);
743 /*----------------------------------------------------------------------
744 Straighten out any local flag problems here. We can't take care of
745 them in the mm_exists or mm_expunged callbacks since the flags
746 themselves are in an MESSAGECACHE and we're not allowed to reenter
747 c-client from a callback...
749 Args: stream -- mail stream to operate on
750 msgmap -- messages in that stream to fix up
752 Result: returns with local flags as they should be
754 ----*/
755 void
756 fixup_flags(MAILSTREAM *stream, MSGNO_S *msgmap)
759 * Deal with the case where expunged away all of the
760 * zoomed messages. Unhide everything in that case...
762 if(mn_get_total(msgmap) > 0L){
763 long i;
765 if(any_lflagged(msgmap, MN_HIDE) >= mn_get_total(msgmap)){
766 for(i = 1L; i <= mn_get_total(msgmap); i++)
767 set_lflag(stream, msgmap, i, MN_HIDE, 0);
769 mn_set_cur(msgmap, THREADING()
770 ? first_sorted_flagged(F_NONE, stream, 0L,
771 (THREADING() ? 0 : FSF_SKIP_CHID)
772 | FSF_LAST)
773 : mn_get_total(msgmap));
775 else if(any_lflagged(msgmap, MN_HIDE)){
777 * if we got here, there are some hidden messages and
778 * some not. Make sure the current message is one
779 * that's not...
781 for(i = mn_get_cur(msgmap); i <= mn_get_total(msgmap); i++)
782 if(!msgline_hidden(stream, msgmap, i, 0)){
783 mn_set_cur(msgmap, i);
784 break;
787 for(i = mn_get_cur(msgmap); i > 0L; i--)
788 if(!msgline_hidden(stream, msgmap, i, 0)){
789 mn_set_cur(msgmap, i);
790 break;
797 /*----------------------------------------------------------------------
798 Force write of the main file so user doesn't lose too much when
799 something bad happens. The only thing that can get lost is flags, such
800 as when new mail arrives, is read, deleted or answered.
802 Args: timing -- indicates if it's a good time for to do a checkpoint
804 Result: returns 1 if checkpoint was written,
805 0 if not.
807 NOTE: mail_check will also notice new mail arrival, so it's imperative that
808 code exist after this function is called that can deal with updating the
809 various pieces of pine's state associated with the message count and such.
811 Only need to checkpoint current stream because there are no changes going
812 on with other streams when we're not manipulating them.
813 ----*/
816 check_point(MAILSTREAM *stream, CheckPointTime timing, int flags)
818 int freq, tm, check_count, tst1 = 0, tst2 = 0, accel;
819 long since_last_input, since_first_change;
820 time_t now, then;
821 #define AT_LEAST (180)
822 #define MIN_LONG_CHK_IDLE (10)
824 if(!stream || stream->rdonly || stream->halfopen
825 || (check_count = sp_check_cnt(stream)) == 0)
826 return(0);
828 since_last_input = (long) time(0) - (long) time_of_last_input();
830 if(timing == GoodTime && since_last_input <= 0)
831 timing = VeryBadTime;
832 else if(timing == GoodTime && since_last_input <= 4)
833 timing = BadTime;
835 dprint((9, "check_point(%s: %s)\n",
836 timing == GoodTime ? "GoodTime" :
837 timing == BadTime ? "BadTime" :
838 timing == VeryBadTime ? "VeryBadTime" : "DoItNow",
839 STREAMNAME(stream)));
841 freq = CHECK_POINT_FREQ * (timing==GoodTime ? 1 : timing==BadTime ? 3 : 4);
842 tm = CHECK_POINT_TIME * (timing==GoodTime ? 2 : timing==BadTime ? 4 : 6);
843 tm = MAX(100, tm);
845 if(timing == DoItNow){
846 dprint((9, "DoItNow\n"));
848 else{
849 since_first_change = (long) (time(0) - sp_first_status_change(stream));
850 accel = MIN(3, MAX(1, (4 * since_first_change)/tm));
851 tst1 = ((check_count * since_last_input) >= (30/accel) * freq);
852 tst2 = ((since_first_change >= tm)
853 && (since_last_input >= MIN_LONG_CHK_IDLE));
854 dprint((9,
855 "Chk changes(%d) x since_last_input(%ld) (=%ld) >= freq(%d) x 30/%d (=%d) ? %s\n",
856 check_count, since_last_input,
857 check_count * since_last_input, freq, accel, (30/accel)*freq,
858 tst1 ? "Yes" : "No"));
860 dprint((9,
861 " or since_1st_change(%ld) >= tm(%d) && since_last_input >= %d ? %s\n",
862 since_first_change, tm, MIN_LONG_CHK_IDLE,
863 tst2 ? "Yes" : "No"));
866 if(timing == DoItNow || tst1 || tst2){
868 if(timing == DoItNow
869 || time(0) - sp_last_chkpnt_done(stream) >= AT_LEAST){
870 then = time(0);
871 dprint((2, "Checkpoint: %s Since 1st change: %ld secs idle: %ld secs\n",
872 debug_time(0,1,ps_global->signal_in_progress),
873 (long) (then - sp_first_status_change(stream)),
874 since_last_input))
876 if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue)
877 (*pith_opt_checkpoint_cue)(TRUE);
879 pine_mail_check(stream); /* write file state */
881 now = time(0);
882 dprint((2,
883 "Checkpoint complete: %s%s%s%s\n",
884 debug_time(0,1,ps_global->signal_in_progress),
885 (now-then > 0) ? " (elapsed: " : "",
886 (now-then > 0) ? comatose((long)(now-then)) : "",
887 (now-then > 0) ? " secs)" : ""));
889 if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue)
890 (*pith_opt_checkpoint_cue)(FALSE);
892 return(1);
894 else{
895 dprint((9,
896 "Skipping checkpoint since last was only %ld secs ago (< %d)\n",
897 (long) (time(0) - sp_last_chkpnt_done(stream)), AT_LEAST));
901 return(0);
905 /*----------------------------------------------------------------------
906 Call this when we need to tell the check pointing mechanism about
907 mailbox state changes.
908 ----------------------------------------------------------------------*/
909 void
910 check_point_change(MAILSTREAM *stream)
912 if(!sp_check_cnt(stream))
913 sp_set_first_status_change(stream, time(0));
915 sp_set_check_cnt(stream, sp_check_cnt(stream)+1);
916 dprint((9, "check_point_change(%s): increment to %d\n",
917 STREAMNAME(stream), sp_check_cnt(stream)));
922 /*----------------------------------------------------------------------
923 Call this when a mail file is written to reset timer and counter
924 for next check_point.
925 ----------------------------------------------------------------------*/
926 void
927 reset_check_point(MAILSTREAM *stream)
929 time_t now;
931 now = time(0);
933 sp_set_check_cnt(stream, 0);
934 sp_set_first_status_change(stream, 0);
935 sp_set_last_chkpnt_done(stream, now);
936 sp_set_last_ping(stream, now);
937 sp_set_last_expunged_reaper(stream, now);
942 changes_to_checkpoint(MAILSTREAM *stream)
944 return(sp_check_cnt(stream) > 0);
949 /*----------------------------------------------------------------------
950 Zero the counters that keep track of mail accumulated between
951 commands.
952 ----*/
953 void
954 zero_new_mail_count(void)
956 int i;
957 MAILSTREAM *m;
959 dprint((9, "New_mail_count zeroed\n"));
961 for(i = 0; i < ps_global->s_pool.nstream; i++){
962 m = ps_global->s_pool.streams[i];
963 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
964 && sp_mail_since_cmd(m)){
965 if(pith_opt_icon_text)
966 (*pith_opt_icon_text)(NULL, IT_NEWMAIL);
968 break;
972 for(i = 0; i < ps_global->s_pool.nstream; i++){
973 m = ps_global->s_pool.streams[i];
974 if(m && sp_flagged(m, SP_LOCKED))
975 sp_set_mail_since_cmd(m, 0L);