Subject: Re: automatic mail forwarding

From: [email-masked] (David W. Tamkin)
Date: Mon, 14 Feb 1994 01:14:34 -0600 (CST)

Dan Smith has a problem with this recipe:

  :0c
  * $!^From: ${ADDRESS}
  * $(!^From|^X-Forward-To):.*.${DOMAIN}
  * ^X-Forward-To: .
  |formail -I "Subject:$SUBJECT [Forwarded from: $ADDRESS]" \
         -i "Resent-To: $FORWARD" \
  	 -i "Resent-From: $ADDRESS" \
  	 -i "Resent-Date: $RESENT_DATE" \
  	 -IX-Forward-To: \
   | $SENDMAIL -oi $FORWARD

| The problem is that the "$(!^From|^X-Forward-To):.*.${DOMAIN}" regexp
| doesn't work as my comments describe; I think the reason is that "|"
| in a regexp isn't the same as a logical OR.  I know there has got to
| be a simple answer, but I can't seem to find it...

The problem is that inside the parentheses the exclamation point loses
its negation meaning and is taken as a literal exclamation point.  Unless
the last character on the line preceding "^From.:.*.${DOMAIN}" is an excla-
mation point, the first alternative in the parentheses cannot happen.  The
pipe is behaving just as he expects, I think.  (By the way, I think he means
that second period literally and should write "\." instead of ".", and the
trailing .* is not necessary for egrepping, only for substituting.)

The recipe looks as though he were trying to write these conditions:

The header must not match ^From:.*$ADDRESS;
the header must contain ^X-Forward-To: something;
if the X-Forward-To: destination is not in $DOMAIN, From: must be in $DOMAIN.

Though he might have meant to write this:

  * $!^From: ${ADDRESS}
  * $!(^From|^X-Forward-To):.*\.${DOMAIN}
  * ^X-Forward-To: .

which would have meant that

The header must not match ^From:.*$ADDRESS;
the header must be neither ^From: nor ^X-Forward-To: an address in $DOMAIN;
it must be X-Forward somewhere (outside the domain, of course).

But his text said this, which makes more sense, and which I'll assume he
really meant:

# If mail originates from within ${DOMAIN} and the forwarding
# address [is] outside of ${DOMAIN}, then the message WILL NOT be
# forwarded on.  Mail originating outside of ${DOMAIN} can be
# forwarded anywhere, and if the forwarding address is also within
# ${DOMAIN}, then all messages can be forwarded on.

There are three suggestions I can make for that.  One involves two separate
recipes to cover all situations properly.  He has a c flag on it, so we have
to make sure that no letter ever matches both or it will get forwarded twice.

  :0c # mail from outside may be forwarded anywhere
  * $!^From:.*\.${DOMAIN}
  * ^X-Forward-To: .
  |formail -I "Subject:$SUBJECT [Forwarded from: $ADDRESS]" \
	 -i "Resent-To: $FORWARD" \
  	 -i "Resent-From: $ADDRESS" \
  	 -i "Resent-Date: $RESENT_DATE" \
  	 -IX-Forward-To: \
   | $SENDMAIL -oi $FORWARD

  :0c  # anything may be forwarded within, but we handled mail from outside
  * $^From:.*\.${DOMAIN}
  * $^X-Forward-To:.*\.${DOMAIN}
  |formail -I "Subject:$SUBJECT [Forwarded from: $ADDRESS]" \
	 -i "Resent-To: $FORWARD" \
  	 -i "Resent-From: $ADDRESS" \
  	 -i "Resent-Date: $RESENT_DATE" \
  	 -IX-Forward-To: \
   | $SENDMAIL -oi $FORWARD

If Dan's version of procmail (he didn't say which he is using, though it's
recent enough to do condition-counting) allows brace nesting, he can use the
second method: set a variable in each approved situation and then run that
pipe only if the variable passes egrep.  Then he has to write the pipe only
once:

  FORWARD = !

  :0
  * $!^From:.*\.${DOMAIN}
  * ^X-Forward-To: .
  { FORWARD = "" }

  :0 # doesn't hurt to null it out twice
  * $^X-Forward-To:.*\.${DOMAIN}
  { FORWARD = "" }

  :0c
  * $$FORWARD
  |formail -I "Subject:$SUBJECT [Forwarded from: $ADDRESS]" \
	 -i "Resent-To: $FORWARD" \
  	 -i "Resent-From: $ADDRESS" \
  	 -i "Resent-Date: $RESENT_DATE" \
  	 -IX-Forward-To: \
   | $SENDMAIL -oi $FORWARD

Or we can invert the logic and, instead of saying when to forward, say
when not to, in this variation of the second method:

  :0 # See if it's to be forwarded at all and otherwise skip the structure.
  * ^X-Forward-To: .
  {
   :0 # Mail from inside to be forwarded outside gets forwarding shut off.
   * $^From:.*\.${DOMAIN}
   * $!^X-Forward-To:.*\.${DOMAIN}
   { FORWARD = ! }

   :0c
   * $$FORWARD
   |formail -I "Subject:$SUBJECT [Forwarded from: $ADDRESS]" \
	  -i "Resent-To: $FORWARD" \
	  -i "Resent-From: $ADDRESS" \
	  -i "Resent-Date: $RESENT_DATE" \
	  -IX-Forward-To: \
    | $SENDMAIL -oi $FORWARD
   }

