use strict;

#
# Section V - Per-recipient and per-sender handling, whitelisting, etc.
#

# @virus_lovers_maps list of lookup tables:
#   (this should be considered a policy option, is does not disable checks,
#   see bypass*checks for that!)
#
# Exclude certain RECIPIENTS from virus filtering by adding their (lower-cased)
# envelope e-mail address (or domain only) to one of the lookup tables in
# the @virus_lovers_maps list - see README.lookups and examples.
# Make sure the appropriate form (e.g. external/internal) of address
# is used in case of virtual domains, or when mapping external to internal
# addresses, etc. - this is MTA-specific.
#
# Notifications would still be generated however (see the overall
# picture above), and infected mail (if passed) gets additional header:
#   X-AMaViS-Alert: INFECTED, message contains virus: ...
# (header not inserted with Courier or milter interface!)
#
# Setting $final_*_destiny=D_PASS is functionally equivalent to having
# all recipients match the @*_lovers_maps.
#
# NOTE (milter interface only): in case of multiple recipients,
# it is only possible to drop or accept the message in its entirety - for all
# recipients. If all of them are virus lovers, we'll accept mail, but if
# at least one recipient is not a virus lover, we'll discard the message.


# @bypass_virus_checks_maps list of lookup tables:
#   (this is mainly a time-saving option, unlike virus_lovers* !)
#
# Similar in concept to @virus_lovers_maps, a @bypass_virus_checks_maps
# is used to skip entirely the decoding, unpacking and virus checking,
# but only if ALL recipients match the lookup.
#
# @bypass_virus_checks_maps does NOT GUARANTEE the message will NOT be checked
# for viruses - this may still happen when there is more than one recipient
# for a message and not all of them match these lookup tables, or when
# check result was cached (i.e. the same contents was recently sent to other
# recipients). To guarantee virus delivery, a recipient must also match
# @virus_lovers_maps lookups (but see milter limitations above),
#
# The following table summarizes the possible combinations:
# bypass lover
#   0      0    useful, check for malware and block it
#   0      1    useful, check but deliver nevertheless, possibly tagged
#   1      0    not too useful, free riding on cached or other-people's checks
#   1      1    useful, no checks if possible, and no effects

# NOTE: it would not be clever to base enabling of virus checks on SENDER
# address, since there are no guarantees that it is genuine. Many viruses
# and spam messages fake sender address. To achieve selective filtering
# based on the source of the mail (e.g. IP address, MTA port number, ...),
# use mechanisms provided by MTA if available, possibly combined with policy
# banks feature.

# Similar to lists of lookup tables controlling virus checking, there are
# counterparts for spam scanning, banned names/types, and headers_checks
# control:
#   @spam_lovers_maps,
#   @banned_files_lovers_maps,
#   @bad_header_lovers_maps
# and:
#   @bypass_spam_checks_maps,
#   @bypass_banned_checks_maps,
#   @bypass_header_checks_maps

# Example:
#   @bypass_header_checks_maps = ( [qw( user@example.com )] );
#   @bad_header_lovers_maps    = ( [qw( user@example.com )] );

# The following example disables spam checking altogether,
# since it matches any recipient e-mail address.
#   @bypass_spam_checks_maps = (1);


# See README.lookups for further detail, and examples below.

# In the following example a list of lookup tables @virus_lovers_maps
# contains three elements, the first is a reference to an ACL lookup table
# (brackets in Perl indicate a ref to a list), the second is a reference
# to a hash lookup table (curly braces in Perl indicate a ref to a hash),
# the third is a regexp lookup table, indicated by the type of object
# created by new_RE() :
#
#@virus_lovers_maps = (
# [ qw( me@lab.xxx.com !lab.xxx.com .xxx.com yyy.org ) ],
# { "postmaster\@$mydomain" => 1, # double quotes permit variable evaluation
#   'postmaster@example.com'=> 1, # in single quotes the '@' need not be quoted
#   'abuse@example.com'=> 1,
#   'some.user@'       => 1,  # this recipient, regardless of domain
#   'boss@example.com' => 0,  # never, even if domain matches
#   'example.com'      => 1,  # this domain, but not its subdomains
#   '.example.com'     => 1,  # this domain, including its subdomains
# },
# new_RE( qr'^(helpdesk|postmaster)@example\.com$'i ),
#);

