diff options
author | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
---|---|---|
committer | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
commit | 094ca96844842928810f14844413109fc6cdd890 (patch) | |
tree | e60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pith/newmail.c | |
download | alpine-094ca96844842928810f14844413109fc6cdd890.tar.xz |
Initial Alpine Version
Diffstat (limited to 'pith/newmail.c')
-rw-r--r-- | pith/newmail.c | 948 |
1 files changed, 948 insertions, 0 deletions
diff --git a/pith/newmail.c b/pith/newmail.c new file mode 100644 index 00000000..26d9a986 --- /dev/null +++ b/pith/newmail.c @@ -0,0 +1,948 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: newmail.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2007 University of Washington + * Copyright 2013 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + */ + +#include "../pith/headers.h" +#include "../pith/newmail.h" +#include "../pith/conf.h" +#include "../pith/flag.h" +#include "../pith/mailindx.h" +#include "../pith/msgno.h" +#include "../pith/bldaddr.h" +#include "../pith/stream.h" +#include "../pith/sort.h" +#include "../pith/status.h" +#include "../pith/util.h" +#include "../pith/thread.h" +#include "../pith/options.h" +#include "../pith/folder.h" +#include "../pith/ablookup.h" + +#ifdef _WINDOWS +#include "../pico/osdep/mswin.h" +#endif + + +/* + * Hooks for various stuff + */ +void (*pith_opt_newmail_announce)(MAILSTREAM *, long, long); +void (*pith_opt_newmail_check_cue)(int); +void (*pith_opt_checkpoint_cue)(int); +void (*pith_opt_icon_text)(char *, int); + + +void fixup_flags(MAILSTREAM *, MSGNO_S *); +int check_point(MAILSTREAM *, CheckPointTime, int); + + +/*---------------------------------------------------------------------- + new_mail() - check for new mail in the inbox + + Input: force -- flag indicating we should check for new mail no matter + time_for_check_point -- 0: GoodTime, 1: BadTime, 2: VeryBadTime + flags -- whether to q a new mail status message or defer the sort + + Result: returns -1 if there was no new mail. Otherwise returns the + sorted message number of the smallest message number that + was changed. That is the screen needs to be repainted from + that message down. + + Limit frequency of checks because checks use some resources. That is + they generate an IMAP packet or require locking the local mailbox. + (Acutally the lock isn't required, a stat will do, but the current + c-client mail code locks before it stats.) + + Returns >= 0 only if there is a change in the given mail stream. Otherwise + this returns -1. On return the message counts in the pine + structure are updated to reflect the current number of messages including + any new mail and any expunging. + + --- */ +long +new_mail(int force_arg, CheckPointTime time_for_check_point, int flags) +{ + static time_t last_check_point_call = 0; + long since_last_input; + time_t expunged_reaper_to, adj_idle_timeout, interval, adj; + static int nexttime = 0; + time_t now; + long n, rv = 0, t_nm_count = 0, exp_count; + MAILSTREAM *m; + int force, i, started_on; + int new_mail_was_announced = 0; + int have_pinged_non_special = 0; + int timeo; + + dprint((9, "new mail called (force=%d %s flags=0x%x)\n", + force_arg, + time_for_check_point == GoodTime ? "GoodTime" : + time_for_check_point == BadTime ? "BadTime" : + time_for_check_point == VeryBadTime ? "VeryBad" : + time_for_check_point == DoItNow ? "DoItNow" : "?", + flags)); + + force = force_arg; + + now = time(0); + + timeo = get_input_timeout(); + + if(time_for_check_point == GoodTime) + adrbk_maintenance(); + + if(time_for_check_point == GoodTime || force_arg) + folder_unseen_count_updater(UFU_ANNOUNCE | (force_arg ? UFU_FORCE : 0)); + + if(sp_need_to_rethread(ps_global->mail_stream)) + force = 1; + + if(!force && sp_unsorted_newmail(ps_global->mail_stream)) + force = !(flags & NM_DEFER_SORT); + + if(!ps_global->mail_stream + || !(timeo || force || sp_a_locked_stream_changed())) + return(-1); + + last_check_point_call = now; + since_last_input = (long) now - (long) time_of_last_input(); + + /* + * We have this for loop followed by the do-while so that we will prefer + * to ping the active streams before we ping the inactive ones, in cases + * where the pings or checks are taking a long time. + */ + for(i = 0; i < ps_global->s_pool.nstream; i++){ + m = ps_global->s_pool.streams[i]; + if(!m || m->halfopen || + (m != ps_global->mail_stream && + !(force_arg && sp_flagged(m, SP_LOCKED)))) + continue; + + /* + * This for() loop is only the current folder, unless the user + * has forced the check, in which case it is all locked folders. + */ + + /* + * After some amount of inactivity on a stream, the server may + * close the stream. Each protocol has its own idea of how much + * inactivity should be allowed before the stream is closed. For + * example, IMAP specifies that the server should not close the + * stream unilaterally until at least 30 minutes of inactivity. + * The GET_IDLETIMEOUT call gives us that time in minutes. We + * want to be sure to keep the stream alive if it is about to die + * due to inactivity. + */ + adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL); + if(adj_idle_timeout <= 0) + adj_idle_timeout = 600; + + adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE : + (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE : + (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE; + adj_idle_timeout = MAX(adj_idle_timeout - adj, 120); + + /* + * Set interval to mail-check-interval unless + * mail-check-interval-noncurrent is nonzero and this is not inbox + * or current stream. + */ + if(ps_global->check_interval_for_noncurr > 0 + && m != ps_global->mail_stream + && !sp_flagged(m, SP_INBOX)) + interval = ps_global->check_interval_for_noncurr; + else + interval = timeo; + + /* + * We want to make sure that we notice expunges, but we don't have + * to be fanatical about it. Every once in a while we'll do a ping + * because we haven't had a command that notices expunges for a + * while. It's also a larger number than interval so it gives us a + * convenient interval to do slower pinging than interval if we + * are busy. + */ + if(interval <= adj_idle_timeout) + expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout); + else + expunged_reaper_to = interval; + + /* + * User may want to avoid new mail checking while composing. + * In this case we will only do the keepalives. + */ + if(timeo == 0 + || (flags & NM_FROM_COMPOSER + && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global) + && !sp_flagged(m, SP_INBOX)) + || + (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global) + && sp_flagged(m, SP_INBOX))))){ + interval = expunged_reaper_to = 0; + } + + dprint((9, + "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n", + STREAMNAME(m), force_arg, (long) interval, + (long) expunged_reaper_to, (long) adj_idle_timeout)); + dprint((9, + " since_last_ping=%ld since_last_reap=%ld\n", + (long) (now - sp_last_ping(m)), + (long) (now - sp_last_expunged_reaper(m)))); + + /* if check_point does a check it resets last_ping time */ + if(force_arg || (timeo && expunged_reaper_to > 0)) + (void) check_point(m, time_for_check_point, flags); + + /* + * Remember that unless force_arg is set, this check is really + * only for the current folder. This is usually going to fire + * on the first test, which is the interval the user set. + */ + if(force_arg + || + (timeo + && + ((interval && (now - sp_last_ping(m) >= interval-1)) + || + (expunged_reaper_to + && (now - sp_last_expunged_reaper(m)) >= expunged_reaper_to) + || + (now - sp_last_ping(m) >= adj_idle_timeout)))){ + + if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue) + (*pith_opt_newmail_check_cue)(TRUE); + + dprint((7, "Mail_Ping(%s): lastping=%ld er=%ld%s%s %s\n", + STREAMNAME(m), + (long) (now - sp_last_ping(m)), + (long) (now - sp_last_expunged_reaper(m)), + force_arg ? " [forced]" : + interval && (now-sp_last_ping(m) >= interval-1) + ? " [it's time]" : + expunged_reaper_to + && (now - sp_last_expunged_reaper(m) >= expunged_reaper_to) + ? " [expunged reaper]" + : " [keepalive]", + m == ps_global->mail_stream ? " [current]" : "", + debug_time(0,1))); + + /* + * We're about to ping the stream. + * If the stream is a #move Mail Drop there is a minimum time + * between re-opens of the mail drop to check for new mail. + * If the check is forced by the user, they want it to + * happen now. We use knowledge of c-client internals to + * make this happen. + */ + if(force_arg && m && m->snarf.name) + m->snarf.time = 0; + + /*-- Ping the stream to check for new mail --*/ + if(sp_dead_stream(m)){ + dprint((6, "no check: stream is dead\n")); + } + else if(!pine_mail_ping(m)){ + dprint((6, "ping failed: stream is dead\n")); + sp_set_dead_stream(m, 1); + } + + dprint((7, "Ping complete: %s\n", debug_time(0,1))); + + if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue) + (*pith_opt_newmail_check_cue)(FALSE); + } + } + + if(nexttime < 0 || nexttime >= ps_global->s_pool.nstream) + nexttime = 0; + + i = started_on = nexttime; + do { + m = ps_global->s_pool.streams[i]; + + nexttime = i; + if(ps_global->s_pool.nstream > 0) + i = (i + 1) % ps_global->s_pool.nstream; + + /* + * This do() loop handles all the other streams that weren't covered + * in the for() loop above. + */ + if((!m || m->halfopen) || + (m == ps_global->mail_stream || + (force_arg && sp_flagged(m, SP_LOCKED)))){ + nexttime = i; + continue; + } + + /* + * If it is taking an extra long time to do the pings and checks, + * think about skipping some of them. Always do at least one of + * these non-special streams (if they are due to be pinged). + * The nexttime variable keeps track of where we were so that we + * don't always ping the first one in the list and then skip out. + */ + if((time(0) - now >= 5) && have_pinged_non_special){ + dprint((7, "skipping pings due to delay: %s\n", + debug_time(0,1))); + break; + } + + nexttime = i; + have_pinged_non_special++; + + adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL); + if(adj_idle_timeout <= 0) + adj_idle_timeout = 600; + + adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE : + (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE : + (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE; + adj_idle_timeout = MAX(adj_idle_timeout - adj, 120); + + if(ps_global->check_interval_for_noncurr > 0 + && m != ps_global->mail_stream + && !sp_flagged(m, SP_INBOX)) + interval = ps_global->check_interval_for_noncurr; + else + interval = timeo; + + if(interval <= adj_idle_timeout) + expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout); + else + expunged_reaper_to = interval; + + if(timeo == 0 + || (flags & NM_FROM_COMPOSER + && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global) + && !sp_flagged(m, SP_INBOX)) + || + (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global) + && sp_flagged(m, SP_INBOX))))){ + interval = expunged_reaper_to = 0; + } + + dprint((9, + "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n", + STREAMNAME(m), force_arg, (long) interval, + (long) expunged_reaper_to, (long) adj_idle_timeout)); + dprint((9, + " since_last_ping=%ld since_last_reap=%ld since_last_input=%ld\n", + (long) (now - sp_last_ping(m)), + (long) (now - sp_last_expunged_reaper(m)), + (long) since_last_input)); + + /* if check_point does a check it resets last_ping time */ + if(force_arg || (timeo && expunged_reaper_to > 0)) + (void) check_point(m, time_for_check_point, flags); + + + /* + * The check here is a little bit different from the current folder + * check in the for() loop above. In particular, we defer our + * pinging for awhile if the last input was recent (except for the + * inbox!). We ping streams which are cached but not actively being + * used (that is, the non-locked streams) at a slower rate. + * If we don't use them for a long time we will eventually close them + * (in maybe_kill_old_stream()) but we do it when we want to instead + * of when the server wants us to by attempting to keep it alive here. + * The other reason to ping the cached streams is that we only get + * told the new mail in those streams is recent one time, the messages + * that weren't handled here will no longer be recent next time + * we open the folder. + */ + if((force_arg && sp_flagged(m, SP_LOCKED)) + || + (timeo + && + ((interval + && sp_flagged(m, SP_LOCKED) + && ((since_last_input >= 3 + && (now-sp_last_ping(m) >= interval-1)) + || (sp_flagged(m, SP_INBOX) + && (now-sp_last_ping(m) >= interval-1)))) + || + (expunged_reaper_to + && sp_flagged(m, SP_LOCKED) + && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to)) + || + (expunged_reaper_to + && !sp_flagged(m, SP_LOCKED) + && since_last_input >= 3 + && (now-sp_last_ping(m) >= expunged_reaper_to)) + || + (now - sp_last_ping(m) >= adj_idle_timeout)))){ + + if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue) + (*pith_opt_newmail_check_cue)(TRUE); + + dprint((7, + "Mail_Ping(%s): lastping=%ld er=%ld%s idle: %ld %s\n", + STREAMNAME(m), + (long) (now - sp_last_ping(m)), + (long) (now - sp_last_expunged_reaper(m)), + (force_arg && sp_flagged(m, SP_LOCKED)) ? " [forced]" : + (interval + && sp_flagged(m, SP_LOCKED) + && ((since_last_input >= 3 + && (now-sp_last_ping(m) >= interval-1)) + || (sp_flagged(m, SP_INBOX) + && (now-sp_last_ping(m) >= interval-1)))) + ? " [it's time]" : + (expunged_reaper_to + && sp_flagged(m, SP_LOCKED) + && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to)) + ? " [expunged reaper]" : + (expunged_reaper_to + && !sp_flagged(m, SP_LOCKED) + && since_last_input >= 3 + && (now-sp_last_ping(m) >= expunged_reaper_to)) + ? " [slow ping]" : " [keepalive]", + since_last_input, + debug_time(0,1))); + + /* + * We're about to ping the stream. + * If the stream is a #move Mail Drop there is a minimum time + * between re-opens of the mail drop to check for new mail. + * If the check is forced by the user, they want it to + * happen now. We use knowledge of c-client internals to + * make this happen. + */ + if(force_arg && m && m->snarf.name) + m->snarf.time = 0; + + /*-- Ping the stream to check for new mail --*/ + if(sp_dead_stream(m)){ + dprint((6, "no check: stream is dead\n")); + } + else if(!pine_mail_ping(m)){ + dprint((6, "ping failed: stream is dead\n")); + sp_set_dead_stream(m, 1); + } + + dprint((7, "Ping complete: %s\n", debug_time(0,1))); + + if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue) + (*pith_opt_newmail_check_cue)(FALSE); + } + } while(i != started_on); + + /* + * Current mail box state changed, could be additions or deletions. + * Also check if we need to do sorting that has been deferred. + * We handle the current stream separately from the rest in the + * similar loop that follows this paragraph. + */ + m = ps_global->mail_stream; + if(sp_mail_box_changed(m) || sp_unsorted_newmail(m) + || sp_need_to_rethread(m)){ + dprint((7, + "Cur new mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n", + (m && m->mailbox) ? m->mailbox : "?", + sp_new_mail_count(m), + sp_expunge_count(m), + mn_get_total(sp_msgmap(m)))); + + new_mail_was_announced = 0; + if(sp_mail_box_changed(m)) + fixup_flags(m, sp_msgmap(m)); + + if(sp_new_mail_count(m)) + process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m)); + + /* worry about sorting */ + if((sp_new_mail_count(m) > 0L + || sp_unsorted_newmail(m) + || sp_need_to_rethread(m)) + && !((flags & NM_DEFER_SORT) + || any_lflagged(sp_msgmap(m), MN_HIDE))) + refresh_sort(m, sp_msgmap(m), + (flags & NM_STATUS_MSG) ? SRT_VRB : SRT_NON); + else if(sp_new_mail_count(m) > 0L) + sp_set_unsorted_newmail(m, 1); + + if(sp_new_mail_count(m) > 0L){ + sp_set_mail_since_cmd(m, sp_mail_since_cmd(m)+sp_new_mail_count(m)); + rv += (t_nm_count = sp_new_mail_count(m)); + sp_set_new_mail_count(m, 0L); + + if((flags & NM_STATUS_MSG) && pith_opt_newmail_announce){ + for(n = m->nmsgs; n > 1L; n--) + if(!get_lflag(m, NULL, n, MN_EXLD)) + break; + + (*pith_opt_newmail_announce)(m, n, t_nm_count); + + if(n) + new_mail_was_announced++; + } + } + + update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE); + + if(flags & NM_STATUS_MSG) + sp_set_mail_box_changed(m, 0); + } + + /* the rest of the streams */ + for(i = 0; i < ps_global->s_pool.nstream; i++){ + m = ps_global->s_pool.streams[i]; + if(!m || m == ps_global->mail_stream) + continue; + + if(sp_mail_box_changed(m)){ + /*-- New mail for this stream, queue up the notification --*/ + dprint((7, + "New mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n", + (m && m->mailbox) ? m->mailbox : "?", + sp_new_mail_count(m), + sp_expunge_count(m), + mn_get_total(sp_msgmap(m)))); + + new_mail_was_announced = 0; + fixup_flags(m, sp_msgmap(m)); + + if(sp_new_mail_count(m)) + process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m)); + + if(sp_new_mail_count(m) > 0){ + sp_set_unsorted_newmail(m, 1); + sp_set_mail_since_cmd(m, sp_mail_since_cmd(m) + + sp_new_mail_count(m)); + if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)) + rv += (t_nm_count = sp_new_mail_count(m)); + + sp_set_new_mail_count(m, 0L); + + /* messages only for user's streams */ + if(flags & NM_STATUS_MSG + && pith_opt_newmail_announce + && sp_flagged(m, SP_LOCKED) + && sp_flagged(m, SP_USERFLDR)){ + for(n = m->nmsgs; n > 1L; n--) + if(!get_lflag(m, NULL, n, MN_EXLD)) + break; + + (*pith_opt_newmail_announce)(m, n, t_nm_count); + + if(n) + new_mail_was_announced++; + } + } + + update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE); + + if(flags & NM_STATUS_MSG) + sp_set_mail_box_changed(m, 0); + } + } + + /* so quit_screen can tell new mail from expunged mail */ + exp_count = sp_expunge_count(ps_global->mail_stream); + + /* see if we want to kill any cached streams */ + for(i = 0; i < ps_global->s_pool.nstream; i++){ + m = ps_global->s_pool.streams[i]; + if(sp_last_ping(m) >= now) + maybe_kill_old_stream(m); + } + + /* + * This is here to prevent banging on the down arrow key (or holding + * it down and repeating) at the end of the index from causing + * a whole bunch of new mail checks. Last_nextitem_forcechk does get + * set at the place it is tested in mailcmd.c, but this is here to + * reset it to a little bit later in case it takes a second or more + * to check for the mail. If we didn't do this, we'd just check every + * keystroke as long as the checks took more than a second. + */ + if(force_arg) + ps_global->last_nextitem_forcechk = time(0); + + dprint((6, "******** new mail returning %ld ********\n", + rv ? rv : (exp_count ? 0 : -1))); + return(rv ? rv : (exp_count ? 0 : -1)); +} + + +/* + * format_new_mail_msg - actual work of generating intro, + * from, subject and expanded subject + */ +void +format_new_mail_msg(char *folder, long int number, ENVELOPE *e, + char *intro, char *from, char *subj, char *subjex, + size_t buflen) /* min length of each of the 4 above if they're non-null */ +{ + char *p, tmp[MAILTMPLEN+1], subj_leadin[MAILTMPLEN]; + static char *carray[] = { "regarding", + "concerning", + "about", + "as to", + "as regards", + "as respects", + "in re", + "re", + "respecting", + "in point of", + "with regard to", + "subject:" + }; + + if(buflen > 0){ + if(intro) + intro[0] = '\0'; + + if(from) + from[0] = '\0'; + + if(subj) + subj[0] = '\0'; + + if(subjex) + subjex[0] = '\0'; + } + + if(from && e && e->from){ + if(e->from->personal && e->from->personal[0]){ + /* + * The reason we use so many characters for tmp is because we + * may have multiple MIME3 chunks and we don't want to truncate + * in the middle of one of them before decoding. + */ + snprintf(tmp, sizeof(tmp), "%s", e->from->personal); + p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp); + removing_leading_and_trailing_white_space(p); + if(*p) + snprintf(from, buflen, "%.200s", p); + } + + if(!from[0]){ + snprintf(tmp, sizeof(tmp), "%s%s%s", + e->from->mailbox, + e->from->host ? "@" : "", + e->from->host ? e->from->host : ""); + snprintf(from, buflen, "%.200s", tmp); + } + } + + if(number <= 1L){ + if(e && e->subject && e->subject[0]){ + snprintf(tmp, sizeof(tmp), "%s", e->subject); + p = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp); + if(subj) + snprintf(subj, buflen, "%.200s", p); + } + + snprintf(subj_leadin, sizeof(subj_leadin), " %s ", carray[(unsigned)random()%12]); + if(!(from && from[0])) + subj_leadin[1] = toupper((unsigned char)subj_leadin[1]); + } + + if(subj && subjex){ + if(subj[0]){ + snprintf(subjex, buflen, "%s%.200s%s", + (number <= 1L) ? (subj[0] ? subj_leadin : "") + : "", + (number <= 1L) ? (subj[0] ? subj + : from ? " w" : " W") + : "", + (number <= 1L) ? (subj[0] ? "" : "ith no subject") + : ""); + } + else + subj[0] = subjex[0] = '\0'; + } + + if(intro){ + if(!folder){ + if(number > 1) + /* TRANSLATORS: The argument is the number of new messages */ + snprintf(intro, buflen, _("%ld new messages!"), number); + else + /* TRANSLATORS: The argument is either " to you" or nothing */ + snprintf(intro, buflen, _("New mail%s!"), + (e && address_is_us(e->to, ps_global)) ? _(" to you") : ""); + } + else { + long fl, tot, newfl; + char *fname = folder ? (char *) folder_name_decoded((unsigned char *) folder) : ""; + + if(number > 1) + snprintf(intro, buflen, _("%ld messages saved to folder \"%.80s\""), + number, fname); + else + snprintf(intro, buflen, _("Mail saved to folder \"%.80s\""), fname); + + if((fl=utf8_width(fname)) > 10 && + (tot=utf8_width(intro) + utf8_width(from ? from : "") + utf8_width(subj ? subj : "")) > + ps_global->ttyo->screen_cols - 2){ + char *f = fs_get((strlen(fname) + 1)*sizeof(char)); + newfl = MAX(10, fl-(tot-(ps_global->ttyo->screen_cols - 2))); + utf8_to_width_rhs(f, fname, strlen(fname) + 1, newfl-3); + if(number > 1) + snprintf(intro, buflen, _("%ld messages saved to folder \"...%.80s\""), number, f); + else + snprintf(intro, buflen, _("Mail saved to folder \"...%.80s\""), f); + if(f) fs_give((void **)&f); + } + + if (fname && *fname) + fs_give((void **)&fname); + } + } +} + + +/*---------------------------------------------------------------------- + Straighten out any local flag problems here. We can't take care of + them in the mm_exists or mm_expunged callbacks since the flags + themselves are in an MESSAGECACHE and we're not allowed to reenter + c-client from a callback... + + Args: stream -- mail stream to operate on + msgmap -- messages in that stream to fix up + + Result: returns with local flags as they should be + + ----*/ +void +fixup_flags(MAILSTREAM *stream, MSGNO_S *msgmap) +{ + /* + * Deal with the case where expunged away all of the + * zoomed messages. Unhide everything in that case... + */ + if(mn_get_total(msgmap) > 0L){ + long i; + + if(any_lflagged(msgmap, MN_HIDE) >= mn_get_total(msgmap)){ + for(i = 1L; i <= mn_get_total(msgmap); i++) + set_lflag(stream, msgmap, i, MN_HIDE, 0); + + mn_set_cur(msgmap, THREADING() + ? first_sorted_flagged(F_NONE, stream, 0L, + (THREADING() ? 0 : FSF_SKIP_CHID) + | FSF_LAST) + : mn_get_total(msgmap)); + } + else if(any_lflagged(msgmap, MN_HIDE)){ + /* + * if we got here, there are some hidden messages and + * some not. Make sure the current message is one + * that's not... + */ + for(i = mn_get_cur(msgmap); i <= mn_get_total(msgmap); i++) + if(!msgline_hidden(stream, msgmap, i, 0)){ + mn_set_cur(msgmap, i); + break; + } + + for(i = mn_get_cur(msgmap); i > 0L; i--) + if(!msgline_hidden(stream, msgmap, i, 0)){ + mn_set_cur(msgmap, i); + break; + } + } + } +} + + +/*---------------------------------------------------------------------- + Force write of the main file so user doesn't lose too much when + something bad happens. The only thing that can get lost is flags, such + as when new mail arrives, is read, deleted or answered. + + Args: timing -- indicates if it's a good time for to do a checkpoint + + Result: returns 1 if checkpoint was written, + 0 if not. + +NOTE: mail_check will also notice new mail arrival, so it's imperative that +code exist after this function is called that can deal with updating the +various pieces of pine's state associated with the message count and such. + +Only need to checkpoint current stream because there are no changes going +on with other streams when we're not manipulating them. + ----*/ + +int +check_point(MAILSTREAM *stream, CheckPointTime timing, int flags) +{ + int freq, tm, check_count, tst1 = 0, tst2 = 0, accel; + long since_last_input, since_first_change; + time_t now, then; +#define AT_LEAST (180) +#define MIN_LONG_CHK_IDLE (10) + + if(!stream || stream->rdonly || stream->halfopen + || (check_count = sp_check_cnt(stream)) == 0) + return(0); + + since_last_input = (long) time(0) - (long) time_of_last_input(); + + if(timing == GoodTime && since_last_input <= 0) + timing = VeryBadTime; + else if(timing == GoodTime && since_last_input <= 4) + timing = BadTime; + + dprint((9, "check_point(%s: %s)\n", + timing == GoodTime ? "GoodTime" : + timing == BadTime ? "BadTime" : + timing == VeryBadTime ? "VeryBadTime" : "DoItNow", + STREAMNAME(stream))); + + freq = CHECK_POINT_FREQ * (timing==GoodTime ? 1 : timing==BadTime ? 3 : 4); + tm = CHECK_POINT_TIME * (timing==GoodTime ? 2 : timing==BadTime ? 4 : 6); + tm = MAX(100, tm); + + if(timing == DoItNow){ + dprint((9, "DoItNow\n")); + } + else{ + since_first_change = (long) (time(0) - sp_first_status_change(stream)); + accel = MIN(3, MAX(1, (4 * since_first_change)/tm)); + tst1 = ((check_count * since_last_input) >= (30/accel) * freq); + tst2 = ((since_first_change >= tm) + && (since_last_input >= MIN_LONG_CHK_IDLE)); + dprint((9, + "Chk changes(%d) x since_last_input(%ld) (=%ld) >= freq(%d) x 30/%d (=%d) ? %s\n", + check_count, since_last_input, + check_count * since_last_input, freq, accel, (30/accel)*freq, + tst1 ? "Yes" : "No")); + + dprint((9, + " or since_1st_change(%ld) >= tm(%d) && since_last_input >= %d ? %s\n", + since_first_change, tm, MIN_LONG_CHK_IDLE, + tst2 ? "Yes" : "No")); + } + + if(timing == DoItNow || tst1 || tst2){ + + if(timing == DoItNow + || time(0) - sp_last_chkpnt_done(stream) >= AT_LEAST){ + then = time(0); + dprint((2, "Checkpoint: %s Since 1st change: %ld secs idle: %ld secs\n", + debug_time(0,1), + (long) (then - sp_first_status_change(stream)), + since_last_input)) +; + if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue) + (*pith_opt_checkpoint_cue)(TRUE); + + pine_mail_check(stream); /* write file state */ + + now = time(0); + dprint((2, + "Checkpoint complete: %s%s%s%s\n", + debug_time(0,1), + (now-then > 0) ? " (elapsed: " : "", + (now-then > 0) ? comatose((long)(now-then)) : "", + (now-then > 0) ? " secs)" : "")); + + if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue) + (*pith_opt_checkpoint_cue)(FALSE); + + return(1); + } + else{ + dprint((9, + "Skipping checkpoint since last was only %ld secs ago (< %d)\n", + (long) (time(0) - sp_last_chkpnt_done(stream)), AT_LEAST)); + } + } + + return(0); +} + + +/*---------------------------------------------------------------------- + Call this when we need to tell the check pointing mechanism about + mailbox state changes. + ----------------------------------------------------------------------*/ +void +check_point_change(MAILSTREAM *stream) +{ + if(!sp_check_cnt(stream)) + sp_set_first_status_change(stream, time(0)); + + sp_set_check_cnt(stream, sp_check_cnt(stream)+1); + dprint((9, "check_point_change(%s): increment to %d\n", + STREAMNAME(stream), sp_check_cnt(stream))); +} + + + +/*---------------------------------------------------------------------- + Call this when a mail file is written to reset timer and counter + for next check_point. + ----------------------------------------------------------------------*/ +void +reset_check_point(MAILSTREAM *stream) +{ + time_t now; + + now = time(0); + + sp_set_check_cnt(stream, 0); + sp_set_first_status_change(stream, 0); + sp_set_last_chkpnt_done(stream, now); + sp_set_last_ping(stream, now); + sp_set_last_expunged_reaper(stream, now); +} + + +int +changes_to_checkpoint(MAILSTREAM *stream) +{ + return(sp_check_cnt(stream) > 0); +} + + + +/*---------------------------------------------------------------------- + Zero the counters that keep track of mail accumulated between + commands. + ----*/ +void +zero_new_mail_count(void) +{ + int i; + MAILSTREAM *m; + + dprint((9, "New_mail_count zeroed\n")); + + for(i = 0; i < ps_global->s_pool.nstream; i++){ + m = ps_global->s_pool.streams[i]; + if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR) + && sp_mail_since_cmd(m)){ + if(pith_opt_icon_text) + (*pith_opt_icon_text)(NULL, IT_NEWMAIL); + + break; + } + } + + for(i = 0; i < ps_global->s_pool.nstream; i++){ + m = ps_global->s_pool.streams[i]; + if(m && sp_flagged(m, SP_LOCKED)) + sp_set_mail_since_cmd(m, 0L); + } +} |