From 3de1461231102f0b49264fa3346bdee334590fd2 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Thu, 22 Aug 1996 22:59:05 +0000 Subject: Added support for RFC-1725-compliant servers with UIDL and without LAST. svn path=/trunk/; revision=56 --- NEWS | 5 ++++ fetchmail.c | 52 ++++++++++++++++++++++++++++++++----- fetchmail.h | 5 ++++ fetchmail.man | 15 ++++++++++- options.c | 35 +++++++++++++++++-------- pop3.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 174 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index 40e2ca1e..08aab328 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,11 @@ S/key for secure challenge-response. Implement IMAP support. +3.05: + +* Experimental support for RFC1725-compliant POP servers with the UIDL + command and without LAST. + 3.04: * Logfile option works. diff --git a/fetchmail.c b/fetchmail.c index 65e309a6..e2687bf2 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -39,7 +39,7 @@ #include "popclient.h" /* release info */ -#define RELEASE_TAG "3.04" +#define RELEASE_TAG "3.05" #ifdef HAVE_PROTOTYPES /* prototypes for internal functions */ @@ -61,6 +61,7 @@ int quitmode; /* if --quit was set */ /* miscellaneous global controls */ char *poprcfile; /* path name of rc file */ +char *idfile; /* path name of id file */ int linelimit; /* limit # lines retrieved per site */ int versioninfo; /* emit only version info */ @@ -83,16 +84,16 @@ char *mda_argv [32]; static void termhook(); static char *lockfile; static int popstatus; +static struct hostrec *hostp, *hostlist = (struct hostrec *)NULL; main (argc,argv) int argc; char **argv; { - int mboxfd; + int mboxfd, st; struct hostrec cmd_opts, def_opts; int parsestatus; char *servername; - struct hostrec *hostp, *hostlist = (struct hostrec *)NULL; FILE *tmpfp; pid_t pid; @@ -121,6 +122,7 @@ char **argv; prc_mergeoptions(servername, &cmd_opts, &def_opts, hostp); strcpy(hostp->servername, servername); parseMDAargs(hostp); + hostp->lastid[0] = '\0'; hostp->next = hostlist; hostlist = hostp; @@ -165,7 +167,7 @@ char **argv; fscanf(fp,"%d",&pid); fprintf(stderr,"popclient: killing popclient at PID %d\n",pid); - if ( kill(pid,SIGKILL) < 0 ) + if ( kill(pid,SIGTERM) < 0 ) fprintf(stderr,"popclient: error killing the process %d.\n",pid); else fprintf(stderr,"popclient: popclient at %d is dead.\n", pid); @@ -184,6 +186,23 @@ char **argv; return(PS_EXCLUDE); } + /* let's get stored message IDs from previous transactions */ + if ((st = prc_filecheck(idfile)) != 0) { + return (st); + } else if ((tmpfp = fopen(idfile, "r")) != (FILE *)NULL) { + char buf[POPBUFSIZE+1], host[HOSTLEN+1], id[IDLEN+1]; + + while (fgets(buf, POPBUFSIZE, tmpfp) != (char *)NULL) { + if ((st = sscanf(buf, "%s %s\n", host, id)) == 2) { + for (hostp = hostlist; hostp; hostp = hostp->next) { + if (strcmp(host, hostp->servername) == 0) + strcpy(hostp->lastid, id); + } + } + } + fclose(tmpfp); + } + /* * Maybe time to go to demon mode... */ @@ -231,8 +250,27 @@ char **argv; void termhook() { - unlink(lockfile); - exit(popstatus); + FILE *tmpfp; + int idcount = 0; + + for (hostp = hostlist; hostp; hostp = hostp->next) { + if (hostp->lastid[0]) + idcount++; + } + + /* write updated last-seen IDs */ + if (!idcount) + unlink(idfile); + else if ((tmpfp = fopen(idfile, "w")) != (FILE *)NULL) { + for (hostp = hostlist; hostp; hostp = hostp->next) { + if (hostp->lastid[0]) + fprintf(tmpfp, "%s %s\n", hostp->servername, hostp->lastid); + } + fclose(tmpfp); + } + + unlink(lockfile); + exit(popstatus); } int query_host(queryctl) @@ -339,6 +377,8 @@ struct hostrec *queryctl; else printf(" Text retrieved per message will be at most %d bytes.\n", linelimit); + if (queryctl->lastid[0]) + printf(" ID of last message retrieved %s\n", queryctl->lastid); } /********************************************************************* diff --git a/fetchmail.h b/fetchmail.h index 3eaa1df9..5d071bb2 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -22,6 +22,7 @@ #define FOLDERLEN 256 /* max folder name length */ #define DIGESTLEN 33 /* length of MD5 digest */ #define MDALEN 256 /* length of delivery agent command */ +#define IDLEN 128 /* length of UIDL message ID */ /* exit code values */ #define PS_SUCCESS 0 /* successful receipt of messages */ @@ -59,6 +60,9 @@ struct hostrec { int flush; int rewrite; + /* state used for tracking UIDL ids */ + char lastid [IDLEN]; + /* dependent on the above members */ int output; struct hostrec *next; @@ -80,6 +84,7 @@ extern int quitmode; /* if --quit was set */ /* miscellaneous global controls */ extern char *poprcfile; /* path name of rc file */ +extern char *idfile; /* path name of id file */ extern int linelimit; /* limit # lines retrieved per site */ extern int versioninfo; /* emit only version info */ diff --git a/fetchmail.man b/fetchmail.man index cb38b7d8..1fc19b30 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -71,6 +71,9 @@ before retrieving new messages. .B \-f pathname, --poprc pathname Specify an alternate name for the .poprc file. .TP +.B \-i pathname, --idfile pathname +Specify an alternate name for the .popids file. +.TP .B \-k, --keep Keep retrieved messages in folder on remote mailserver. Normally, messages are deleted from the folder on the mailserver after they have been retrieved @@ -530,6 +533,13 @@ was written by Carl Harris at Virginia Polytechnic Institute and State University (a.k.a. Virginia Tech). Version 3.0 was extensively improved by Eric S. Raymond and is now maintained by esr.. .PP +.SH FILES +.TP 5 +~/.poprc +default configuration file +~/.popids +default location of file associating hosts with last message IDs seen +(used only with newer RFC1725-compliant servers supporting the UIDL command). .SH BUGS .PP The --version option doesn't display MDA arguments. @@ -542,6 +552,8 @@ dies after the first delivery. To cope with this, in daemon mode the delivery mode is automatically switched to append-and-lock if sendmail is the selected delivery agent. This needs to be fixed. .PP +The UIDL support for RFC1725-compliant servers is not yet well tested. +.PP No IMAP or RPOP support yet. .PP Send comments, bug reports, gripes, and the like to Eric S. Raymond @@ -553,4 +565,5 @@ must now be specified either manually or in your .I ~/.poprc file. .SH SEE ALSO -mail(1), binmail(1), sendmail(8), popd(8), RFC 937, RFC 1225. +mail(1), binmail(1), sendmail(8), popd(8), +RFC 937, RFC 1081, RFC 1082, RFC 1225, RFC 1460, RFC 1725. diff --git a/options.c b/options.c index 97b00530..70c9ac71 100644 --- a/options.c +++ b/options.c @@ -33,14 +33,15 @@ #define LA_PROTOCOL 10 #define LA_DAEMON 11 #define LA_POPRC 12 -#define LA_USERNAME 13 -#define LA_REMOTEFILE 14 -#define LA_LOCALFILE 15 -#define LA_MDA 16 -#define LA_LOGFILE 17 -#define LA_QUIT 18 -#define LA_NOREWRITE 19 -#define LA_YYDEBUG 20 +#define LA_IDFILE 13 +#define LA_USERNAME 14 +#define LA_REMOTEFILE 15 +#define LA_LOCALFILE 16 +#define LA_MDA 17 +#define LA_LOGFILE 18 +#define LA_QUIT 19 +#define LA_NOREWRITE 20 +#define LA_YYDEBUG 21 static char *shortoptions = "23VaKkvscl:Fd:f:u:r:o:m:L:qN"; static struct option longoptions[] = { @@ -63,6 +64,7 @@ static struct option longoptions[] = { {"local", required_argument, (int *) 0, LA_LOCALFILE }, {"mda", required_argument, (int *) 0, LA_MDA }, {"logfile", required_argument, (int *) 0, LA_LOGFILE }, + {"idfile", required_argument, (int *) 0, LA_IDFILE }, {"quit", no_argument, (int *) 0, LA_QUIT }, {"norewrite", no_argument, (int *) 0, LA_NOREWRITE }, {"yydebug", no_argument, (int *) 0, LA_YYDEBUG }, @@ -87,7 +89,7 @@ static struct option longoptions[] = { syntax errors. calls: none. globals: writes outlevel, versioninfo, yydebug, logfile, - poll_interval, quitmode, poprcfile, linelimit. + poll_interval, quitmode, poprcfile, idfile, linelimit. *********************************************************************/ int parsecmdline (argc,argv,queryctl) @@ -190,6 +192,11 @@ struct hostrec *queryctl; poprcfile = (char *) xmalloc(strlen(optarg)+1); strcpy(poprcfile,optarg); break; + case 'i': + case LA_IDFILE: + idfile = (char *) xmalloc(strlen(optarg)+1); + strcpy(idfile,optarg); + break; case 'u': case LA_USERNAME: strncpy(queryctl->remotename,optarg,sizeof(queryctl->remotename)-1); @@ -252,6 +259,7 @@ struct hostrec *queryctl; fputs(" -v, --verbose work noisily (diagnostic output)\n", stderr); fputs(" -d, --daemon run as a daemon once per n seconds\n", stderr); fputs(" -f, --poprc specify alternate config file\n", stderr); + fputs(" -i, --idfile specify alternate ID database\n", stderr); fputs(" -u, --username specify server user ID\n", stderr); fputs(" -c, --stdout write received mail to stdout\n", stderr); fputs(" -o, --local specify filename for received mail\n", stderr); @@ -279,7 +287,7 @@ struct hostrec *queryctl; return value: zero if defaults were successfully set, else non-zero (indicates a problem reading /etc/passwd). calls: none. - globals: writes outlevel, poprcfile. + globals: writes outlevel, poprcfile, idfile. *********************************************************************/ int setdefaults (queryctl) @@ -318,6 +326,13 @@ struct hostrec *queryctl; strcat(poprcfile, "/"); strcat(poprcfile, POPRC_NAME); + idfile = + (char *) xmalloc(strlen(pw->pw_dir)+strlen(IDFILE_NAME)+2); + + strcpy(idfile, pw->pw_dir); + strcat(idfile, "/"); + strcat(idfile, IDFILE_NAME); + outlevel = O_NORMAL; return(0); diff --git a/pop3.c b/pop3.c index adb865cf..d699bd50 100644 --- a/pop3.c +++ b/pop3.c @@ -40,6 +40,7 @@ int POP3_sendSTAT (int *msgcount, int socket); int POP3_sendRETR (int msgnum, int socket); int POP3_sendDELE (int msgnum, int socket); int POP3_sendLAST (int *last, int socket); +int POP3_sendUIDL (int num, int socket, char **cp); int POP3_readmsg (int socket, int mboxfd, char *host, int topipe, int rewrite); int POP3_BuildDigest (char *buf, struct hostrec *options); #endif @@ -63,7 +64,7 @@ int POP3_BuildDigest (char *buf, struct hostrec *options); int doPOP3 (queryctl) struct hostrec *queryctl; { - int ok; + int ok, use_uidl; int mboxfd; char buf [POPBUFSIZE]; int socket; @@ -124,11 +125,39 @@ struct hostrec *queryctl; /* * Ask for number of last message retrieved. * Newer, RFC-1760-conformant POP servers may not have the LAST command. - * Therefore we don't croak if we get a nonzero return. + * Therefore we don't croak if we get a nonzero return. Instead, send + * UIDL and try to find the last received ID stored for this host in + * the list we get back. */ first = 1; + use_uidl = 0; if (!queryctl->fetchall) { + char buf [POPBUFSIZE]; + char id [IDLEN]; + int num; + + /* try LAST first */ ok = POP3_sendLAST(&first, socket); + use_uidl = (ok != 0); + + /* otherwise, if we have a stored last ID for this host, + * send UIDL and search the returned list for it + */ + if (use_uidl && queryctl->lastid[0]) { + if ((ok = POP3_sendUIDL(-1, socket, 0)) == 0) { + while (SockGets(socket, buf, sizeof(buf)) == 0) { + if (outlevel == O_VERBOSE) + fprintf(stderr,"%s\n",buf); + if (strcmp(buf, ".\n") == 0) { + break; + } + if (sscanf(buf, "%d %s\n", &num, id) == 2) + if (strcmp(id, queryctl->lastid) == 0) + first = num; + } + } + } + if (ok == 0) first++; } @@ -146,6 +175,8 @@ struct hostrec *queryctl; if (count > 0) { for (number = queryctl->flush ? 1 : first; number <= count; number++) { + char *cp; + /* open the mail pipe if we're using an MDA */ if (queryctl->output == TO_MDA && (queryctl->fetchall || number >= first)) { @@ -173,6 +204,11 @@ struct hostrec *queryctl; if (ok != 0) goto cleanUp; + /* update the last-seen field for this host */ + if (use_uidl && (ok = POP3_sendUIDL(number, socket, &cp)) == 0) + (void) strcpy(queryctl->lastid, cp); + + /* maybe we delete this message now? */ if ((number < first && queryctl->flush) || !queryctl->keep) { if (outlevel > O_SILENT && outlevel < O_VERBOSE) fprintf(stderr,"flushing message %d\n", number); @@ -671,7 +707,7 @@ int rewrite; arguments: last integer buffer to receive last message# - ret. value: non-zero on success, else zero. + ret. value: zero if success, else status code. globals: SockPrintf, POP3_OK. calls: reads outlevel. *****************************************************************/ @@ -697,6 +733,46 @@ int socket; return(ok); } +/****************************************************************** + function: POP3_sendUIDL + description: send the UIDL command to the server, + + arguments: + num number of message to query (may be -1) + + ret. value: zero if success, else status code. + globals: SockPrintf, POP3_OK. + calls: reads outlevel. + *****************************************************************/ + +int POP3_sendUIDL (num, socket, cp) +int num; +int socket; +char **cp; +{ + int ok; + char buf [POPBUFSIZE]; + static char id[IDLEN]; + + (void) strcpy(buf, "UIDL\r\n"); + if (num > -1) + (void) sprintf(buf, "UIDL %d\r\n", num); + + SockPrintf(socket, buf); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> %s", buf); + + ok = POP3_OK(buf,socket); + if (ok != 0 && outlevel > O_SILENT) + fprintf(stderr,"Server says '%s' to UIDL command.\n",buf); + + if (cp) { + sscanf(buf, "%*d %s\n", id); + *cp = id; + } + return(ok); +} + /****************************************************************** function: POP3_BuildDigest -- cgit v1.2.3