procmail
[Top] [All Lists]

Re: Question of a procmail newbie

1999-11-28 18:42:18
Matt Dunford <matt(_at_)stary(_dot_)zoomedia(_dot_)com> writes:
# legend
# B - search the body of the message
# b - feed body to the pipe
# c - continue with promail script
# \/ - put whatever is behind this sign into the MATCH variable
# | - '|' pipe, output message to unix shell
:0Bc
* ^Category: \/.+
CATEGORY = $MATCH

:0Bc
* ^Subcategory: \/.+
SUBCAT = $MATCH

:0Bc
* ^File: \/.+
FILE = $MATCH

:0b:
| cat >$CATEGORY/$SUBCAT/$FILE

Danger, danger!  The above recipes don't include any checking to make
sure the specified filename isn't something like "../../.cshrc" or
"../../.rhosts".  On the otherhand, they're broken, so I guess they're
not that dangerous.

Okay, how can you limit the filenames to something safe?  Just limit
the text being matched.  Instead of using a condition of

        * ^Category: \/.+
use
        * ^Category: *\/[-a-z0-9_]+

That would limit the category to just letters, numbers, underbar, and
minus sign.  In particular you should probably not include forward slash,
and you should *definitely* not allow period as the first character
of a match or after a slash.  To let period be used as the second or
succeeding character you would do something like:

        * ^Category: *\/[-a-z0-9_][-a-z0-9_.]*


Okay, now for the bugs: everyone repeat after me
        VARIABLE ASSIGNMENTS ARE NOT ACTIONS

The recipe:
        :0Bc
        * ^Category: \/.+
        CATEGORY = $MATCH

Saves a copy of the message to the mailbox CATEGORY, and then either
complains about skipping "= $MATCH" if CATEGORY is a file or tries to
treat '=' and $MATCH as the names of directories mailboxes in which to
make links to the message saved in the CATEGORY directory.

The correct way to perform a variable assignment open the successful
matching of a condition is by opening a nested block, performing the
assignment, and then closing the block.

        :0 B
        * ^Category: *\/[-a-z0-9_]+
        {
            CATEGORY = $MATCH
        }

Note that the above recipe doesn't need the 'c' flag as nested blocks
are not themselves delivering actions and we don't want procmail to
clone itself when processing this.

Finally, to prevent problems when one or more of regexps fails to match,
they should be chained so that nothing will be done if any of the regexps
fails to match.  That can be done either by sticking each succeeding
recipe inside the previous recipes nested block, or by using the 'A'
flag to chain conditions:

        :0 B
        * ^Category: *\/[-a-z0-9_]+
        {
            CATEGORY = $MATCH

            :0 B
            * ^Subcategory: *\/[-a-z0-9_]+
            {
                SUBCAT = $MATCH

                :0 B
                * ^File: *\/[-a-z0-9_]+
                {
                    FILE = $MATCH

                    # create the directory if it doesn't exist yet
                    :0 ir
                    |mkdir -p $CATEGORY/$SUBCAT

                    # Pick one of the following recipes:

                    # Store the body into the file.  This first recipe
                    # overwrites the file in case it already exists:
                    :0 bw
                    | cat >$CATEGORY/$SUBCAT/$FILE

                    # Store the body into the file.  This recipe appends
                    # to the file if it already exists.  It's much more
                    # efficient than the former, so if it'll work in your
                    # situation you should choose it
                    :0 b
                    $CATEGORY/$SUBCAT/$FILE
                }
            }
        }

OR:
        :0 B
        * ^Category: *\/[-a-z0-9_]+
        {
            CATEGORY = $MATCH
        }

        :0 BA
        * ^Subcategory: *\/[-a-z0-9_]+
        {
            SUBCAT = $MATCH
        }

        :0 BA
        * ^File: *\/[-a-z0-9_]+
        {
            FILE = $MATCH

            # create the directory if it doesn't exist yet
            :0 ir
            |mkdir -p $CATEGORY/$SUBCAT

            # Pick one of the following recipes:

            # Store the body into the file.  This first recipe
            # overwrites the file in case it already exists:
            :0 bw
            | cat >$CATEGORY/$SUBCAT/$FILE

            # Store the body into the file.  This recipe appends
            # to the file if it already exists.  It's much more
            # efficient than the former, so if it'll work in your
            # situation you should choose it
            :0 b
            $CATEGORY/$SUBCAT/$FILE
        }


I find the latter easier to read, but it's your call.


Philip Guenther

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