procmail
[Top] [All Lists]

Re: variable assignment from pipe, multiple lines

2002-01-13 21:54:58
Stefan Alfredsson asked,

| Is there any way of making var=| cmd  span multiple lines?

You know, Stefan, I have a feeling that that isn't the problem.  Please
read on.  The code you posted needs a lot of little fixes; somebody has
been giving you some *really* bad advice.

| :0 c:

And we're off.  OK, either you want the parent procmail to serialize runs
of the clones or not.  If you don't (and you probably don't), get rid of
the second colon; if you do, you have to put a name for the local lockfile
to the right of the second colon, because a left brace gives the parent
procmail nothing to infer a lockfile name from.

| * !^FROM_DAEMON
| * !^X-Loop: $XLOOP

Without the "$" modifier on that condition, $XLOOP means newline plus the
five letters "xloop" (matching is case-insensitive in the absence of the `D'
flag), so we need the "$" modifier to get variable substitution.  Also,
$\XLOOP is preferable, so that all characters in the value of $XLOOP will be
taken literally.

| {
|         :0 W

I have a feeling that get_vac doesn't read in any text from the message, so
procmail will report a write error if you don't have `i' on the recipe that
ends up running get_vac.

|         * .

You want to run the action line only on messages that have at least one non-
newline character in the header?  All messages will meet that condition.
Just omit it.  An unconditional recipe needs no conditions.

|         VACMSG=| /sbin/get_vac ldapdb "ou=pop,dc=acme,dc=com" $LOGNAME

OK; you're concluding that only the first line of get_vac's output is
captured in $VACMSG.  I think it's all there and the problem is arising
somewhere else.  To see what's really happening, put

LOG="The value of the VACMSG variable is \"$VACMSG\"
" # closing quote is one line down

at that point in your rcfile and see what is logged.  You *are* setting a
logfile, aren't you?

By the way, are you sure that "dc=" should be in there twice?  I don't know
from beans about get_vac, but that looks funny to me.  If you say it's right,
it's right.

|         :0 a   # If vacation was activated, send reply.

We're not going to need the body in the action of this recipe, so add an `h'
flag there to spare procmail the effort of feeding the body.

Also, I don't like to use the word "reply" there.  Vacation messages should
IMO ignore any Reply-To: address and go to what seems to be the source ad-
dress of the message, because they are a reaction to receipt rather than a
commentary on content.  That's why you are correct here to use formail -r
rather than formail -rt; but the word "reply" is reminiscent of "Reply-To:,"
so I would rather not call a vacation message a "reply."

|       # Rewrite from->to, to->from, append vacationmessage
|         | ( formail -R To: From: -i From: | formail -ri From \
|                 -R Old-From From -U From \
|                 -I"Precedence: bulk" \
|                 -A"X-Loop: $XLOOP" \

Oh my stars.  You just want to interchange To: and From: (a bad idea anyway,
since To: can have multiple entries, and you wouldn't want your new From: to
look like that)?  You can do it this way (but it's still a bad idea):

 formail -R To: From: -R From: To: -I "Precedence: bulk" -A"X-Loop: $XLOOP"

However, if your goal is to prepare a return header, just use formail -r and
let formail figure out the To: and From:.  It's very good at that.

Also, you're missing the semicolon between the end of formail's options and
the echo command.  I'm surprised you're not getting a whole pile of errors.

|                 echo $VACMSG \

OK, I'll bet that that is the cause of your problem.  Are you using csh or a
derivative of csh as procxmail's $SHELL?  Don't.  Assign SHELL=/bin/sh at the
top of the rcfile.  csh and csh-based shells are notorious for being unable
to deal with embedded newlines.  Set your shell to /bin/sh and change that
line to

 echo "$VACMSG" \

to keep sh's echo from changing the embedded newlines to spaces.

|         ) | $SENDMAIL -oi -t
| }

| However, $VACMSG seems to only be assigned the first line delivered from
| my get_vac program (whose exit code reflects if the vacationmessage
| was activiated, and puts the message on stdout in that case.

I doubt that; I think that the whole text is in the variable but that 
echo is outputting only the first line.  Getting the variable logged by
procmail will tell us for sure.

So let's recap, assuming that the two dc= are correct:

 SHELL=/bin/sh
 XLOOP=${XLOOP:-$LOGNAME(_at_)$HOST} # make sure it has a non-null value

 :0c
 * ! ^FROM_DAEMON
 * $ ! ^X-Loop: $\XLOOP
 {
         :0Wi
         VACMSG=| /sbin/get_vac ldapdb "ou=pop,dc=acme,dc=com" $LOGNAME

         :0ah   # If vacation was activated, send vacation message.
         | ( formail -r -I"Precedence: bulk" -A"X-Loop: $XLOOP" ; \
                 echo "$VACMSG" \
         ) | $SENDMAIL -oi -t
 }

HOWEVER, there really is no need for forking a clone there, so let's ease the
load on the CPU and on the process table a little bit:

 SHELL=/bin/sh
 XLOOP=${XLOOP:-$LOGNAME(_at_)$HOST} # make sure it has a non-null value

 :0Wi
 * ! ^FROM_DAEMON
 * $ ! ^X-Loop: $\XLOOP
 VACMSG=| /sbin/get_vac ldapdb "ou=pop,dc=acme,dc=com" $LOGNAME

         :0ahc   # If vacation was activated, send vacation message.
         | ( formail -r -I"Precedence: bulk" -A"X-Loop: $XLOOP" ; \
                 echo "$VACMSG" \
         ) | $SENDMAIL -oi -t

Finally, unless somehow get_vac takes care of this, you really should main-
tain a cache of addresses who have already received that vacation period's
message from that user and not send tell anyone more than once.  Cache
handling requires serialization, and if we're doing it in a condition rather
than an action, we need to set up the lockfile as a regional one around the
entire recipe rather than as a local one that would be secured only after the
conditions have matched:

 SHELL=/bin/sh
 XLOOP=${XLOOP:-$LOGNAME(_at_)$HOST} # make sure it has a non-null value

 :0 # if from a daemon or $XLOOP is there, skip ahead now
 * ! ^FROM_DAEMON
 * $ ! ^X-Loop: $\XLOOP
 {
  CACHE=.vacationcache # in $MAILDIR by default, else use absolute path
  CACHESIZE=16384 # or whatever; powers of two are traditional

  LOCKFILE=$CACHE$LOCKEXT # secure regional lockfile

  # if sender is in cache, formail -rD reports success
  # otherwise, formail -rD reports failure and adds sender to cache
  :0Wi # match on report of *failure*
  * ! H ?? ? formail -rD $CACHESIZE $CACHE
  VACMSG=| /sbin/get_vac ldapdb "ou=pop,dc=acme,dc=com" $LOGNAME

  LOCKFILE # release regional lockfile

          :0ahc   # If vacation was activated, send vacation message.
          | ( formail -r -I"Precedence: bulk" -A"X-Loop: $XLOOP" ; \
                  echo "$VACMSG" \
          ) | $SENDMAIL -oi -t
 }

_______________________________________________
procmail mailing list
procmail(_at_)lists(_dot_)RWTH-Aachen(_dot_)DE
http://MailMan.RWTH-Aachen.DE/mailman/listinfo/procmail

<Prev in Thread] Current Thread [Next in Thread>