#@spam_lovers_maps = (
# ["postmaster\@$mydomain", 'postmaster@example.com', 'abuse@example.com'],
#);

#@bad_header_lovers_maps = (
# ["postmaster\@", "abuse\@$mydomain"],
#);


# as an alternative to fiddling with @_lovers_maps and similar _maps, here
# is an illustration of using a more general *_by_ccat associative array,
# introduced with 2.4.0, like %lovers_maps_by_ccat in this example:
#
#$lovers_maps_by_ccat{+CC_SPAM} = [
#  read_hash("$MYHOME/etc/spam_lovers.txt"),
#  [qw(postmaster@example.com abuse@example.com)],
#];
#
#$lovers_maps_by_ccat{+CC_BANNED} = [
#  { map {lc $_ => 1}    # construct a hash lookup table from a list
#        qw(user1@example.com user2.example.com)
#  },
#];


# to save some typing of quotes and commas, a Perl operator qw can be used
# to split its argument on whitespace and to quote resulting elements:
#@bypass_spam_checks_maps = (
#  [ qw( some.ddd !butnot.example.com .example.com ) ],
#);


# don't run spam check for these RECIPIENT domains:
#   @bypass_spam_checks_maps = ( [qw( d1.com .d2.com a.d3.com )] );
# or the other way around (bypass check for all BUT these):
#   @bypass_spam_checks_maps = ( [qw( !d1.com !.d2.com !a.d3.com . )] );
# a practical application: don't check outgoing mail for spam:
#   @bypass_spam_checks_maps = ( [ "!.$mydomain", "." ] );
# or calculated (negated) from the %local_domains:
#   @bypass_spam_checks_maps =
#     ( {map {$_ => !$local_domains{$_}} keys %local_domains}, 1);
# (a downside of which is that such mail will not count as ham in SA bayes db)
#
# Note that 'outgoing' is not the same as 'originating from inside'.
# The internal-to-internal mail is not outgoing, but is originating from
# inside. To base rules on 'originating from inside', the use of policy bank
# MYNETS is needed, in conjunction with XFORWARD Postfix extension to SMTP.

# Where to find SQL server(s) and database to support SQL lookups?
# A list of triples: (dsn,user,passw).   (dsn = data source name)
# More than one entry may be specified for multiple (backup) SQL servers.
# See 'man DBI', 'man DBD::mysql', 'man DBD::Pg', ... for details.
# When chroot-ed, accessing SQL server over inet socket may be more convenient.
#
# @lookup_sql_dsn =
#   ( ['DBI:mysql:database=mail;host=127.0.0.1;port=3306', 'user1', 'passwd1'],
#     ['DBI:mysql:database=mail;host=host2', 'username2', 'password2'],
#     ["DBI:SQLite:dbname=$MYHOME/sql/mail_prefs.sqlite", '', ''] );
# @storage_sql_dsn = @lookup_sql_dsn;  # none, same, or separate database
#
# ('mail' in the example is the database name, choose what you like)
# With PostgreSQL the dsn (first element of the triple) may look like:
#      'DBI:Pg:dbname=mail;host=host1'

# The SQL select clause to fetch per-recipient policy settings.
# The %k will be replaced by a comma-separated list of query addresses
# (e.g. full address, domain only (stripped level by level), and a catchall).
# Use ORDER if there is a chance that multiple records will match - the first
# match wins. If field names are not unique (e.g. 'id'), the later field
# overwrites the earlier in a hash returned by lookup, which is why we use
# '*,users.id' instead of just '*'. No need to uncomment the following
# assignment if the default is ok.
#   $sql_select_policy = 'SELECT *,users.id FROM users,policy'.
#     ' WHERE (users.policy_id=policy.id) AND (users.email IN (%k))'.
#     ' ORDER BY users.priority DESC';
#
# The SQL select clause to check sender in per-recipient whitelist/blacklist
# The first SELECT argument '?' will be users.id from recipient SQL lookup,
# the %k will be sender addresses (e.g. full address, domain only, catchall).
# The default value is:
#   $sql_select_white_black_list = 'SELECT wb FROM wblist,mailaddr'.
#     ' WHERE (wblist.rid=?) AND (wblist.sid=mailaddr.id)'.
#     '   AND (mailaddr.email IN (%k))'.
#     ' ORDER BY mailaddr.priority DESC';
#
# To disable SQL white/black list, set to undef (otherwise comment-out
# the following statement, leaving it at the default value):
$sql_select_white_black_list = undef;  # undef disables SQL white/blacklisting

