# pm-jaube-prg-spamprobe.rc -- Interface to Annoyance Filter program
#
#   File id
#
#       Copyright (C) 1997-2010 Jari Aalto
#
#       This program is free software; you can redistribute it and/or
#       modify it under the terms of the GNU General Public License as
#       published by the Free Software Foundation; either version 2 of the
#       License, or (at your option) any later version
#
#       This program is distributed in the hope that it will be useful, but
#       WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#       General Public License for more details at
#       <http://www.gnu.org/copyleft/gpl.html>.
#
#   Warning
#
#       Put all your Unsolicited Bulk Emacil (aka spam) filters towards the
#       end of your `~/.procmailrc'. The idea is that valid messages are filed
#       first (mailing lists, your work and private mail, bounces) and only
#       the uncategorized messages are checked last.
#
#       YOU CANNOT USE THIS PROCMAIL SUBROUTINE UNLESS YOU HAVE TRAINED THE
#       BAYESIAN PROGRAM FIRST!
#
#       To train:
#
#           $ mkdir $HOME/.annoyance
#           $ DB=$HOME/.annoyance/dict.bin; DB2=$HOME/.annoyance/fdict.bin
#           $ annoyance-filter --mail single.msg --prune --write $DB
#           $ annoyance-filter --phrasemax 2 \
#             --read  $DB \
              --mail dir/to/good/messages \
#             --junk dir/to/bad/messages \
#             --prune --write $DB
#           $ annoyance-filter -v --read $DB --prune --fwrite $DB2
#
#       To check message:
#
#           $ annoyance-filter --read $DB --test mail.msg
#           $ annoyance-filter --fread $DB2 -v --class mail.msg
#
#   Overview of features
#
#       o   Implements interface to
#           http://sourceforge.net/projects/annoyancefilter/ project.
#           See article "Training Annoyance Filter to combat spam" by
#           Corrado Cau at
#           http://www.newsforge.com/software/03/10/24/2046238.shtml?tid=74
#       o   variable `ERROR' is set if the message was UBE.
#       o   Results are available by default in header
#           `X-Spam-Annoyance-Status'.
#
#   Description
#
#       There are several Bayesian based statistical analysis programs that
#       study the message's tokens and then classify it into two categories:
#       good or bad, or if you like, ham and spam. All the Bayesian programs
#       are not the same, so if you want to achive magic 99.99% probability
#       the only methodology to do that is to chain several programs in
#       serially. There is no single program that can solve the UBE detection.
#       This procmail subroutine implements call interface to program
#       `annoyance-filter', which must already have been installed.
#
#   About bouncing message back
#
#       The general consensus is, that you should not send bounces. The UBE
#       sender is not there, because the address forged. Do not increase
#       the network traffic; you will not do any good to anybody by
#       bouncing messgas -- you just increase mail traffic even more.
#       Instead save the messages to folders and periodically periodically
#       check their contents.
#
#   Required settings
#
#       If `annoyance-filter' program is available, define this variable in your
#       `~/.procmailrc'. Use absolute path to make the external shell
#       quick; it'll save server load considerably.
#
#           JA_UBE_ANNOYANCE_PRG = /usr/bin/spamprobe
#
#       If you _do_ _not_ have program installed, do not leave the
#       variable lying aroung, because it will keep this subroutine active.
#       Calling a non existing program is not a good idea, so it better to
#       empty the variable if the program is not available.
#
#   Required settings
#
#       None. No dependencies to other procmail modules.
#
#   Call arguments (variables to set before calling)
#
#       o   `JA_UBE_ANNOYANCE_PRG', path to the program [required].
#       o   `JA_UBE_ANNOYANCE_SPAM_DB', path to the dictionary database
#           [required]. E.g. `$HOME/.annoyance/dict.db'.
#       o   `JA_UBE_ANNOYANCE_SPAM_DB_OPT', type of dictionary to read.
#           Default is "--read", but this could be fast dictionary option
#           "--fread".
#       o   `JA_UBE_ANNOYANCE_HEADER', the header name where the results
#           are put. If not defined, no header is added.
#           Defaults to *X-Spam-Annoyance-Status*
#       o   `JA_UBE_ANNOYANCE_FORCE', if set to _yes_ then call program no matter
#           what. Normally if there already is *X-Spam-Annoyance-Status* header,
#           it is assumed that the message has already been checked
#           and no new checking is needed.
#
#   Return values
#
#       o   ERROR, is set to the return value of the program.
#
#   Usage example
#
#           PMSRC                = $HOME/procmail # procmail recipe dir
#
#           <other checks, mailing lists, work mail etc.>
#
#           JA_UBE_ANNOYANCE_PRG     = "/usr/bin/nice -n 5 /usr/bin/annoyance-filter"
#           JA_UBE_ANNOYANCE_SPAM_DB = $HOME/.annoyance/dict.db
#           INCLUDERC                = $PMSRC/pm-jaube-prg-spamprobe.rc
#
#           #   The ERROR will contains word "yes" if message was spam
#
#           :0 :
#           * ERROR ?? yes
#           junk.mbox
#
#   File layout
#
#       The layout of this file is managed by Emacs packages tinyprocmail.el
#       and tinytab.el for the 4 tab text placement.
#       See http://freshmeat.net/projects/emacs-tiny-tools/
#
#   Change Log
#
#       None