The third method requires procmail 2.92pre9 or later to make use of weights:

  : # skip the whole thing if there's no X-Forward-To: header or an empty one
  * ^X-Forward-To: .
  {
   :0c # mail from inside to be forwarded outside scores zero and fails
   * 1^0 $!^From:.*\.${DOMAIN}
   * 1^0 $^X-Forward-To:.*\.${DOMAIN}
   |formail -I "Subject:$SUBJECT [Forwarded from: $ADDRESS]" \
	 -i "Resent-To: $FORWARD" \
  	 -i "Resent-From: $ADDRESS" \
  	 -i "Resent-Date: $RESENT_DATE" \
  	 -IX-Forward-To: \
   | $SENDMAIL -oi $FORWARD
  }

David W. Tamkin  P. O. Box 3284  Skokie IL  60076-6284
[email-masked]   MCI Mail: 426-1818   +1 312 714 5610

---

From: [email-masked] (J. Daniel Smith)
Date: Thu, 17 Feb 94 17:56:57 -0500

David W. Tamkin ([email-masked]) writes on 14 February 1994 at 01:14:34 -0600
>[...]
>[...]
>[...]
I'm running version 2.90 or procmail, so I'm missing out on a few of
the newer features.

In any case, I combined a couple of David's ideas and came up with the
following
-----
# mail from outside may be forwarded anywhere
:0hf
* $!^From:${ADDRESS}
* $!^From:.*\.${DOMAIN}
* ^X-Forward-To: .
| formail -i "X-Forwarding-Ok: YES"
# anything may be forwarded within domain, but we already handled mail
# from outside above 
:0hf
* $!^From:${ADDRESS}
* $^From:.*\.${DOMAIN}
* $^X-Forward-To:.*\.${DOMAIN}
| formail -i "X-Forwarding-Ok: YES"

:0c
* ^X-Forwarding-Ok: YES$
|formail -I "Subject:$SUBJECT [Forwarded from: $ADDRESS]" \
         -i "Resent-To: $FORWARD" \
	 -i "Resent-From: $ADDRESS" \
	 -i "Resent-Date: $RESENT_DATE" \
	 -IX-Forward-To: -IX-Forwarding-Ok: \
 | $SENDMAIL -oi $FORWARD
#
# Use the regexp from the previous recipe to set an environment
# variable indicating if messages can be forwarded.  This is used below
# to tailer the message sent back to the original sender.
FWD_OK="NO"
:Ac
FWD_OK=|echo "YES"
-----

Sure, its not nice having to invoke "formail" an extra time just to
insert a header that will be removed in the next recipe, but I didn't
want to duplicate adding all the "Resent-" headers.

In case anybody else out there is interested, I've appended the entire
procmail script for users who have left.  This works in conjunction
with the alias file as follows:
-----
# Joe Schmoe: joe -> [email-masked]	(31 December 1999)
joe:"|/usr/local/bin/procmail \
     USER=joe FORWARD=[email-masked] \
     /etc/mail/not_here"

# Billy Bob: NO FORWARDING ADDRESS	(31 December 1999)
billy:"|/usr/local/bin/procmail USER=billy /etc/mail/not_here"
-----

   Dan
==== message is author's opinion only -- it is not an official statement ====
J. Daniel Smith                                      Applicon, Inc.
(313) 995-6395, 995-6171 (FAX)                       Ann Arbor, Michigan (USA)
[email-masked] (Internet), AAACA1::DSMITH (SINet)
-----
# 
# J. Daniel Smith
# 18 April 1993
#
# ~/.procmailrc file to process mail for users that are no longer at
# Applicon.
#   * bounce the message back to the sender telling them user is no
#     longer here and give forwarding address
#   * forward message onto recipient using new address with a comment
#     telling them is passed through Applicon

PATH=/bin:/usr/bin:/usr/local/bin
SHELL=/bin/sh

# log everything verbosely, since I want to see how all this works
LOGFILE=/etc/mail/LOG
VERBOSE=on

# cause procmail to exit with exit code 67 = EX_NOUSER; this will make
# sendmail bounce the message   
TRAP="exit 67;"

##### THIS MUST BE THE FIRST RECIPIE
# Add an X-Forward-To: header to the mail message so that later
# recipies can check the forwarding address.
# Also be sure there is a Subject: line in the message
##### THIS MUST BE THE FIRST RECIPIE
:0hf
| formail -i "X-Forward-To: ${FORWARD}" -a "Subject: No subject given!"

# find intereseting header lines
FROM=`formail -xFrom:`
TO=`formail -xTo:`
CC=`formail -xCc:`
SUBJECT=`formail -xSubject:`
DATE=`formail -xDate:`