# Controls the format of timestamps in the field msgs.time_iso:
# $timestamp_fmt_mysql = 1; # if using MySQL *and* msgs.time_iso is TIMESTAMP;
#   defaults to 0, which is good for non-MySQL or if msgs.time_iso is CHAR(16)

# Does a database mail address field with no '@' character represent a
# local username or a domain name?  By default it implies a username in
# SQL and LDAP lookups (but represents a domain in hash and acl lookups),
# so domain names in SQL and LDAP should be specified as '@domain'.
# Setting these to true will cause 'xxx' to be interpreted as a domain
# name, just like in hash or acl lookups.
#
# $sql_lookups_no_at_means_domain  = 0;  # default is 0
# $ldap_lookups_no_at_means_domain = 0;  # default is 0

# Here is an example of a SELECT clause that fabricates an artificial 'users'
# table from actual table 'postfix_domains' containing a field 'domain_name'.
# The effect is that domains listed in the 'postfix_domains' table will be
# treated as local by amavisd, and be given settings from a policy id 99
# if such a policy id exists, or just fall back to static lookups.
# The user.id (with a value 1) is there only to provide a user id (same id
# for all listed domains) when global SQL-based white/blacklisting is used.
#
# $sql_lookups_no_at_means_domain = 1;
# $sql_select_policy =
#   'SELECT *, user.id'.
#   ' FROM (SELECT 1 as id, 99 as policy_id, "Y" AS local'.
#         ' FROM postfix_domains WHERE domain_name IN (%k)) AS user'.
#   ' LEFT JOIN policy ON policy_id=policy.id';

# If passing malware to certain recipients ($final_*_destiny=D_PASS or
# *_lovers), the recipient-based lookup tables @addr_extension_*_maps may
# return a string, which (if nonempty) will be added as an address extension
# to the local-part of the recipient's address. This extension may be used
# by the final local delivery agent (LDA) to place such mail into different
# subfolders (the extension is usually interpreted as a folder name).
# This is sometimes known as the 'plus addressing'. Appending address
# extensions is prevented when:
# - recipient does not match lookup tables @local_domains_maps;
# - lookup into corresponding @addr_extension_*_maps results
#   in an empty string or undef;
# - $recipient_delimiter is empty (see below)
# LDAs usually default to stripping away address extension if no special
# handling is specified or if a named subfolder or alias does not exist,
# so adding address extensions normally does no harm.

# @addr_extension_virus_maps  = ('virus');     # defaults to empty
# @addr_extension_spam_maps   = ('spam');      # defaults to empty
# @addr_extension_banned_maps = ('banned');    # defaults to empty
# @addr_extension_bad_header_maps = ('badh');  # defaults to empty
#
# A more complex example:
# @addr_extension_virus_maps = (
#   {'sub.example.com'=>'infected', '.example.com'=>'filtered'}, 'virus' );

# Delimiter between local part of the envelope recipient address and address
# extension (which can optionally be added, see @addr_extension_*_maps. E.g.
# recipient address <user@example.com> is changed to <user+virus@example.com>.
#
# Delimiter must match the equivalent (final) MTA delimiter setting.
# (e.g. for Postfix add 'recipient_delimiter = +' to main.cf)
# Setting it to an empty string or to undef disables adding extensions
# regardless of $addr_extension_*_maps.

# $recipient_delimiter = '+';		# (default is undef, i.e. disabled)

# true: replace extension;  false: append extension
# $replace_existing_extension = 1;	# (default is true)

# Affects matching of localpart of e-mail addresses (left of '@')
# in lookups: true = case sensitive, false = case insensitive
$localpart_is_case_sensitive = 0;	# (default is false)


