diff options
| -rw-r--r-- | NEWS | 3 | ||||
| -rw-r--r-- | conf.c | 1 | ||||
| -rw-r--r-- | design-notes.html | 17 | ||||
| -rw-r--r-- | fetchmail.c | 9 | ||||
| -rw-r--r-- | fetchmail.h | 1 | ||||
| -rw-r--r-- | fetchmail.man | 17 | ||||
| -rwxr-xr-x | fetchmailconf | 18 | ||||
| -rw-r--r-- | options.c | 70 | ||||
| -rw-r--r-- | rcfile_l.l | 1 | ||||
| -rw-r--r-- | rcfile_y.y | 6 | ||||
| -rw-r--r-- | sink.c | 44 | 
11 files changed, 122 insertions, 65 deletions
@@ -10,6 +10,9 @@ fetchmail-4.7.8 ():  * FreeBSD support for interface and monitor options by Andy Doran <ad@psn.ie>.  * Fixed server-deletion bug in fetchmailconf.  * Timestamps now generated into logfiles at start of poll cycle. +* New `nobounce' debugging option (specifically exempted from feature freeze) +  allows SMTP error bouncemail to be redirected from sender to local +  postmaster.  There are 257 people on fetchmail-friends and 348 on fetchmail-announce. @@ -188,6 +188,7 @@ void dump_config(struct runctl *runp, struct query *querylist)      stringdump("logfile", runp->logfile);      stringdump("idfile", runp->idfile);      stringdump("postmaster", runp->postmaster); +    booldump("bouncemail", runp->bouncemail);      stringdump("properties", runp->properties);      booldump("invisible", runp->invisible);      booldump("syslog", runp->use_syslog); diff --git a/design-notes.html b/design-notes.html index 8a48ba6f..fcc446bf 100644 --- a/design-notes.html +++ b/design-notes.html @@ -10,7 +10,7 @@  <table width="100%" cellpadding=0><tr>  <td width="30%">Back to <a href="/~esr/index.html">Fetchmail Home Page</a>  <td width="30%" align=center>To <a href="/~esr/sitemap.html">Site Map</a> -<td width="30%" align=right>$Date: 1999/01/01 18:49:07 $ +<td width="30%" align=right>$Date: 1999/02/07 21:06:09 $  </table>  <HR>  <H1 ALIGN=CENTER>Design Notes On Fetchmail</H1> @@ -318,15 +318,15 @@ a lot of fiddly details in the process.  You'll need to do the  following minimum steps.  <UL> -<LI>Add a field to represent the control in <code>struct query</code> or -    <code>struct hostdata</code>. - -<LI>Pick a token to declare the option in the .fetchmailrc file.  Add -    the token to <code>rcfile_l</code>.   +<LI>Add a field to represent the control in <code>struct run</code>, +    <code>struct query</code>, or <code>struct hostdata</code>.  <LI>Go to <code>rcfile_y.y</code>.  Add the token to the grammar. Don't      forget the <code>%token</code> declaration.   +<LI>Pick an actual string to declare the option in the .fetchmailrc file.  Add +    the token to <code>rcfile_l</code>.   +  <LI>Pick a long-form option name, and a one-letter short option if any      are left.  Go to <code>options.c</code>.  Pick a new <code>LA_</code>      value.  Hack the <code>longoptions</code> table to set up the @@ -339,7 +339,8 @@ following minimum steps.  <LI>Add code to dump the option value in <code>fetchmail.c:dump_params</code>.  <LI>Add proper <code>FLAG_MERGE</code> actions in fetchmail.c's -    optmerge() function. +    optmerge() function.  (If it's a global option, add an override  +    at the end of load_params.  <LI>Document the option in fetchmail.man.  This will require at least      two changes; one to the collected table of options, and one full @@ -530,7 +531,7 @@ all shaped the design in one way or another.<P>  <table width="100%" cellpadding=0><tr>  <td width="30%">Back to <a href="index.html">Fetchmail Home Page</a>  <td width="30%" align=center>To <a href="/~esr/sitemap.html">Site Map</a> -<td width="30%" align=right>$Date: 1999/01/01 18:49:07 $ +<td width="30%" align=right>$Date: 1999/02/07 21:06:09 $  </table>  <P><ADDRESS>Eric S. Raymond <A HREF="mailto:esr@thyrsus.com"><esr@snark.thyrsus.com></A></ADDRESS> diff --git a/fetchmail.c b/fetchmail.c index 03af1481..cfbbb863 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -874,6 +874,8 @@ static int load_params(int argc, char **argv, int optind)      struct passwd *pw;      struct query def_opts, *ctl; +    run.bouncemail = TRUE; +      memset(&def_opts, '\0', sizeof(struct query));      def_opts.smtp_socket = -1;      def_opts.smtpaddress = (char *)0; @@ -1161,6 +1163,8 @@ static int load_params(int argc, char **argv, int optind)  	run.use_syslog = (cmd_run.use_syslog == FLAG_TRUE);      if (cmd_run.postmaster)  	run.postmaster = cmd_run.postmaster; +    if (cmd_run.bouncemail) +	run.bouncemail = cmd_run.bouncemail;      /* check and daemon options are not compatible */      if (check_only && run.poll_interval) @@ -1351,6 +1355,11 @@ static void dump_params (struct runctl *runp,  	printf(_("Fetchmail will forward misaddressed multidrop messages to %s.\n"),  	       runp->postmaster); +    if (!runp->bouncemail) +	printf(_("Fetchmail will direct error mail to the postmaster.\n")); +    else if (outlevel >= O_VERBOSE) +	printf(_("Fetchmail will direct error mail to the sender.\n")); +      for (ctl = querylist; ctl; ctl = ctl->next)      {  	if (!ctl->active || (implicit && ctl->server.skip)) diff --git a/fetchmail.h b/fetchmail.h index e0e53933..3549130a 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -104,6 +104,7 @@ struct runctl      char	*idfile;      int		poll_interval;      char	*postmaster; +    flag	bouncemail;      char	*properties;      flag	use_syslog;      flag	invisible; diff --git a/fetchmail.man b/fetchmail.man index 0cb7f688..8005cc6e 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -765,6 +765,12 @@ can be found.  Normally this is just the user who invoked fetchmail.  If the invoking user is root, then the default of this option is  the user `postmaster'.  .PP +The +.B --nobounce +option suppresses the normal action of bouncing errors back to the  +sender in an RFC1894-conformant error message.  If nobounce is on, the +message will go to the postmaster instead. +.PP  The   .B --invisible  option (keyword: set invisible) tries to make fetchmail invisible. @@ -956,6 +962,9 @@ T}  set postmaster  	\&	T{  Give the name of the last-resort mail recipient  T} +set nobouncemail	\&	T{ +Direct error mail to postmaster rather than sender +T}  set logfile  	\&	T{  Name of a file to dump error and status messages to  T} @@ -1216,10 +1225,10 @@ of `dns', `checkalias', `localdomains', and `aka' for details on how  matching addresses are handled.  .PP  If \fIfetchmail\fR cannot match any mailserver usernames or -localdomain addresses, the default recipient is the value of the -`postmaster' global option if that has been set; otherwise it's the -calling user (as set by the USER or LOGNAME variable in the -environment). +localdomain addresses, the mail will be bounced. +Normally it will be bounced to the sender, but if `nobounce' is on +it will go to the postmaster (which in turn defaults to being the +calling user).  .PP  The `dns' option (normally on) controls the way addresses from  multidrop mailboxes are checked.  On, it enables logic to check each diff --git a/fetchmailconf b/fetchmailconf index 75e0de01..9537cb14 100755 --- a/fetchmailconf +++ b/fetchmailconf @@ -19,6 +19,7 @@ class Configuration:  	self.logfile = None		# No logfile, initially  	self.idfile = os.environ["HOME"] + "/.fetchids"		# Default idfile, initially          self.postmaster = None		# No last-resort address, initially +        self.bouncemail = TRUE		# Vounce errors to users          self.properties = None		# No exiguous properties  	self.invisible = FALSE		# Suppress Received line & spoof?  	self.syslog = FALSE		# Use syslogd for logging? @@ -28,6 +29,7 @@ class Configuration:  	    ('logfile',		'String'),  	    ('idfile',		'String'),  	    ('postmaster',	'String'), +	    ('bouncemail',	'Boolean'),  	    ('properties',	'String'),  	    ('syslog',		'Boolean'),  	    ('invisible',	'Boolean')) @@ -42,6 +44,10 @@ class Configuration:  	    str = str + ("set idfile \"%s\"\n" % (self.idfile,));  	if self.postmaster != ConfigurationDefaults.postmaster:  	    str = str + ("set postmaster \"%s\"\n" % (self.postmaster,)); +        if self.bouncemail: +            str = str + ("set bouncemail\n") +        else: +            str = str + ("set nobouncemail\n")  	if self.properties != ConfigurationDefaults.properties:  	    str = str + ("set properties \"%s\"\n" % (self.properties,));  	if self.poll_interval > 0: @@ -591,6 +597,10 @@ Postmaster          invoking user as the address of last resort unless that user is          root.  If that user is root, fetchmail sends to `postmaster'. +Bounces to sender? +	If this option is on (the default) error mail goes to the sender. +        Otherwise it goes to the postmaster. +  Invisible          If false (the default) fetchmail generates a Received line into          each message and generates a HELO from the machine it is running on. @@ -658,6 +668,7 @@ class ConfigurationEdit(Frame, MyWidget):              # Set the postmaster              log = LabeledEntry(ff, '     Postmaster:', self.postmaster, '14')              log.pack(side=RIGHT, anchor=E) +          # Set the poll interval          de = LabeledEntry(ff, '     Poll interval:', self.poll_interval, '14')          de.pack(side=RIGHT, anchor=E) @@ -666,6 +677,13 @@ class ConfigurationEdit(Frame, MyWidget):          df.pack()          if self.mode != 'novice': +            pf = Frame(gf) +            Checkbutton(pf, +		{'text':'Bounces to sender?', +		'variable':self.bouncemail, +		'relief':GROOVE}).pack(side=LEFT, anchor=W) +            pf.pack(fill=X) +              sf = Frame(gf)              Checkbutton(sf,  		{'text':'Log to syslog?', @@ -37,38 +37,39 @@  #define LA_RCFILE	13  #define LA_IDFILE	14  #define LA_POSTMASTER	15 -#define LA_PROTOCOL	16 -#define LA_UIDL		17 -#define LA_PORT		18 -#define LA_AUTHENTICATE	19 -#define LA_TIMEOUT	20 -#define LA_ENVELOPE	21 -#define LA_QVIRTUAL     22 -#define LA_USERNAME	23 -#define LA_ALL          24 -#define LA_NOKEEP	25 -#define	LA_KEEP		26 -#define LA_FLUSH        27 -#define LA_NOREWRITE	28 -#define LA_LIMIT	29 -#define LA_WARNINGS	30 -#define LA_FOLDER	31 -#define LA_SMTPHOST	32 -#define LA_SMTPADDR     33 -#define LA_ANTISPAM	34 -#define LA_BATCHLIMIT	35 -#define LA_FETCHLIMIT	36 -#define LA_EXPUNGE	37 -#define LA_MDA		38 -#define LA_BSMTP	39 -#define LA_LMTP		40 -#define LA_PLUGIN	41 -#define LA_PLUGOUT	42 -#define LA_NETSEC	43 -#define LA_INTERFACE    44 -#define LA_MONITOR      45 -#define LA_CONFIGDUMP	46 -#define LA_YYDEBUG	47 +#define LA_NOBOUNCE	16 +#define LA_PROTOCOL	17 +#define LA_UIDL		18 +#define LA_PORT		19 +#define LA_AUTHENTICATE	20 +#define LA_TIMEOUT	21 +#define LA_ENVELOPE	22 +#define LA_QVIRTUAL     23 +#define LA_USERNAME	24 +#define LA_ALL          25 +#define LA_NOKEEP	26 +#define	LA_KEEP		27 +#define LA_FLUSH        28 +#define LA_NOREWRITE	29 +#define LA_LIMIT	30 +#define LA_WARNINGS	31 +#define LA_FOLDER	32 +#define LA_SMTPHOST	33 +#define LA_SMTPADDR     34 +#define LA_ANTISPAM	35 +#define LA_BATCHLIMIT	36 +#define LA_FETCHLIMIT	37 +#define LA_EXPUNGE	38 +#define LA_MDA		39 +#define LA_BSMTP	40 +#define LA_LMTP		41 +#define LA_PLUGIN	42 +#define LA_PLUGOUT	43 +#define LA_NETSEC	44 +#define LA_INTERFACE    45 +#define LA_MONITOR      46 +#define LA_CONFIGDUMP	47 +#define LA_YYDEBUG	48  /* options still left: CDgGhHjJoORwWxXYz */  static const char *shortoptions =  @@ -91,6 +92,7 @@ static const struct option longoptions[] = {    {"fetchmailrc",required_argument,(int *) 0, LA_RCFILE      },    {"idfile",	required_argument, (int *) 0, LA_IDFILE      },    {"postmaster",required_argument, (int *) 0, LA_POSTMASTER  }, +  {"nobounce",  no_argument,       (int *) 0, LA_NOBOUNCE    },    {"protocol",	required_argument, (int *) 0, LA_PROTOCOL    },    {"proto",	required_argument, (int *) 0, LA_PROTOCOL    }, @@ -297,6 +299,9 @@ struct query *ctl;	/* option record to be initialized */  	    rctl->postmaster = (char *) xmalloc(strlen(optarg)+1);  	    strcpy(rctl->postmaster,optarg);  	    break; +	case LA_NOBOUNCE: +	    run.bouncemail = FALSE; +	    break;  	case 'p':  	case LA_PROTOCOL:  	    /* XXX -- should probably use a table lookup here */ @@ -565,6 +570,7 @@ struct query *ctl;	/* option record to be initialized */  	P(_("  -f, --fetchmailrc specify alternate run control file\n"));  	P(_("  -i, --idfile      specify alternate UIDs file\n"));  	P(_("      --postmaster  specify recipient of last resort\n")); +	P(_("      --nobounce    redirect bounces from user to postmaster."));  #if (defined(linux) && !INET6) || defined(__FreeBSD__)  	P(_("  -I, --interface   interface required specification\n"));  	P(_("  -M, --monitor     monitor interface for activity\n")); @@ -28,6 +28,7 @@ daemon		{ return DAEMON; }  syslog		{ return SYSLOG; }  invisible	{ return INVISIBLE; }  postmaster	{ return POSTMASTER; } +bouncemail	{ return BOUNCEMAIL; }  warnings	{ return WARNINGS; }  defaults 	{ return DEFAULTS; } @@ -60,11 +60,11 @@ extern char * yytext;  %token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL  %token AUTHENTICATE TIMEOUT KPOP SDPS KERBEROS4 KERBEROS5 KERBEROS  %token ENVELOPE QVIRTUAL USERNAME PASSWORD FOLDER SMTPHOST MDA BSMTP LMTP -%token SMTPADDRESS SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT +%token SMTPADDRESS SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS  %token NETSEC INTERFACE MONITOR PLUGIN PLUGOUT  %token IS HERE THERE TO MAP WILDCARD  %token BATCHLIMIT FETCHLIMIT EXPUNGE PROPERTIES -%token SET LOGFILE DAEMON SYSLOG IDFILE INVISIBLE POSTMASTER WARNINGS +%token SET LOGFILE DAEMON SYSLOG IDFILE INVISIBLE POSTMASTER BOUNCEMAIL  %token <proto> PROTO  %token <sval>  STRING  %token <number> NUMBER @@ -88,6 +88,8 @@ statement	: SET LOGFILE optmap STRING	{run.logfile = xstrdup($4);}  		| SET IDFILE optmap STRING	{run.idfile = xstrdup($4);}  		| SET DAEMON optmap NUMBER	{run.poll_interval = $4;}  		| SET POSTMASTER optmap STRING	{run.postmaster = xstrdup($4);} +		| SET BOUNCEMAIL		{run.bouncemail = TRUE;} +		| SET NO BOUNCEMAIL		{run.bouncemail = FALSE;}  		| SET PROPERTIES optmap STRING	{run.properties =xstrdup($4);}  		| SET SYSLOG			{run.use_syslog = TRUE;}  		| SET INVISIBLE			{run.invisible = TRUE;} @@ -252,18 +252,21 @@ static void sanitize(char *s)      	*cp = '_';  } -static int send_bouncemail(struct msgblk *msg, int userclass, -			   char *message, int nerrors, char *errors[]) +static int send_bouncemail(struct query *ctl, struct msgblk *msg, +			   int userclass, char *message, +			   int nerrors, char *errors[])  /* bounce back an error report a la RFC 1892 */  {      char daemon_name[18 + HOSTLEN] = "FETCHMAIL-DAEMON@"; -    char boundary[BUFSIZ], *ts; +    char boundary[BUFSIZ], *ts, *bounce_to;      int sock;      /* don't bounce  in reply to undeliverable bounces */      if (!msg->return_path[0] || strcmp(msg->return_path, "<>") == 0)  	return(FALSE); +    bounce_to = (run.bouncemail ? msg->return_path : run.postmaster); +      SMTP_setmode(SMTP_MODE);      strcat(daemon_name, fetchmailhost); @@ -273,7 +276,7 @@ static int send_bouncemail(struct msgblk *msg, int userclass,      		|| SMTP_ok(sock) != SM_OK   		|| SMTP_helo(sock, "localhost") != SM_OK  		|| SMTP_from(sock, daemon_name, (char *)NULL) != SM_OK -		|| SMTP_rcpt(sock, msg->return_path) != SM_OK +		|| SMTP_rcpt(sock, bounce_to) != SM_OK  		|| SMTP_data(sock) != SM_OK)  	return(FALSE); @@ -289,7 +292,7 @@ static int send_bouncemail(struct msgblk *msg, int userclass,      /* bouncemail headers */      SockPrintf(sock, "Return-Path: <>\r\n");      SockPrintf(sock, "From: %s\r\n", daemon_name); -    SockPrintf(sock, "To: %s\r\n", msg->return_path); +    SockPrintf(sock, "To: %s\r\n", bounce_to);      SockPrintf(sock, "MIME-Version: 1.0\r\n");      SockPrintf(sock, "Content-Type: multipart/report; report-type=delivery-status;\r\n\tboundary=\"%s\"\r\n", boundary);      SockPrintf(sock, "\r\n"); @@ -367,6 +370,7 @@ static int send_bouncemail(struct msgblk *msg, int userclass,  static int handle_smtp_report(struct query *ctl, struct msgblk *msg)  /* handle SMTP errors based on the content of SMTP_response */ +/* Mail is deleted from the server if this function returns PS_REFUSED. */  {      int smtperr = atoi(smtp_response);      char *responses[1]; @@ -399,7 +403,7 @@ static int handle_smtp_report(struct query *ctl, struct msgblk *msg)  	 * 571 = sendmail's "unsolicited email refused"  	 *  	 */ -	send_bouncemail(msg, XMIT_ACCEPT, +	send_bouncemail(ctl, msg, XMIT_ACCEPT,  			"Our spam filter rejected this transaction.\r\n",   			1, responses);  	return(PS_REFUSED); @@ -439,26 +443,26 @@ static int handle_smtp_report(struct query *ctl, struct msgblk *msg)  	 * ESMTP server.  Don't try to ship the message,   	 * and allow it to be deleted.  	 */ -	send_bouncemail(msg, XMIT_ACCEPT, +	send_bouncemail(ctl, msg, XMIT_ACCEPT,  			"This message was too large.\r\n",   			1, responses); -	return(PS_REFUSED); - +	return(run.bouncemail ? PS_REFUSED : PS_TRANSIENT); +        case 553: /* invalid sending domain */  	/*  	 * These latter days 553 usually means a spammer is trying to  	 * cover his tracks.  	 */ -	send_bouncemail(msg, XMIT_ACCEPT, +	send_bouncemail(ctl, msg, XMIT_ACCEPT,  			"Invalid address.\r\n",   			1, responses);  	return(PS_REFUSED);      default:	/* bounce the error back to the sender */ -	if (send_bouncemail(msg, XMIT_ACCEPT, +	if (send_bouncemail(ctl, msg, XMIT_ACCEPT,  			"General SMTP/ESMTP error.\r\n",   			1, responses)) -	    return(PS_REFUSED); +	    return(run.bouncemail ? PS_REFUSED : PS_TRANSIENT);  	else  	    return(PS_TRANSIENT);      } @@ -761,7 +765,7 @@ int open_sink(struct query *ctl, struct msgblk *msg,  		}  	    }  	if (*bad_addresses) -	    send_bouncemail(msg, XMIT_RCPTBAD, +	    send_bouncemail(ctl, msg, XMIT_RCPTBAD,                              "Some addresses were rejected by the MDA fetchmail forwards to.\r\n",                              *bad_addresses, from_responses);  	/* @@ -941,13 +945,15 @@ int close_sink(struct query *ctl, struct msgblk *msg, flag forward)  		else  		    /*  		     * One or more deliveries failed. -		     * If we can bounce a failures list back to the sender, - 		     * return TRUE, deleting the message from the server so - 		     * it won't be re-forwarded on subsequent poll cycles. +		     * If we can bounce a failures list back to the +		     * sender, and the postmaster does not want to +		     * deal with the bounces return TRUE, deleting the +		     * message from the server so it won't be +		     * re-forwarded on subsequent poll cycles.  		     */ -		    return(send_bouncemail(msg, XMIT_ACCEPT, -				   "LSMTP partial delivery failure.\r\n", -				   errors, responses)); + 		  return(send_bouncemail(ctl, msg, XMIT_ACCEPT, +					 "LSMTP partial delivery failure.\r\n", +					 errors, responses));  	    }      }  | 
