package Parp::Config;

=head1 NAME

Parp::Config - base class for Parp config

=head1 SYNOPSIS

Do not use directly!  Subclass as follows:

  package Parp::Config::User;

  use strict;
  use warnings;

  use base 'Parp::Config';

  # Override methods with your own settings, e.g.
  sub mail_dir { "/nfs/mail/my_folders" }

  ...

  1;

Your configuration settings can be accessed as follows:

  package Parp::Filter::User;

  use strict;
  use warnings;

  use Parp::Config qw(config);
  
  ...

  my $mail_dir = config->mail_dir;

etc.

=head1 DESCRIPTION

This class provides defaults for various configuration settings, and
the interface for the code to access them.

parp tries to load user-specific settings from a file called
F<Config.pm> in the F<.parp> subdirectory of your home directory,
which typically inherits from the C<Parp::Config> module, the latter
providing sensible default settings which can be overridden.  It must
provide the methods within the C<Parp::Filter::User> package.

If you wish to place F<Config.pm> in a different directory, set the
environment variable C<PARP_USERDIR> to that directory before you
invoke parp.

Note that it is not necessary for a user-specific F<Config.pm> to
exist anywhere; in that case, the settings are taken entirely from
C<Parp::Config>.  However, it is strongly recommended that you do
provide a C<Parp::Config::User> with your own settings for the C<me>
regexp at very least.

The methods which return the various configuration settings are now
explained.  To change any setting from its default, override the
method to return your customised setting in your user-specific
settings class.

=cut

use strict;
use warnings;

use base 'Exporter';
our @EXPORT_OK = qw(config);

=head1 FILE AND DIRECTORY SETTINGS

=cut

=head2 mail_dir

Directory where your mail is kept.  See
L<Parp::Mail::Deliverable/deliver_to>.

=cut

sub mail_dir { "$ENV{HOME}/mail" }

=head2 inbox_dir

Directory where your inboxes are kept (see
L<Parp::Mail::Deliverable/deliver_to_inbox>), and also where parp
stores various files required for its operation.

=cut

sub inbox_dir { shift->mail_dir . '/inboxes' }

=head2 inbox($inbox)

Helper method which returns the full path to C<$inbox>.  You should
probably not override this.

=cut

sub inbox { shift->inbox_dir . '/' . shift }

=head2 main_folder

The full path to your main inbox.  Defaults to the value of the
C<$MAIL> environment variable, or if that's not defined, F<MAIN> in
your inbox directory.

=cut

sub main_folder { $ENV{MAIL} || shift->inbox('MAIN') }

=head2 backup_folder

The full path to your backup folder.  Every mail gets delivered here
during a delivery, no matter what else happens to it, unless backing
up has been disabled explicitly for the mail.  See L<FIXME>.

=cut

sub backup_folder { shift->mail_dir . "/backup" }

=head2 errors_folder

The full path to a folder where reports of parp errors are delivered.
Defaults to F<parp-errors> in your inbox directory.

=cut

sub errors_folder { shift->inbox("parp-errors") }

=pod 

By default the next 5 files all go in the directory specified by
C<mail_dir>.

=head2 shutdown_file

If this file exists, parp will remove it and shutdown if in daemon
mode.  Defaults to F<.parp.shutdown>.

=cut

sub shutdown_file { shift->mail_dir . "/.parp.shutdown" }

=head2 log_file

Where parp logs to when appropriate.  Defaults to F<.parp.log>.

=cut

sub log_file { shift->mail_dir . "/.parp.log"  }

=head2 lock_dir

A directory which parp stores lock files in.  Defaults to
F<.parp.lock>.

=cut

sub lock_dir { shift->mail_dir . "/.parp.lock" }

=head2 friends_db

Filename for Berkeley DB database of known good addresses
("friends").  Defaults to F<.parp.friends.db>.

=cut

sub friends_db { shift->mail_dir . "/.parp.friends.db" }

=head2 id_cache

Cache containing Message-IDs of recent messages, used to eliminate
duplicates.  Defaults to F<.parp.msgid.cache>.

=cut

sub id_cache { shift->mail_dir . "/.parp.msgid.cache" }

=head1 SPAM DETECTION SETTINGS

=head2 me

A compiled regexp which should match any of the addresses you want
to receive mail at.  Always matches by default.

=head2 old_me

A compiled regexp which matches old addresses you still receive mail
at, but want to deprecate.  Never matches by default.

=head2 decoys

A compiled regexp which matches addresses which you receive mail at,
but have deliberately submitted to untrustworthy places e.g. when
buying online from a company you're not sure you can trust not to spam
the e-mail address you gave them.

=head2 good_domains

A compiled regexp which matches domains you trust.

=head2 subject_buzzwords