# ENVELOPE SENDER SOFT-WHITELISTING / SOFT-BLACKLISTING

# Instead of hard black- or whitelisting, a softer approach is to add
# score points (penalties) to the SA score for mail from certain senders.
# Positive points lean towards blacklisting, negative towards whitelisting.
# This is much like adding SA rules or using its white/blacklisting, except
# that here only envelope sender addresses are considered (not addresses
# in a mail header), and that score points can be assigned per-recipient
# (or globally), and the assigned penalties are customarily much lower
# than the default SA white/blacklisting score.
#
# The table structure is similar to $per_recip_blacklist_sender_lookup_tables
# i.e. the first level key is recipient, pointing to by-sender lookup tables.
# The essential difference is that scores from _all_ matching by-recipient
# lookups (not just the first that matches) are summed to give the final
# score boost. That means that both the site and domain administrators,
# as well as the recipient can have a say on the final score.
#
# NOTE: keep hash keys in lowercase, either manually or by using function lc

@score_sender_maps = ({  # a by-recipient hash lookup table

# # per-recipient personal tables  (NOTE: positive: black, negative: white)
# 'user1@example.com'  => [{'bla-mobile.press@example.com' => 10.0}],
# 'user3@example.com'  => [{'.ebay.com'                 => -3.0}],
# 'user4@example.com'  => [{'cleargreen@cleargreen.com' => -7.0,
#                           '.cleargreen.com'           => -5.0}],

  # site-wide opinions about senders (the '.' matches any recipient)
  '.' => [  # the _first_ matching sender determines the score boost

   new_RE(  # regexp-type lookup table, just happens to be all soft-blacklist
    [qr'^(bulkmail|offers|cheapbenefits|earnmoney|foryou)@'i         => 5.0],
    [qr'^(greatcasino|investments|lose_weight_today|market\.alert)@'i=> 5.0],
    [qr'^(money2you|MyGreenCard|new\.tld\.registry|opt-out|opt-in)@'i=> 5.0],
    [qr'^(optin|saveonlsmoking2002k|specialoffer|specialoffers)@'i   => 5.0],
    [qr'^(stockalert|stopsnoring|wantsome|workathome|yesitsfree)@'i  => 5.0],
    [qr'^(your_friend|greatoffers)@'i                                => 5.0],
    [qr'^(inkjetplanet|marketopt|MakeMoney)\d*@'i                    => 5.0],
   ),

#  read_hash("/var/amavis/sender_scores_sitewide"),

   { # a hash-type lookup table (associative array)
     'nobody@cert.org'                        => -3.0,
     'cert-advisory@us-cert.gov'              => -3.0,
     'owner-alert@iss.net'                    => -3.0,
     'slashdot@slashdot.org'                  => -3.0,
     'securityfocus.com'                      => -3.0,
     'ntbugtraq@listserv.ntbugtraq.com'       => -3.0,
     'security-alerts@linuxsecurity.com'      => -3.0,
     'mailman-announce-admin@python.org'      => -3.0,
     'amavis-user-admin@lists.sourceforge.net'=> -3.0,
     'amavis-user-bounces@lists.sourceforge.net' => -3.0,
     'spamassassin.apache.org'                => -3.0,
     'notification-return@lists.sophos.com'   => -3.0,
     'owner-postfix-users@postfix.org'        => -3.0,
     'owner-postfix-announce@postfix.org'     => -3.0,
     'owner-sendmail-announce@lists.sendmail.org'   => -3.0,
     'sendmail-announce-request@lists.sendmail.org' => -3.0,
     'donotreply@sendmail.org'                => -3.0,
     'ca+envelope@sendmail.org'               => -3.0,
     'noreply@freshmeat.net'                  => -3.0,
     'owner-technews@postel.acm.org'          => -3.0,
     'ietf-123-owner@loki.ietf.org'           => -3.0,
     'cvs-commits-list-admin@gnome.org'       => -3.0,
     'rt-users-admin@lists.fsck.com'          => -3.0,
     'clp-request@comp.nus.edu.sg'            => -3.0,
     'surveys-errors@lists.nua.ie'            => -3.0,
     'emailnews@genomeweb.com'                => -5.0,
     'yahoo-dev-null@yahoo-inc.com'           => -3.0,
     'returns.groups.yahoo.com'               => -3.0,
     'clusternews@linuxnetworx.com'           => -3.0,
     lc('lvs-users-admin@LinuxVirtualServer.org')    => -3.0,
     lc('owner-textbreakingnews@CNNIMAIL12.CNN.COM') => -5.0,
     'community@altlinux.ru'                  => -3.0,
     'community-en@altlinux.org'              => -3.0,
     'sisyphus@altlinux.ru'                   => -3.0,
     'security-announce@altlinux.ru'          => -3.0,
     'castle@altlinux.ru'                     => -3.0,
     'talk-room@altlinux.ru'                  => -3.0,
     'fonts-devel@altlinux.ru'                => -3.0,
     'freepublish@altlinux.ru'                => -3.0,
     'freeschool@linux.ru.net'                => -3.0,
     'legal@altlinux.ru'                      => -3.0,
     'music@altlinux.ru'                      => -3.0,
     'samba@altlinux.ru'                      => -3.0,
     'seminar@altlinux.ru'                    => -3.0,
     'devel@altlinux.ru'                      => -3.0,
     'hardware@altlinux.ru'                   => -3.0,

     # soft-blacklisting (positive score)
#    'sender@example.net'                     =>  3.0,
#    '.example.net'                           =>  1.0,

   },
  ],  # end of site-wide tables
});


