procmail
[Top] [All Lists]

Re: Rescue of unfiltered data

1999-11-09 22:13:39
Beth Clarke <beth(_at_)the-hug(_dot_)org> writes:
Occasionally while processing a receipe procmail gives the
following error:

procmail: Error while writing to " ($FORMAIL -rt \
   -I"From: ${ALTFROM}" \
   -A"X-Loop: ${NOLOOP}";\
   cat ${SBDIR}/msworddoc; \
   sed 's/^/> /g' tmsworddoc; \
   ${RM} tmsworddoc) \
   | ${SENDMAIL} -oi -t"
procmail: Rescue of unfiltered data succeeded

The reciepe is:

:0 BHh
* ^Content-Type:.*application/ms 
{
:0 c:
| $FORMAIL -A"X-Sorted: Microsoft application" >>$CLIVE 

:0 c:
tmsworddoc

:0 f: tmsworddoc.lock
| ($FORMAIL -rt \
   -I"From: ${ALTFROM}" \
   -A"X-Loop: ${NOLOOP}";\
   cat ${SBDIR}/msworddoc; \
   sed 's/^/> /g' tmsworddoc; \
   ${RM} tmsworddoc) \
   | ${SENDMAIL} -oi -t
}

I dont understand why it works most of the time, but occasionally
procmail chokes on it. 

The idea of it that it matchs on an ms applicattion content-type
line. Then appends a copy in $CLIVE which is a file. Saves the body to
another file. Then produces a reply to the sender with a helpfull
message explaining why we don't accept microsoft attachments and
appends a quoted copy of the original. Maybe there is a better way of
doing this?

Okay, there are a few problems here.  The reason you're _sometimes_
getting an "Error while writing to..." error is that procmail normally
keeps track of whether programs started as actions read in the entire
message.  If they don't then procmail assumes they failed in some way,
unless the recipe had the 'i' flag telling procmail to ignore such
write errors.  You only see the error sometimes and not always because
the kernel will buffer some number of bytes (4096 is common) so that if
the unread portion fits in that then procmail won't see a write error.

Okay, so why isn't the action shown above reading the entire message?
Because it doesn't need to.  The only process in the action that reads
anything from procmail is formail, and formail -rt only reads the header
unless you give it the -k flag to keep the body.  Since you don't _want_
the body, you should just tell procmail to not even try feeding it to
the action by putting the 'h' flag on the recipe.

The next problem is that the recipe has the 'f' flag on it.  That flag
tells procmail to read back the output the action and treat that as the
message from then on in the rcfile.  You don't actually want to do that,
given that the output that action will be _nothing_ unless sendmail
spits something out, and if it did spit something out you wouldn't want
to treat it as an e-mail message anyway.  So, remove that 'f' flag.

Then there's a locking problem: if two messages arrive close together
in time that contain such attachments then two procmail process could
be processing the above recipes at the same time.  There's nothing to
prevent one process from writing its message to tmsworddoc, then the
other process appends its message to that file, then the first process
mails both of them to the first sender and removes the file, leaving
nothing for the second process to e-mail.  There are two solutions:
one is to use a regional lockfile via the LOCKFILE variable.  The
other is to generate the reply header in an earlier recipe and then
have the mail recipe get the original message via the pipe from
procmail.  Here are the two solutions, for comparison:

        :0 BH
        * ^Content-Type:.*application/ms 
        {
            :0 c:
            | formail -A"X-Sorted: Microsoft application" >>$CLIVE 

            # What's the name of the temp file?
            TFILE = tmsworddoc

            # Set a regional lockfile to avoid running into ourselves here
            LOCKFILE = $TFILE.lock

            # Don't use a locallockfile here as we're under the regional one
            :0 c
            $TFILE

            :0 h
            | ( formail -rt \
                        -I"From: ${ALTFROM}" \
                        -A"X-Loop: ${NOLOOP}"; \
                cat ${SBDIR}/msworddoc; \
                sed 's/^/> /g' $TFILE \
              ) | ${SENDMAIL} -oi -t; \
              rm -f $TFILE

            # If something went wrong in the above, explicitly
            # remove the temp file and then drop the lock
            :0ih
            | rm -f $TFILE
            LOCKFILE
        }


Here's solution two, using no tempfiles:

        :0 BH
        * ^Content-Type:.*application/ms 
        {
            :0 c:
            | formail -A"X-Sorted: Microsoft application" >>$CLIVE 

            # Generate the reply header and store it in the REPLHEADER variable
            :0 h
            REPLHEADER=|formail -rt -I"From: ${ALTFROM}" -A"X-Loop: ${NOLOOP}"

            # Send the message
            :0
            | ( echo "$REPLHEADER"; \
                cat ${SBDIR}/msworddoc; \
                sed 's/^/> /g'; \
              ) | ${SENDMAIL} -oi -t
        }


The final thing you should note is that the 'h' flags does nothing on a
nested block recipe.  Future versions of procmail will log an "Extraneous
deliver-header flag ignored" message when they see one.


Philip Guenther

<Prev in Thread] Current Thread [Next in Thread>
  • Re: Rescue of unfiltered data, Philip Guenther <=