#! /usr/bin/perl -w
#
#
#   OPEN-XCHANGE legal information
#
#   All intellectual property rights in the Software are protected by
#   international copyright laws.
#
#
#   In some countries OX, OX Open-Xchange, open xchange and OXtender
#   as well as the corresponding Logos OX Open-Xchange and OX are registered
#   trademarks of the Open-Xchange, Inc. group of companies.
#   The use of the Logos is not covered by the GNU General Public License.
#   Instead, you are allowed to use these Logos according to the terms and
#   conditions of the Creative Commons License, Version 2.5, Attribution,
#   Non-commercial, ShareAlike, and the interpretation of the term
#   Non-commercial applicable to the aforementioned license is published
#   on the web site http://www.open-xchange.com/EN/legal/index.html.
#
#   Please make sure that third-party modules and libraries are used
#   according to their respective licenses.
#
#   Any modifications to this package must retain all copyright notices
#   of the original copyright holder(s) for the original code used.
#
#   After any such modifications, the original and derivative code shall remain
#   under the copyright of the copyright holder(s) and/or original author(s)per
#   the Attribution and Assignment Agreement that can be located at
#   http://www.open-xchange.com/EN/developer/. The contributing author shall be
#   given Attribution for the derivative code and a license granting use.
#
#    Copyright (C) 2004-2006 Open-Xchange, Inc.
#    Mail: info@open-xchange.com
#
#
#    This program is free software; you can redistribute it and/or modify it
#    under the terms of the GNU General Public License, Version 2 as published
#    by the Free Software Foundation.
#
#    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.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, see <http://www.gnu.org/licenses/>.
#
#
use strict;
use DBI;
use Fcntl ':flock';

my $DATASOURCE = "dbi:mysql:";
my $DRIVER     = "mysql";
my $CFPROP     = "/opt/open-xchange/etc/configdb.properties";
my $IMAPPROP   = "/etc/imapd/imapd.conf";
my $CYRSPOOL   = "/var/spool/cyrus/mail/domain/";
my $DBPATH     = "/var/lib/amavis/.spamassassin";
my $LOCKFILE   = "/var/lib/amavis/.spamassassin/.spamrunner.lock";

# DO NOT CHANGE BELOW
# -------------------------------------------------------------------

my $SPAM_ENABLED_BIT = 4096;

my $DBUSER     = undef;
my $DBPASS     = undef;
my $HIERSEP    = undef;
my $DEBUG      = undef;

# -------------------------------------------------------------------

my $contextid = "10";
open(my $fhucr, "ucr get ox/context/id |") || die "unable to read context id from UCR: $?";
while( my $line = <$fhucr> ) {
	if ($line =~ /^([0-9]+)$/ ) {
		$contextid = $1;
		last;
	} else {
		next;
	}
}
close($fhucr);

# check amavis home
die "amavis home $DBPATH not found" if not -d $DBPATH;
foreach my $f ("bayes_journal", "bayes_seen", "bayes_toks") {
	my $i = "$DBPATH/$f";
	`touch "$i"` if not -e $i;
	`chown amavis:amavis "$i"; chmod 600 "$i"`;
}

# get lock
open(my $fh, '>>', $LOCKFILE) or die "Could not open '$LOCKFILE' - $!";
flock($fh, LOCK_EX | LOCK_NB) or die "Could not lock '$LOCKFILE' - $!";

my $mysqldriver = undef;
foreach my $driver ( DBI->available_drivers ) {
  if( $driver eq $DRIVER ) {
    $mysqldriver = $driver;
  }
}

if( ! defined $mysqldriver ) {
  die "mysqldriver not installed, exiting";
}

open(PROP,$CFPROP) || die "unable to open $CFPROP: $?";
while( <PROP> ) {
  next if $_ =~ /^#.*$/;
  if( $_ =~ /^readProperty.1=user=(.*)$/ ) {
    $DBUSER=$1;
  }
  if( $_ =~ /^readProperty.2=password=(.*)$/ ) {
    $DBPASS=$1;
  }
}
close(PROP);

open(PROP,$IMAPPROP) || die "unable to open $IMAPPROP: $?";
while( <PROP> ) {
  next if $_ =~ /^#.*$/;
  if( $_ =~ /^unixhierarchysep:\s*(\w*)\s*$/ ) {
    my $sep = uc($1);
    $HIERSEP="/";
  }
}
close(PROP);

if( ! defined($HIERSEP) || ! defined($DBUSER) || ! defined($DBPASS) ) {
  die "unable to determine required system parameters";
}

print "using \"$HIERSEP\" as IMAP separator\n";
print "using \"$DBUSER\" as db user\n";


my $dbh = DBI->connect($DATASOURCE, $DBUSER, $DBPASS,
		       { RaiseError => 1, AutoCommit => 0 }) ||
  die "unable to connect to database: $DBI::errstr";