# ENVELOPE SENDER WHITELISTING / BLACKLISTING  - GLOBAL (RECIPIENT-INDEPENDENT)
# (affects spam checking only, has no effect on virus and other checks)

# WHITELISTING: use ENVELOPE SENDER lookups to ENSURE DELIVERY from whitelisted
# senders even if the message would be recognized as spam. Effectively, for
# the specified senders, message recipients temporarily become 'spam_lovers'.
# To avoid surprises, whitelisted sender also suppresses inserting/editing
# the tag2-level header fields (X-Spam-*, Subject), appending spam address
# extension, and quarantining.
#
# BLACKLISTING: messages from specified SENDERS are DECLARED SPAM.
# Effectively, for messages from blacklisted envelope sender addresses, spam
# level is artificially pushed high, and the normal spam processing applies,
# resulting in 'X-Spam-Flag: YES', high 'X-Spam-Level' bar and other usual
# reactions to spam, including possible rejection. If the message nevertheless
# still passes (e.g. for spam loving recipients), it is tagged as BLACKLISTED
# in the 'X-Spam-Status' header field, but the reported spam value and
# set of tests in this report header field (if available from SpamAssassin,
# which may or may not have been called) is not adjusted.
#
# A sender may be both white- and blacklisted at the same time, settings
# are independent. For example, being both white- and blacklisted, message
# is delivered to recipients, but is not tagged as spam (X-Spam-Flag: No;
# X-Spam-Status: No, ...), but the reported spam level (if computed) may
# still indicate high spam score.
#
# If ALL recipients of the message either white- or blacklist the sender,
# spam scanning (calling the SpamAssassin) is bypassed, saving on time.
#
# The following variables (lists of lookup tables) are available,
# with the semantics and syntax as specified in README.lookups:
# @whitelist_sender_maps, @blacklist_sender_maps

# SOME EXAMPLES:
#
#ACL:
# @whitelist_sender_maps = ( ['.example.org', '.example.net'] );
# @whitelist_sender_maps = ( [qw(.example.org  .example.net)] );  # same thing
#
# @whitelist_sender_maps = ( [".$mydomain"] );  # $mydomain and its subdomains
# NOTE: This is not a reliable way of turning off spam checks for
#   locally-originating mail, as sender address can easily be faked.
#   To reliably avoid spam-scanning outgoing mail, use @bypass_spam_checks_maps
#   for nonlocal recipients. To reliably avoid spam scanning for locally
#   originating mail (including internal-to-internal mail), recognized by
#   the original SMTP client IP address matching @mynetworks, use policy bank
#   MYNETS, adjust @mynetworks, and turn on XFORWARD in the Postfix smtp client
#   service feeding amavisd.

