diff options
| -rw-r--r-- | NEWS | 5 | ||||
| -rw-r--r-- | fetchmail.c | 52 | ||||
| -rw-r--r-- | fetchmail.h | 5 | ||||
| -rw-r--r-- | fetchmail.man | 15 | ||||
| -rw-r--r-- | options.c | 35 | ||||
| -rw-r--r-- | pop3.c | 82 | 
6 files changed, 174 insertions, 20 deletions
| @@ -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 <esr@snark.thyrsus.com> 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. @@ -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); @@ -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 | 