# get the date in RFC822 format for insertion into some messages;
# the "Resent-Date:" field is copied from the "Date:" field on some systems.
# RFC1123 says "All mail software SHOULD use 4-digit years in dates..."
RESENT_DATE=`date '+%a, %d %h %Y %H:%M:%S %Z'`

# The domain to check against for mail forwarding.  This is used to
# prevent mail messages that may contain confidential information from
# being automatically forwarded onto the forwarding address.
#
# If mail originates from within ${DOMAIN} and the forwarding
# address is outside of ${DOMAIN}, then the message WILL NOT be
# forwarded on.  Mail originating outside of ${DOMAIN} can be
# forwarded anywhere, and if the forwarding address is also within
# ${DOMAIN}, then all messages can be forwarded on.
DOMAIN=slb.com

# several sub-domains could exist in ${DOMAIN}.  This is our
# fully-qualified domain name
FQDN=ann-arbor.applicon.${DOMAIN}

# USER is set on the command line from the sendmail aliases file when
# procmail is invoked.  Since messages likely will be sent off-site,
# we want to only use a fully-qualified address.
ADDRESS=${USER}@${FQDN}

#
# forward the message onto the new address if its OK.  Remove the
# X-Forward-To: line we just added above if the message is resent.
# Also, add various Resent-* headers to the forwarded message and
# change the Subject: line so the recipient will know it passed through
# our system.

# mail from outside may be forwarded anywhere
:0hf
* $!^From:${ADDRESS}
* $!^From:.*\.${DOMAIN}
* ^X-Forward-To: .
| formail -i "X-Forwarding-Ok: YES"
# anything may be forwarded within domain, but we already handled mail
# from outside above 
:0hf
* $!^From:${ADDRESS}
* $^From:.*\.${DOMAIN}
* $^X-Forward-To:.*\.${DOMAIN}
| formail -i "X-Forwarding-Ok: YES"

:0c
* ^X-Forwarding-Ok: YES$
|formail -I "Subject:$SUBJECT [Forwarded from: $ADDRESS]" \
         -i "Resent-To: $FORWARD" \
	 -i "Resent-From: $ADDRESS" \
	 -i "Resent-Date: $RESENT_DATE" \
	 -IX-Forward-To: -IX-Forwarding-Ok: \
 | $SENDMAIL -oi $FORWARD
#
# Use the regexp from the previous recipe to set an environment
# variable indicating if messages can be forwarded.  This is used below
# to tailer the message sent back to the original sender.
FWD_OK="NO"
:Ac
FWD_OK=|echo "YES"

# "bounce" a message back saying this addressee has moved on.
# Carbon copy the forwarded address and the local postmaster.
# Add a "Precedence: bulk" header line to keep programs like
# vacation(1) from trying to respond to this message.
:0h
* $!^From: ${ADDRESS}
|(formail -r \
          -I "Subject: your mail to \"$TO\"" \
          -I "From: Postmaster@${FQDN}" \
	  -I "Precedence: bulk" \
          -A "CC: $FORWARD, Postmaster@${FQDN}" ;\
  echo "Mail can no longer be delivered locally to: $ADDRESS" ;\
  echo "" ;\
  echo "Your message of: $DATE" ;\
  echo "Sent to: $TO" ;\
  if [ "$CC" != "" ]; then \
    echo "Carbon copies to: $CC" ;\
  fi ;\
  echo "Regarding: $SUBJECT" ;\
  if [ "$FWD_OK" = "YES" ]; then \
    echo "Has been forwared onto $FORWARD" ;\
  else \
    echo "Has been bounced back to the originator." ;\
    if [ "$FORWARD" != "" ]; then \
      echo "Mail originating within the \"$DOMAIN\" domain can not be" ;\
      echo "automatically forwarded outside of Schlumberger." ;\
    else \
      echo "No forwarding address is available for $ADDRESS" ;\
    fi ;\
  fi ;\
  echo "" ;\
  if [ "$FORWARD" != "" ]; then \
    echo "Please direct all future correspondence to this addressee's" ;\
    echo "new address of: $FORWARD" ;\
    echo "" ;\
  fi; \
  echo "If you maintain a mailing list, please determine if" ;\
  echo "$ADDRESS (or some form of \"$USER\") can be" ;\
  if [ "$FORWARD" != "" ]; then \
    echo "be changed to the new address shown above." ;\
  else \
    echo "removed from it." ;\
  fi ;\
  echo ""; echo "" ;\
  echo "This message was generated automatically by a \"procmail\" script" ;\
  echo "on the machine aamail.$FQDN located at" ;\
  echo "Applicon, Inc. in Ann Arbor, MI, USA.  Questions regarding this" ;\
  echo "message itself should be sent to:" ;\
  echo "   Postmaster@$FQDN" ) \
  | $SENDMAIL -t

# pitch anything that gets through to here; we don't want it
# ending up in somebody's mailbox
:0
/dev/null