#with regexps:
# @whitelist_sender_maps = ( new_RE(
#   qr'^postmaster@.*\bexample\.com$'i,
#   qr'^owner-[^@]*@'i,  qr'-request@'i,
#   qr'\.example\.com$'i
# ));


# illustrates the use of regexp lookup table:

@blacklist_sender_maps = ( new_RE(
    qr'^(bulkmail|offers|cheapbenefits|earnmoney|foryou|greatcasino)@'i,
    qr'^(investments|lose_weight_today|market\.alert|money2you|MyGreenCard)@'i,
    qr'^(new\.tld\.registry|opt-out|opt-in|optin|saveonlsmoking2002k)@'i,
    qr'^(specialoffer|specialoffers|stockalert|stopsnoring|wantsome)@'i,
    qr'^(workathome|yesitsfree|your_friend|greatoffers)@'i,
    qr'^(inkjetplanet|marketopt|MakeMoney)\d*@'i,
));


# NOTE: whitelisting is becoming deprecated because sender address is
#       all too often faked; use @score_sender_maps for soft-whitelisting!
#
# Illustrates the use of several lookup tables:
#
# @whitelist_sender_maps = (
#
# # read_hash("$MYHOME/whitelist_sender"),  # a hash table read from a file
#
#   # and another hash lookup table constructed in-line, with keys lowercased:
#   { map {lc $_ => 1} qw(
#     nobody@cert.org
#     cert-advisory@us-cert.gov
#     owner-alert@iss.net
#     slashdot@slashdot.org
#     bugtraq@securityfocus.com
#     NTBUGTRAQ@LISTSERV.NTBUGTRAQ.COM
#     security-alerts@linuxsecurity.com
#     amavis-user-admin@lists.sourceforge.net
#     amavis-user-bounces@lists.sourceforge.net
#     notification-return@lists.sophos.com
#     mailman-announce-admin@python.org
#     owner-postfix-users@postfix.org
#     owner-postfix-announce@postfix.org
#     owner-sendmail-announce@lists.sendmail.org
#     sendmail-announce-request@lists.sendmail.org
#     owner-technews@postel.ACM.ORG
#     lvs-users-admin@LinuxVirtualServer.org
#     ietf-123-owner@loki.ietf.org
#     cvs-commits-list-admin@gnome.org
#     rt-users-admin@lists.fsck.com
#     clp-request@comp.nus.edu.sg
#     surveys-errors@lists.nua.ie
#     emailNews@genomeweb.com
#     owner-textbreakingnews@CNNIMAIL12.CNN.COM
#     yahoo-dev-null@yahoo-inc.com
#     returns.groups.yahoo.com
#   )},
#
# # { '' => 1 },  # and another one, containing just an empty reverse path (DSN)
#
# );


# ENVELOPE SENDER WHITELISTING / BLACKLISTING - PER-RECIPIENT

# The same semantics as for global white/blacklisting applies, but this
# time each recipient (or its domain, or subdomain, ...) can be given
# an individual lookup table for matching senders. The per-recipient lookups
# take precedence over the global lookups, which serve as a fallback default.

# Specify a two-level lookup table: the key for the outer table is recipient,
# and the result should be an inner lookup table (hash or ACL or RE),
# where the key used will be the sender. (Note that this structure is flatter
# than @score_sender_maps, where the first level result is a ref to a _list_
# of inner lookup tables, not a ref to a single lookup table.)
#
#$per_recip_blacklist_sender_lookup_tables = {
# 'user1@my.example.com'=>new_RE(qr'^(inkjetplanet|marketopt|MakeMoney)\d*@'i),
# 'user2@my.example.com'=>[qw( spammer@d1.example,org .d2.example,org )],
#};
#$per_recip_whitelist_sender_lookup_tables = {
# 'user@my.example.com' => [qw( friend@example.org .other.example.org )],
# '.my1.example.com'    => [qw( !foe.other.example,org .other.example,org )],
# '.my2.example.com'    => read_hash("$MYHOME/my2-wl.dat"),
# 'abuse@' => { 'postmaster@'=>1,
#               'cert-advisory-owner@cert.org'=>1, 'owner-alert@iss.net'=>1 },
#};

1;  # insure a defined return
