From ed3bd0c5048cac6def24c3d70f3eaaf449a38a82 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sun, 28 Sep 1997 20:07:16 +0000 Subject: Added --expunge option for Richard Kooidjman. svn path=/trunk/; revision=1430 --- NEWS | 1 + driver.c | 4 ++-- etrn.c | 8 +++++++- fetchmail.c | 11 +++++++++-- fetchmail.h | 3 ++- fetchmail.man | 21 +++++++++++++++++++++ imap.c | 57 ++++++++++++++++++++++++++++++++++++++------------------- options.c | 25 +++++++++++++++++-------- pop2.c | 8 +++++++- pop3.c | 8 +++++++- rcfile_l.l | 5 +++-- rcfile_y.y | 6 +++++- sample.rcfile | 1 + 13 files changed, 120 insertions(+), 38 deletions(-) diff --git a/NEWS b/NEWS index 518c02d5..b7c2252c 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ fetchmail-4.2.8 () * Only emit length-mismatch messages in verbose mode. * POP3 lock busy error is now treated as authorization failure (Greg Stark) * Kerberos error is now treated as authorization failure (Greg Stark) +* New --expunge option allows you to better control resource use under IMAP. There are 284 people on the fetchmail-friends list. diff --git a/driver.c b/driver.c index 33c85c76..796574d4 100644 --- a/driver.c +++ b/driver.c @@ -1848,7 +1848,7 @@ const struct method *proto; /* protocol method table */ no_error: set_timeout(ctl->server.timeout); - ok = gen_transact(sock, protocol->exit_cmd); + ok = (protocol->logout_cmd)(sock, ctl); if (ok == 0) ok = (fetches > 0) ? PS_SUCCESS : PS_NOMAIL; set_timeout(0); @@ -1858,7 +1858,7 @@ const struct method *proto; /* protocol method table */ cleanUp: set_timeout(ctl->server.timeout); if (ok != 0 && ok != PS_SOCKET) - gen_transact(sock, protocol->exit_cmd); + (protocol->logout_cmd)(sock, ctl); set_timeout(0); close(sock); } diff --git a/etrn.c b/etrn.c index 59ce2fee..534e3425 100644 --- a/etrn.c +++ b/etrn.c @@ -105,6 +105,12 @@ static int etrn_getrange(int sock, struct query *ctl, char *id, int *countp, return(0); } +static int etrn_logout(int sock, struct query *ctl) +/* send logout command */ +{ + return(gen_transact(sock, "QUIT")); +} + const static struct method etrn = { "ETRN", /* ESMTP ETRN extension */ @@ -120,7 +126,7 @@ const static struct method etrn = NULL, /* no way to fetch body */ NULL, /* no message trailer */ NULL, /* how to delete a message */ - "QUIT", /* the ETRN exit command */ + etrn_logout, /* log out, we're done */ }; int doETRN (struct query *ctl) diff --git a/fetchmail.c b/fetchmail.c index 54dc45c6..c255530e 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -527,6 +527,7 @@ static int load_params(int argc, char **argv, int optind) def_opts.remotename = user; save_str(&def_opts.smtphunt, TRUE, fetchmailhost); save_str(&def_opts.smtphunt, FALSE, "localhost"); + def_opts.expunge = 1; /* this builds the host list */ if (prc_parse_file(rcfile, !versioninfo) != 0) @@ -928,7 +929,12 @@ void dump_params (struct query *ctl) if (ctl->batchlimit > 0) printf(" SMTP message batch limit is %d.\n", ctl->batchlimit); else if (outlevel == O_VERBOSE) - printf(" No SMTP message batch limit.\n"); + printf(" No SMTP message batch limit (--batchlimit 0).\n"); + if (ctl->server.protocol == P_IMAP) + if (ctl->expunge > 0) + printf(" Max deletions between expunges is %d (--expunge %d).\n", ctl->expunge, ctl->expunge); + else if (outlevel == O_VERBOSE) + printf(" No deletion limit between expunges (--expunge 0).\n"); if (ctl->mda) printf(" Messages will be delivered with '%s.'\n", visbuf(ctl->mda)); else @@ -979,7 +985,8 @@ void dump_params (struct query *ctl) if (ctl->server.envelope == STRING_DISABLED) printf(" Envelope-address routing is disabled\n"); else - printf(" Envelope header is assumed to be: %s\n", ctl->server.envelope); + printf(" Envelope header is assumed to be: %s\n", + ctl->server.envelope ? ctl->server.envelope : "Received"); } #ifdef linux if (ctl->server.interface) diff --git a/fetchmail.h b/fetchmail.h index da9c050e..df817234 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -138,6 +138,7 @@ struct query int limit; /* limit size of retrieved messages */ int fetchlimit; /* max # msgs to get in single poll */ int batchlimit; /* max # msgs to pass in single SMTP session */ + int expunge; /* max # msgs to pass between expunges */ /* unseen, previous state of mailbox (initially from .fetchids) */ struct idlist *oldsaved, *newsaved; @@ -169,7 +170,7 @@ struct method int (*fetch_body)(); /* fetch a given message */ int (*trail)(); /* eat trailer of a message */ int (*delete)(); /* delete method */ - char *exit_cmd; /* exit command */ + int (*logout_cmd)(); /* logout command */ }; #define TAGLEN 6 diff --git a/fetchmail.man b/fetchmail.man index c627fdce..5b794fb5 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -245,6 +245,24 @@ Limit the number of messages accepted from a given server in a single poll. By default there is no limit. An explicit --fetchlimit of 0 overrides any limits set in your run control file. This option does not work with ETRN. +.TP +.B -e, --expunge +(keyword: expunge) +When talking to an IMAP server, +.I fetchmail +normally issues an EXPUNGE command after each deletion in order to +force the deletion to be done immediately. This is safest when your +connection to the server is flaky and expensive, as it avoids +resending duplicate mail after a line hit. However, on large +mailboxes the overhead of re-indexing after every message can slam the +server pretty hard, so if your connection is reliable it is good to do +expunges less frequently. If you specify this option to an integer N, +it tells +.I fetchmail +to only issue expunges on every Nth delete. An argument +of zero suppresses expunges entirely (so no expunges at all will be +done until the end of run). +This option does not work with ETRN, POP2, or POP3. .SS Authentication Options .TP .B \-u name, --username name @@ -817,6 +835,9 @@ T} fetchlimit -B T{ Max # messages to forward in single connect T} +expunge -e T{ +Perform an expunge on every #th message (IMAP only) +T} syslog \& T{ Do error logging through syslog(3). T} diff --git a/imap.c b/imap.c index ef4df99c..ce30d121 100644 --- a/imap.c +++ b/imap.c @@ -33,7 +33,7 @@ extern char *strstr(); /* needed on sysV68 R3V7.1. */ #define IMAP4 0 /* IMAP4 rev 0, RFC1730 */ #define IMAP4rev1 1 /* IMAP4 rev 1, RFC2060 */ -static int count, seen, recent, unseen, deletecount, imap_version; +static int count, seen, recent, unseen, deletions, expunged, imap_version; int imap_ok(int sock, char *argbuf) /* parse command response */ @@ -441,7 +441,7 @@ static int imap_getrange(int sock, else *newp = -1; /* should never happen, RECENT is mandatory */ - deletecount = 0; + expunged = deletions = 0; return(PS_SUCCESS); } @@ -474,8 +474,8 @@ static int imap_is_old(int sock, struct query *ctl, int number) { int ok; - /* expunges change the fetch numbers */ - number -= deletecount; + /* expunged change the fetch numbers */ + number -= expunged; if ((ok = gen_transact(sock, "FETCH %d FLAGS", number)) != 0) return(PS_ERROR); @@ -489,8 +489,8 @@ static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp) char buf [POPBUFSIZE+1]; int num; - /* expunges change the fetch numbers */ - number -= deletecount; + /* expunged change the fetch numbers */ + number -= expunged; /* * This is blessed by RFC 1176, RFC1730, RFC2060. @@ -519,8 +519,8 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp) char buf [POPBUFSIZE+1], *cp; int num; - /* expunges change the fetch numbers */ - number -= deletecount; + /* expunged change the fetch numbers */ + number -= expunged; /* * If we're using IMAP4, we can fetch the message without setting its @@ -577,8 +577,8 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp) static int imap_trail(int sock, struct query *ctl, int number) /* discard tail of FETCH response after reading message text */ { - /* expunges change the fetch numbers */ - /* number -= deletecount; */ + /* expunged change the fetch numbers */ + /* number -= expunged; */ for (;;) { @@ -601,8 +601,8 @@ static int imap_delete(int sock, struct query *ctl, int number) { int ok; - /* expunges change the fetch numbers */ - number -= deletecount; + /* expunged change the fetch numbers */ + number -= expunged; /* * Use SILENT if possible as a minor throughput optimization. @@ -616,18 +616,37 @@ static int imap_delete(int sock, struct query *ctl, int number) return(ok); /* - * We do an expunge after each message, rather than just before quit, - * so that a line hit during a long session won't result in lots of - * messages being fetched again during the next session. + * We do an expunge after ctl->expunge messages, rather than + * just before quit, so that a line hit during a long session + * won't result in lots of messages being fetched again during + * the next session. */ - if ((ok = gen_transact(sock, "EXPUNGE"))) - return(ok); + if (ctl->expunge > 0 && (++deletions % ctl->expunge) == 0) + { + if ((ok = gen_transact(sock, "EXPUNGE"))) + return(ok); - deletecount++; + expunged = deletions;; + } return(PS_SUCCESS); } +static int imap_logout(int sock, struct query *ctl) +/* send logout command */ +{ + /* if expunges after deletion have been suppressed, ship one now */ + if (ctl->expunge <= 0 && deletions) + { + int ok; + + if ((ok = gen_transact(sock, "EXPUNGE"))) + return(ok); + } + + return(gen_transact(sock, "LOGOUT")); +} + const static struct method imap = { "IMAP", /* Internet Message Access Protocol */ @@ -643,7 +662,7 @@ const static struct method imap = imap_fetch_body, /* request given message body */ imap_trail, /* eat message trailer */ imap_delete, /* delete the message */ - "LOGOUT", /* the IMAP exit command */ + imap_logout, /* expunge and exit */ }; int doIMAP(struct query *ctl) diff --git a/options.c b/options.c index 4d9515cd..fc610f6b 100644 --- a/options.c +++ b/options.c @@ -45,14 +45,15 @@ #define LA_SMTPHOST 27 #define LA_BATCHLIMIT 28 #define LA_FETCHLIMIT 29 -#define LA_MDA 30 -#define LA_INTERFACE 31 -#define LA_MONITOR 32 -#define LA_YYDEBUG 33 +#define LA_EXPUNGE 30 +#define LA_MDA 31 +#define LA_INTERFACE 32 +#define LA_MONITOR 33 +#define LA_YYDEBUG 34 -/* options still left: CDegGhHjJoOqQRTUwWxXYzZ */ +/* options still left: CDgGhHjJoOqQRTUwWxXYzZ */ static const char *shortoptions = - "?Vcsvd:NqL:f:i:p:UP:A:t:E:u:akKFnl:r:S:b:B:m:I:M:y"; + "?Vcsvd:NqL:f:i:p:UP:A:t:E:u:akKFnl:r:S:b:B:e:m:I:M:y"; static const struct option longoptions[] = { /* this can be const because all flag fields are 0 and will never get set */ @@ -91,6 +92,7 @@ static const struct option longoptions[] = { {"smtphost", required_argument, (int *) 0, LA_SMTPHOST }, {"batchlimit",required_argument, (int *) 0, LA_BATCHLIMIT }, {"fetchlimit",required_argument, (int *) 0, LA_FETCHLIMIT }, + {"expunge", required_argument, (int *) 0, LA_EXPUNGE }, {"mda", required_argument, (int *) 0, LA_MDA }, #ifdef linux @@ -105,8 +107,8 @@ static const struct option longoptions[] = { int parsecmdline (argc, argv, ctl) /* parse and validate the command line options */ -int argc; /* argument count */ -char **argv; /* argument strings */ +int argc; /* argument count */ +char **argv; /* argument strings */ struct query *ctl; /* option record to be initialized */ { /* @@ -298,6 +300,12 @@ struct query *ctl; /* option record to be initialized */ if (ctl->fetchlimit == 0) ctl->fetchlimit = -1; break; + case 'e': + case LA_EXPUNGE: + ctl->expunge = atoi(optarg); + if (ctl->expunge == 0) + ctl->expunge = -1; + break; case 'm': case LA_MDA: ctl->mda = xstrdup(optarg); @@ -375,6 +383,7 @@ struct query *ctl; /* option record to be initialized */ fputs(" -S, --smtphost set SMTP forwarding host\n", stderr); fputs(" -b, --batchlimit set batch limit for SMTP connections\n", stderr); fputs(" -B, --fetchlimit set fetch limit for server connections\n", stderr); + fputs(" -e, --expunge set max deletions between expunges\n", stderr); fputs(" -r, --folder specify remote folder name\n", stderr); return(-1); } diff --git a/pop2.c b/pop2.c index c3a4b650..db34cc32 100644 --- a/pop2.c +++ b/pop2.c @@ -114,6 +114,12 @@ static int pop2_trail(int sock, struct query *ctl, int number) return(gen_transact(sock, ctl->keep ? "ACKS" : "ACKD")); } +static int pop2_logout(int sock, struct query *ctl) +/* send logout command */ +{ + return(gen_transact(sock, "QUIT")); +} + const static struct method pop2 = { "POP2", /* Post Office Protocol v2 */ @@ -129,7 +135,7 @@ const static struct method pop2 = NULL, /* no way to fetch body alone */ pop2_trail, /* eat message trailer */ NULL, /* no POP2 delete method */ - "QUIT", /* the POP2 exit command */ + pop2_logout, /* log out, we're done */ }; int doPOP2 (struct query *ctl) diff --git a/pop3.c b/pop3.c index 2f1c2a65..d219f7d0 100644 --- a/pop3.c +++ b/pop3.c @@ -443,6 +443,12 @@ static int pop3_delete(int sock, struct query *ctl, int number) return(gen_transact(sock, "DELE %d", number)); } +static int pop3_logout(int sock, struct query *ctl) +/* send logout command */ +{ + return(gen_transact(sock, "QUIT")); +} + const static struct method pop3 = { "POP3", /* Post Office Protocol v3 */ @@ -458,7 +464,7 @@ const static struct method pop3 = NULL, /* no way to fetch body alone */ NULL, /* no message trailer */ pop3_delete, /* how to delete a message */ - "QUIT", /* the POP3 exit command */ + pop3_logout, /* log out, we're done */ }; int doPOP3 (struct query *ctl) diff --git a/rcfile_l.l b/rcfile_l.l index 95df5e90..a751f6fc 100644 --- a/rcfile_l.l +++ b/rcfile_l.l @@ -21,8 +21,6 @@ int prc_lineno = 1; %% set { return SET; } -batchlimit { return BATCHLIMIT; } -fetchlimit { return FETCHLIMIT; } logfile { return LOGFILE; } daemon { return DAEMON; } syslog { return SYSLOG; } @@ -52,6 +50,9 @@ pre(connect)? { return PRECONNECT; } post(connect)? { return POSTCONNECT; } interface { return INTERFACE; } monitor { return MONITOR; } +batchlimit { return BATCHLIMIT; } +fetchlimit { return FETCHLIMIT; } +expunge { return EXPUNGE; } is { return IS; } here { return HERE; } diff --git a/rcfile_y.y b/rcfile_y.y index 63a4543d..29a64b0e 100644 --- a/rcfile_y.y +++ b/rcfile_y.y @@ -59,7 +59,8 @@ extern char * yytext; %token ENVELOPE USERNAME PASSWORD FOLDER SMTPHOST MDA %token PRECONNECT POSTCONNECT LIMIT %token IS HERE THERE TO MAP WILDCARD -%token SET BATCHLIMIT FETCHLIMIT LOGFILE DAEMON SYSLOG INTERFACE MONITOR +%token BATCHLIMIT FETCHLIMIT EXPUNGE +%token SET LOGFILE DAEMON SYSLOG INTERFACE MONITOR %token PROTO %token STRING %token NUMBER @@ -229,6 +230,7 @@ user_option : TO localnames HERE | LIMIT NUMBER {current.limit = $2;} | FETCHLIMIT NUMBER {current.fetchlimit = $2;} | BATCHLIMIT NUMBER {current.batchlimit = $2;} + | EXPUNGE NUMBER {current.expunge = $2;} ; %% @@ -425,6 +427,7 @@ static void record_current(void) FLAG_FORCE(limit); FLAG_FORCE(fetchlimit); FLAG_FORCE(batchlimit); + FLAG_FORCE(expunge); #undef FLAG_FORCE @@ -475,6 +478,7 @@ void optmerge(struct query *h2, struct query *h1) FLAG_MERGE(limit); FLAG_MERGE(fetchlimit); FLAG_MERGE(batchlimit); + FLAG_MERGE(expunge); #undef FLAG_MERGE } diff --git a/sample.rcfile b/sample.rcfile index 01b3349d..45bcc2ff 100644 --- a/sample.rcfile +++ b/sample.rcfile @@ -66,6 +66,7 @@ # limit -- must be followed by numeric size limit # fetchlimit -- must be followed by numeric msg fetch limit # batchlimit -- must be followed by numeric SMTP batch limit +# expunge -- must be followed by numeric delete count # # Legal protocol identifiers are # pop2 (or POP2) -- cgit v1.2.3