If a mail's subject matches this compiled regexp, it will
automatically be let through.

=cut

sub me                { qr// } # nasty hack - this should always match
sub old_me            { qr/nasty hack - this should never match/  }
sub decoys            { qr/nasty hack - this should never match/  }
sub good_domains      { qr/nasty hack - this should never match/  }
sub subject_buzzwords { qr/nasty hack - this should never match/  }

=head2 password

=head2 password_header

If the subject line or header corresponding to the setting of
C<password_header> match the password given by the C<password>
setting, the mail is given special consideration.  C<password_header>
defaults to C<X-Password>.  C<password> does not have a default, 
and must be overridden.

=cut

sub password_header { 'X-Password'                   }
sub password        { "this isn't really a password" }

=head2 max_recipients

Threshold number of recipients before an e-mail is considered as spam.

=cut

sub max_recipients { 100 }

=head2 max_forwards_lines

=head2 max_forwards

If the body has more than C<max_forwards_lines> lines containing more
than C<max_forwards> '>' quotation marks, the mail will be rejected.

=cut

sub max_forwards_lines { 50 } 
sub max_forwards       { 15 } 

=head2 max_quite_bad_words

=head2 max_unique_quite_bad_words

If the body matches these more than the given amount, the mail will be
rejected.  Be very careful what you put in here!

=cut

sub max_quite_bad_words        { 8 } 
sub max_unique_quite_bad_words { 5 } 

=head2 like_me

=head2 max_like_me

If the mail has been sent to more than C<max_like_me> addresses
matching the compiled regexp C<like_me>, assume it has been sent by a
spammer who has a sorted list of addresses to spam.

=cut

sub like_me     { qr/nasty hack - this should never match/ }
sub max_like_me { 5 }

=head2 bad_content_type

If the body, or any nested multiparts have a Content-Type
matching this, I don't want to see it.

=cut

sub bad_content_type { qr/charset="(ks_|.*-jp)/ } 

{
  (my $dir = $INC{'Parp/Config.pm'}) =~ s!\.pm$!!; # hope this works ...
  sub regexp_dir { $dir }
}

sub lines_from_file {
  my ($self, $file) = @_;
  open(FILE, $file) or die "Couldn't open $file: $!\n";
  my @lines = ();
  while (<FILE>) {
    next if /^\s*#/;
    s/^\s*(.*?)\s*$/$1/;
    push @lines, $_ if $_;
  }
  close(FILE);
  return @lines;
}

sub regexp_from_file {
  my ($self, $file, $options, @extra_regexps) = @_;
  my @lines = $self->lines_from_file($self->regexp_dir . '/' . $file);
  my $re = join '|', @lines, @extra_regexps;
  $re =~ s!/!\\/!g;
  my $regexp = eval "qr/($re)/$options";
  die $@ if $@;
# warn "$file is regexp m[$regexp]\n";
  return $regexp;
} 

sub bad_from        { shift->regexp_from_file('bad_from',        'i' ) }
sub bad_to          { shift->regexp_from_file('bad_to',          'i' ) }
sub bad_subjects    { shift->regexp_from_file('bad_subjects',    'ix') }
sub bad_words       { shift->regexp_from_file('bad_words',       'ix') }
sub bad_origins     { shift->regexp_from_file('bad_origins',     'ix') }
sub quite_bad_words { shift->regexp_from_file('quite_bad_words', 'ix') }
sub very_bad_words  { shift->regexp_from_file('very_bad_words',  'ix') }

=head1 MISCELLANEOUS SETTINGS

=head2 daemon_poll_interval

How often in seconds the daemon should poll the spool directory for
new mail.  Defaults to 10.

=cut

sub daemon_poll_interval { 10 }

=head2 max_cache_ids

Maximum number of Message-IDs in cache.  Defaults to 500.

=cut

sub max_cache_ids { 500 }

=head2 loop_value

Value of X-Loop header set to avoid mail loops.  Defaults to 
your user name or login name, as returned by getpwuid(3).

=cut

sub loop_value {
  my ($login, $username) = (getpwuid($<))[0, 6];
  return $username || $login;
}

=head2 debug_parp_id_calcs

Debug calculation of the unique Parp-Id header.  See the source
for more information.

=cut

sub debug_parp_id_calcs { 0 }

sub new { shift } # monadic class, no state needed

# This is naughty but convenient.
my $user_dir    = $ENV{PARP_USERDIR} || $ENV{HOME};
my $user_config = $user_dir . '/.parp/Config.pm';
if (-e $user_config) {
  eval { require $user_config };
  die $@ if $@;
}
my $config = Parp::Config::User->can('new') ?
               Parp::Config::User->new : Parp::Config->new;
sub config { $config }

1;
