# @(#) pm-jacookie.rc -- Handle cookie (unique id) confirmations
# @(#) $Id: pm-jacookie.rc,v 2.0 2001/07/27 22:45:50 jaalto Exp $
#
#   File id
#
#       .Copyright (C)  1997-20001 Jari Aalto
#       .$Contactid:    jari.aalto@poboxes.com $
#       .$Created:	1997-12 $
#       .$Keywords:     procmail, cookie, confirm, subroutine
#
#       This code is free software in terms of GNU Gen. pub. Lic. v2 or later
#	Refer to http://www.gnu.org/copyleft/gpl.html
#
#   Overview of features
#
#       o   Each user must register himself to cookie cache before he
#           is considered "known"
#       o   Unless user return the generated cookie string; which is
#           typically a decimal or hex number, he is not considered as
#           known and should not have access to services you provide.
#       o   Can be used as a "doorbell" spam/UBE shield
#
#   Description
#
#	This recipe handles generating the cookie to new users, comparing
#       the returned cookie against the original one and passing known
#       users through if they already had returned their cookie.
#
#       When you run automatised scripts, eg. to manage mailing lists
#       where users can subscribe and unsubscribe, you have better to
#       install safety measure so that someone can not subscribe his enemy
#       to 30 mailing lists.
#
#       The *cookie* is any continuous block of random characters that is
#       sent to person who wanted to use the service. He must send back the
#       *cookie* before the service starts or action, like subscribe will
#       take in effect. If someone forges the From address to pretend to
#       be someone else and then subscribes as-beeing-someone-else to a
#       mailing list, the cookie protects this from happening.
#
#       The cookie is sent to someone-else, and he must return the cookie
#       before the "subscribe" service is activated. Obviously this
#       someone-else will not be interested in sending back the cookie and
#       thus the forgery fails. Isn't that simple, but efective protection
#       against misuse?
#
#       And nowadays when UBE (aka Spam) is crawling from every possible
#       domain thinkable, you can also start requesting that people
#       "join" to your white list, before you start discussing with them.
#       Bulk email shotguns do not reply to you, so you won't get cookies
#       back from them, but individual peoples that want to talk with
#       you, may want to return the cookie. For start you should add your
#       friends to your cookie database by hand, so that they don't get
#       upset when you activate "cookie-shield".
#
#       It's burden, I admit, to have to response to the cookies, but if
#       that is the best way so far after the spam filters, to keep out
#       uninvited people. Think this as a "doorbell", you don't usually
#       let people in to your house without invitation or your approval.
#
#       If you have strong spam shield already, then you may not want to
#       install cookie confirmation for your mates. But it's possible with
#       this module.
#
#   How it works
#
#       By default, the cookie generated uses CRC 32 `cksum', but if you have
#       md5, you should use it. The cookie is generated from the reply
#       address and immediately stored to cookie database file with entry
#
#           DATE FROM-a COOKIE-a
#           DATE FROM-b COOKIE-b
#
#       If this was a new user or an old user, who has not registered his
#       cookie yet, then original message is sent back to the sender with
#       instructions: "please place the magic string to Subject line and
#       resent the message."
#
#       When cookie is returned back, a new line to the database is added,
#       simply by adding a duplicate entry. The file now looks like this:
#
#           DATE FROM-a COOKIE-a
#           DATE FROM-b COOKIE-b
#           DATE FROM-a COOKIE-a
#
#       When there is two or more same entries, like FROM-a, the address
#       is supposed to be known and person behind it "cleared".
#
#   Required settings
#
#       PMSRC must point to source direcry of procmail code. This subroutine
#       will include
#
#       o   pm-javar.rc
#       o   pm-jadate.rc
#       o   pm-jacookie1.rc
#       o   pm-jastore.rc
#
#   Call arguments (variables to set before calling)
#
#       o   JA_COOKIE_SEND, flag. Default is "yes". Set to "no" if you want
#           to take full control of the message returned to user. You can check
#           variable `ERROR' and use `key' which holds the unique `cookie'
#       o   JA_COOKIE_CACHE, cache to determine if this is new user or not.
#       o   JA_COOKIE_AUTO_KEY, flag. If set to "yes"; the cookie is
#           initially put to the Subject when the message is bounched back.
#           Receiver only has to press "r" to reply to send the cookie
#           and message back (convenient). You set this flag to "no" when
#           you want to avoid accidnebts eg. when receiver is about to
#           subscribe to a mailing lists: he has to manually insert the cookie
#           into subject. But keep flag to "yes" if you use this module to
#           get your friends registered easily.
#       o   JA_COOKIE_KEYS, the cookie database. Email address and person's
#           access cookie.
#       o   JA_COOKIE_MBOX, if contains [a-z] characters, then the message
#           that got bounched due to unknown user, is stored into this
#           mailbox. Only used if JA_COOKIE_SEND is set to "yes".
#       o   JA_COOKIE_RC, dubroutine to generate the cookie id from INPUT.
#           By default uses CRC 32.
#       o   JA_COOKIE, the string from which the cookie will be generated.
#           If you already have the return addres for the sender derived,
#           you should assing a value to this to save unnecessary formail call.
#
#   Returned values
#
#       ERROR will contain the efective action when this recipe file ends
#
#       o   "new-user", This is first message from sender.
#       o   "known-user", message has email that has been "cleared" ie.
#           cookie had been returned and user registered.
#       o   "key-mismatch", This is at least second message from sender.
#           But he dind't send the confirmation in this message.
#
#       `key' is an internal variable in this recipe file and will hold the
#       cookie id in case of "new-user" and "key-mismatch". You may want to
#       use it if you generate your own reply.
#
#   Example usage for UBE shield
#
#       This is what I use to prevent unknown people from sending me UBE.
#       It takes a bit extra, but they can easily return the message.
#       Fill in the missing variables, this won't work out of the box for you.
#
#            WORK = "(domain1|domain2|domain3)"
#           LISTS = "(procmail|list-2|list-3|list-4)"
#           VALID = "(postmaster|abuse|$LISTS|$WORK)"
#           RC_COOKIE   = $PMSRC/pm-jacookie.rc
#
#           :0
#           *$ ! From:.*$VALID
#           *$ ! $from_daemon
#           {
#               JA_COOKIE_SEND  = "no"   # Take over the resend control
#               INCLUDERC       = $RC_COOKIE
#
#               :0 wc: $RC_COOKIE.lock
#               *    mail ?? yes
#               * ! ^X-Loop: $JA_COOKIE_XLOOP
#               *   ^Subject:\/.*
#               | ( $FORMAIL -rkt -b                                        \
#                   -A "X-Authentication-key: $key [$ERROR]"                \
#                   -A "X-Loop: $JA_COOKIE_XLOOP";                          \
#                   -I "Subject: $MATCH $key"                               \
#                   echo "You Authentication key is: $key";                 \
#                   echo "$JA_COOKIE_MSG";                                  \
#                   ) | $SENDMAIL;                                          \
#                   echo "[my; cookie; $ERROR; $JA_COOKIE]" >> $HIN
#
#               :0
#               * mail ?? yes
#               $COOKIE_SPOOL
#           }
#
#   Example usage for subscriptions
#
#           :0 c:        # keep backups, expired by cron
#           $BACKUP
#
#           $RC_COOKIE   = $PMSRC/pm-jacookie.rc
#
#           ...Mailing lists handled here...
#           ...Your work messages filed here..
#
#           TO            = `formail -rt -zxTo:`  # We need this elswhere
#           JA_COOKIE_TO  = $TO                   # Update this, it is known
#
#           #   I run private List-X, and all subscribe requests must
#           #   be confirmaed
#
#           * ^TO_()list-x
#           * ^Subject: +subscribe\>
#           {
#               JA_COOKIE_SEND  = "no"
#               INCLUDERC = $RC_COOKIE
#
#               :0
#               * ERROR ?? known-user
#               {
#                   #   User sent the subsribe request again, allow joining
#                   #   immediately.
#               }
#               :0 E
#               {
#                   # Because the Send was set to "no"; we're in charge
#                   # to send a reply to the user.
#                   # ...generate suitable message with formail -rt
#               }
#
#           }
#
#           #   Any other messages may be UBE
#           JA_COOKIE_SEND  = "yes"
#           INCLUDERC       = $RC_COOKIE
#
#   Change Log: (none)