dummy = "
========================================================================
pm-jaube-prg-annoyance-filter.rc: init:"

# ................................................... User variables ...

#   You must define program path, because we don't know
#   if it has been installed in this system or not

JA_UBE_ANNOYANCE_PRG     = ${JA_UBE_ANNOYANCE_PRG:-""}

#   There is no default location for the dictionaly file, so these
#   variables *must* be set in order to activate this module.

JA_UBE_ANNOYANCE_SPAM_DB_OPT = ${JA_UBE_ANNOYANCE_SPAM_DB_OPT:-"--read"}
JA_UBE_ANNOYANCE_SPAM_DB     = ${JA_UBE_ANNOYANCE_SPAM_DB:-""}

#   The header name with no colon at the end. If this variable
#   is empty, then external formail call is saved - results can still
#   be checked from variable ERROR. There is no trailing colon in this
#   variable.

JA_UBE_ANNOYANCE_HEADER = ${JA_UBE_ANNOYANCE_HEADER:-"\
X-Spam-Annoyance-"}

# Should we check even if there already was header
# JA_UBE_ANNOYANCE_HEADER

JA_UBE_ANNOYANCE_FORCE = ${JA_UBE_ANNOYANCE_FORCE:-"no"}

# ............................................................ do it ...

ERROR   # Kill variable

:0
*                   JA_UBE_ANNOYANCE_PRG     ??  [a-z]
*                   JA_UBE_ANNOYANCE_SPAM_DB ??  [a-z]
*$ ! 9876543210^0  ^$JA_UBE_ANNOYANCE_HEADER
*    9876543210^0   JA_UBE_ANNOYANCE_FORCE   ?? yes
{
    #   Will return headers like:
    #
    #   X-Annoyance-Filter-Junk-Probability: 0
    #   X-Annoyance-Filter-Classification: Mail

    :0 w : spamannoyance$LOCKEXT
    jaubeAnnoyanceData=|$JA_UBE_ANNOYANCE_PRG \
                        $JA_UBE_ANNOYANCE_SPAM_DB_OPT \
                        $JA_UBE_ANNOYANCE_SPAM_DB \
                        --transcript - --test -

    :0
    * jaubeAnnoyanceData ?? ^X-Annoyance-Filter-Junk-Probability: \/.+
    {
        jaubeAnnoyanceScore = $MATCH

        :0
        * jaubeAnnoyanceData ?? ^X-Annoyance-Filter-Classification: \/.+
        {
            jaubeAnnoyanceType = $MATCH

            :0
            * jaubeAnnoyanceType ?? Junk
            {
                ERROR = "yes"
            }
        }

        :0 fw
        * ! JA_UBE_ANNOYANCE_HEADER ?? ^^^^
        | ${FORMAIL:-formail}                                                \
          -I "${JA_UBE_ANNOYANCE_HEADER}Status: ${ERROR:-no}"                \
          -I "${JA_UBE_ANNOYANCE_HEADER}Classification: $jaubeAnnoyanceType" \
          -I "${JA_UBE_ANNOYANCE_HEADER}Probability: $jaubeAnnoyanceScore"
    }
}

dummy = "pm-jaube-prg-annoyance-filter.rc: end: $ERROR"

# End of file