my $stmt = $dbh->prepare("show databases");
$stmt->execute();

my $oxdb;
while ( my $row = $stmt->fetchrow_array ) {
  if( $row =~ /^localhost/ ) {
     $oxdb = $row;
     last;
  }
}

if( ! defined($oxdb) ) {
   die "unable to find an open-xchange database";
}

print "using \"$oxdb\" as ox db\n";

my $QUERY = "SELECT login2user.uid,user_setting_mail.bits,
                    user_setting_mail.confirmed_spam,
                    user_setting_mail.confirmed_ham,
		    send_addr
             FROM ${oxdb}.user_setting_mail,${oxdb}.login2user
             WHERE user_setting_mail.user=login2user.id
             AND login2user.cid=${contextid}";

$stmt = $dbh->prepare($QUERY);

$stmt->execute();

my ($uid, $bits, $cspam, $cham, $mailaddr);
$stmt->bind_columns( undef, \$uid, \$bits, \$cspam, \$cham, \$mailaddr);

while( $stmt->fetch() ) {
  print "$uid $bits $cspam $cham\n" if defined($DEBUG);
  if( ($bits & $SPAM_ENABLED_BIT) == $SPAM_ENABLED_BIT ) {
    print "checking for spam and ham for $uid\n";
    my @mailaddr = split(/@/, $mailaddr);
    my $username = $mailaddr[0];
	 my $cyrusname = $username;
	 $cyrusname =~ tr/./^/;
    my $domainname = $mailaddr[1];
    my $userdir = $CYRSPOOL.substr($domainname,0,1)."/".$domainname."/".substr($username,0,1)."/user/".$cyrusname;
    my $cspamdir = $userdir."/".$cspam;
    my $chamdir = $userdir."/".$cham;
    if( defined($DEBUG) ) {
		print "debug: $username\n";
		print "uid: $uid\n";
		print "domainname: $domainname\n";
		print "mailaddr: $mailaddr\n";
		print "userdir: $userdir\n";
		print "cspamdir: $cspamdir\n";
		print "chamdir: $chamdir\n";
    }

    # learn spam
    if( -d $cspamdir ) {

      my $foundfiles = 0;
      foreach my $file ( @{getListOfMails($cspamdir)} ) {
	$foundfiles = 1;
	$file = $cspamdir."/".$file;
	pipeSALearn("spam", $file);
	unlink($file);
      }

      cyrReconstruct($username, $domainname, $cspam) if $foundfiles;
    }

    # learn ham
    if( -d $chamdir ) {

      my $foundfiles = 0;
      foreach my $file ( @{getListOfMails($chamdir)} ) {
	$foundfiles = 1;
	$file = $chamdir."/".$file;
	pipeSALearn("ham", $file);
	unlink($file);
      }

      cyrReconstruct($username, $domainname, $cham) if $foundfiles;
    }
  }
}

$dbh->disconnect();

# release lock
close($fh) or die "Could not write '$LOCKFILE' - $!";

# ----------------- functions


# returns array ref containing all mails in folder
#
sub getListOfMails {
  my $folder = shift;

  my $mlist = [];
  opendir(DIR, $folder) || warn "unable to opendir";
  foreach my $file ( readdir(DIR) ) {
    push @$mlist, $file if $file =~ /^\d+\.$/;
  }
  closedir(DIR);

  return $mlist;
}

# pipe mail into sa-learn
# arg1 == spam or ham
# arg2 == abs path to file
#
sub pipeSALearn {
  my $type = shift;
  my $file = shift;

  my $sacmd = "| /usr/bin/sa-learn --$type --no-sync --dbpath $DBPATH 2> /dev/null";
  if( open(SAOUT, $sacmd) != 0 ) {
    open(MAIL,"$file") || warn "unable to feed $file into sa-learn";
    while( <MAIL> ) {
      print SAOUT $_;
    }
    close(MAIL);
    close(SAOUT);
  } else {
    warn "unable to start sa-learn";
  }
}

# calling cyrus reconstruct for specific folder
# arg1 == user name
# arg2 == folder name
#
sub cyrReconstruct {
  my $user = shift;
  my $domainname = shift;
  my $folder = shift;

  my $cyrcmd =
    "/bin/su cyrus -c \"/usr/lib/cyrus/bin/reconstruct -C /etc/imapd/imapd.conf -r user".$HIERSEP.$user.$HIERSEP.$folder."@".$domainname."\"|";
  print "cyrcmd: $cyrcmd\n";
  if( open(CYRIN, $cyrcmd) != 0 ) {
    while( <CYRIN> ) {
      print $_;
    }
    close(CYRIN);
  } else {
    warn "unable to start cyrus reconstruct";
  }

}