# ............................................................ &init ...

id    = "pm-jacookie.rc"
dummy = "
======================================================================
$id: init:
"


:0
* !  WSPC ?? [ ]
{
    INCLUDERC = $PMSRC/pm-javar.rc
}

:0
* ! YYYY ?? ^^$d$d$d$d^^
{
    INCLUDERC = $PMSRC/pm-jadate.rc
}

# ........................................................... &input ...

JA_COOKIE_SEND       = ${JA_COOKIE_SEND:-"yes"}
JA_COOKIE_AUTO_KEY   = ${JA_COOKIE_AUTO_KEY:-"yes"}
JA_COOKIE_CACHE      = ${JA_COOKIE_CACHE:-$HOME/.cookie-cache-in}
JA_COOKIE_CACHE_SIZE = ${JA_COOKIE_CACHE_SIZE:-8192}
JA_COOKIE_KEYS       = ${JA_COOKIE_KEYS:-$HOME/.cookie-cache-keys}
JA_COOKIE_MBOX       = ${JA_COOKIE_MBOX:-""}
JA_COOKIE_RC         = ${JA_COOKIE_RC:-$PMSRC/pm-jacookie1.rc}

JA_COOKIE_XLOOP      = ${JA_COOKIE_XLOOP:-"\
Procmail Authentication service (PAS); $EMAIL"}

JA_COOKIE_MSG        = ${JA_COOKIE_MSG:-"\
Include authentication key anywhere in Subject and resend your email.
This will open a gateway to your messages now and in the future.
If you do not send the key, your mesage will be deleted from queue.
Sorry about the inconvenience, but only legimite email is allowed to pass.
"}



# - If not set, set the variable. We use `r' instead of `rt'
#   because of `-rD' later.
# - User can preset the JA_COOKIE to his liking if the "to" field's
#   string is not enough to generate a unique cookie from it.

JA_COOKIE = ${JA_COOKIE:-`$FORMAIL -r -zxTo:`}

# .......................................................... &output ...

ERROR       = ""

# ........................................................... &do-it ...

newUser     = "yes"             # Private variables in lowercase
date        = $YYYY-$MM-$DD

#   - Mail arrived, check cache. Use regional lock because formail
#     is in the condition line.
#   - If user was there already, formail returns success
#   - Options -rD cause adding the email to the cache.

LOCKFILE    = ${JA_COOKIE_CACHE}.lock
dummy       = "$id: Testing new user"

:0
* ? $FORMAIL -rD $JA_COOKIE_CACHE_SIZE $JA_COOKIE_CACHE
{
    newUser = "no"
}

LOCKFILE

:0
* newUser ?? yes
{
    ERROR   = "new-user"
    mail    = "yes"

    INPUT=$JA_COOKIE   INCLUDERC = $JA_COOKIE_RC   key=$OUTPUT

    #   Save "ADDR KEY value for this user". Global lockfile still in effect

    :0 hwic:
    | echo "$date $JA_COOKIE $key" >> $JA_COOKIE_KEYS

}

:0 E
{
    dummy = "$id: Old user"

    #   Old user, do we know him? That is, has he returned the
    #   cookie?

    :0 hwi
    count|= `$GREP "$date $JA_COOKIE" $JA_COOKIE_KEYS | wc -l`

    :0
    *$ count ?? [2-9]|$d$d+
    {
        #   Yes; there was 2 or more user entries in the key file. User had
        #   sent the confirmation to us previously.

        ERROR = "known-user"
    }
    :0 E
    {
        INPUT=$JA_COOKIE   INCLUDERC = $JA_COOKIE_RC   key=$OUTPUT

        #   Unknown user:
        #   - User must return the cookie in the subject line
        #   - The key can be anywhere in the subject line

	:0
	* ^Subject:\/.*
	{
	    returnedKey  = $MATCH
	}

        dummy = "$id: Unknown user"

        #   Is the sent cookie identical to generated one?
        #   I don't think we need ^^ anchors here

        :0
        *$ returnedKey ?? $key
        {
            #   Clear the user by appending new entry to KEYS file

            :0 hwic:
            | echo "$date $JA_COOKIE $key" >> $JA_COOKIE_KEYS
        }
        :0 E                            # Else
        {
            ERROR   = "key-mismatch"
            mail    = "yes"
        }
    }
}

dummy = "$id: If mismatched key, maybe send message"

:0
*     mail              ?? yes
*     JA_COOKIE_SEND    ?? yes
*$ ! ^X-Loop: $JA_COOKIE_XLOOP
*$ ! ^from_daemon
{
    #   - If auto-mode is "on"; then put the key in the subject
    #   - User only has to hit "r" reply button.

    :0 fhw
    *  JA_COOKIE_AUTO_KEY ?? yes
    * ^Subject:\/.*
    | $FORMAIL -I "Subject: $MATCH $key"

    #   We add this header here, so that the key gets filed too.

    :0 fhw
    | $FORMAIL -A "X-Authentication-key: $key [$ERROR]"

    #   We store (maybe) and (c)ontinue with sending the message

    :0 c
    * JA_COOKIE_MBOX ?? [a-z]
    {
        MBOX        = $JA_COOKIE_MBOX
        INCLUDERC   = $PMSRC/pm-jastore.rc
    }

    :0 w
    | ( $FORMAIL -rkt -b                                                    \
        -A "X-Loop: $JA_COOKIE_XLOOP";                                      \
        echo "You Authentication key is: $key";                             \
        echo "$JA_COOKIE_MSG";                                              \
        ) | $SENDMAIL                                                       \
}

dummy = "$id: end:"

# pm-store.rc ends here
