#!/usr/bin/perl -w

# This script try to convert a mailfilter file into sieve filter file.
# I have some variables defined in my primary filter file thats the reason
# why the options -v exists.

# example call
# perl convert_maildropfilter_sieve.pl -v HOME=/var/vmail/none.at/al -v FTC=' ["from","to","cc"] '\
# -v FTCD=' ["from","to","cc","delivered-to"] ' -v FTCR=' ["to","from","cc","reply-to"] '\
# -v FTCRP=' ["to","from","cc","return-path"] ' -file=/var/vmail/none.at/al/.mailfilters/extensions\
# > /var/vmail/none.at/al/sieve/extentions_converted.sieve

# This script is written by Aleksandar Lazic <al-dovecot@none.at>
# Version: 0.1
# Date: 2011.09.19

use strict;
use File::Slurp;
use Data::Dumper;
use Getopt::Long;

our $DEBUG=undef;
our $vars;
our $cmd_params;
our $mailfilter_file;

GetOptions ("variables=s%" => \$cmd_params,
            "file=s"   => \$mailfilter_file,
            "debug"    => \$DEBUG,
            );
print 'Commandline options ',Dumper($cmd_params),"\n" if $DEBUG;
print 'Commandline options HOME ',$cmd_params->{'HOME'},"\n" if $DEBUG;

if (-d $mailfilter_file || !-f $mailfilter_file ||!-s $mailfilter_file) {
  print 'File: ', $mailfilter_file,' is not readable ',$!,"\n";
  exit 2;
}

# read in a whole file into a array ref
my $array_ref = read_file( $mailfilter_file, { chomp => 1 , array_ref => 1 }) ;

print Dumper($array_ref),"\n" if $DEBUG;

sub _handle_var($);
sub _handle_if($);
sub _handle_include($);
sub _handle_to($);

# default modules for sieve
print 'require "envelope";
require "subaddress";
require "fileinto";
require "variables";
require "mailbox";
require "regex";

';

foreach my $aline (@{$array_ref}){
   next if  $aline =~ /^\#/;
  _handle_var($aline) if $aline =~ /^[A-Z]+/;
  _handle_if($aline) if $aline =~ /^if/;
  _handle_include($aline) if $aline =~ /^include/;

  if ($aline =~ /\s+to/) {
    my $dest_dir = _handle_to($aline);
    print 'Destination Directory >>',$dest_dir,"<<\n" if $DEBUG;
  }
}

print Dumper($vars),"\n" if $DEBUG;

sub _handle_if($) {
  my $line = shift;

  # the following if statements are handled
  print '_handle_if::line >>',$line,"<<\n" if $DEBUG;
  $line =~ s/^?\(From\|to\|cc\)/\$FTC/i;
  $line =~ s/^?\(to\|from\|cc\)/\$FTC/i;
  $line =~ s/^?\(To\|CC\|From\)/\$FTC/i;
  $line =~ s/^?\(To\|From\|Cc\|Reply-To\)/\$FTCR/i;
  $line =~ s/^?\(From\|To\|Cc\|Return-Path\)/\$FTCRP/i;
  print '_handle_if::line >>',$line,"<<\n" if $DEBUG;

  my ($ext_val1,$ext_val2) = (undef,undef);
  my ($ext_name1,$ext_name2) = (undef,undef);
  my $allof_or_anyof = undef;

  # FTCD="(From|To|Cc|Delivered-To)"
  # FTC = "(From|To|Cc)"
  #
  #   re> /if\s*\(+\$\{(\w+)\}\s+=~\s+\/\^?(.*)\/\)?\s+.*?\|\|\s+\(+\/\^?\$\{?(\w+)\}?:(.*)\//
  # data> if((${EXT} =~ /^webappsec/) || (/^$FTC:.*webappsec.*@securityfocus.com.*/))
  # 0: if((${EXT} =~ /^webappsec/) || (/^$FTC:.*webappsec.*@securityfocus.com.*/
  # 1: EXT
  # 2: webappsec
  # 3: FTC
  # 4: .*webappsec.*@securityfocus.com.*

  if($line =~/if\s*\(+\$\{(\w+)\}\s+=~\s+\/\^?(.*)\/\)?\s*.*?\|\|\s*\(*\/\^?\$\{?(\w+)\}?:(.*)\//){
    print "_handle_if::match frist regex\n" if $DEBUG;
    print '_handle_if::ext_parse >>',"($1,$2)<<\n" if $DEBUG;
    print '_handle_if::ext_parse >>',"($3,$4)<<\n" if $DEBUG && defined $3;
    $ext_val1 = $2;
    $ext_name2 = $cmd_params->{$3} if defined $3 && exists  $cmd_params->{$3};
    $ext_val2 = $4 if defined $4;

    if($ext_val1 =~ /\&\&/){
    # from original line 
    # if((${EXT} =~ /^mutt/ && ${EXT2} =~ /^dev/) || /^$FTC:(.*mutt-dev)?.*@(bugs\.guug\.de|mutt\.org|gbnet\.net).*/)
    # _handle_if::ext_val1 >>mutt/ && ${EXT2} =~ /^dev<<
      $ext_val1 =~ /(\w+)\/\)?\s+\&\&\s+\(?\$\{(\w+)\}\s+=~\s+\/\^?(.*)/;
      print '_handle_if::ext_parse and >>',"($1,$2,$3)<<\n" if $DEBUG;
      print '_handle_if::ext_parse and error >>',"$ext_val1<<\n" if $DEBUG && (!defined $1 or !defined $2 or !defined $3);
      $ext_val1 = "$1-$3";
    }
    print '_handle_if::ext_ error >>',$line,"<<\n" if $DEBUG && (!defined $ext_name2 or !defined $ext_val2);
    $allof_or_anyof = 'if anyof ( header :regex '.$ext_name2." \"$ext_val2\",\n";
    print '_handle_if::allof_or_anyof >>',$allof_or_anyof,"<<\n" if $DEBUG;
  # if(${EXT} =~ /^work/ && ${EXT2} =~ /^leica/)
  }elsif($line =~ /if\s*\(\$\{(\w+)\}\s+=~\s+\/\^?(.*)\/\s+.*?\$\{(\w+)\}\s+=~\s+\/\^(.*)\//){
    print "_handle_if::match second regex\n" if $DEBUG;
    print '_handle_if::ext_parse >>',"($1,$2)<<\n" if $DEBUG;
    print '_handle_if::ext_parse >>',"($3,$4)<<\n" if $DEBUG && defined $3;
    $ext_val1 = $2;
    $ext_val2 = $4 if defined $4;
  # if(${EXT} =~ /^dovecot/)
  }elsif ($line =~ /if\s*\(\$\{(\w+)\}\s+=~\s+\/\^?(.*)\//){
    print "_handle_if::match third regex\n" if $DEBUG;
    print '_handle_if::ext_parse >>',"($1,$2)<<\n" if $DEBUG;
    $ext_val1 = $2;
  }elsif ($line =~ /if\s*\(\/\^?\$\{?(\w+)\}?\s*:\s*(.*)\//){
    print "_handle_if::match foured regex\n" if $DEBUG;
    print '_handle_if::ext_parse >>',"($1,$2)<<\n" if $DEBUG;
    $ext_name1 = $cmd_params->{$1} if defined $1 && exists  $cmd_params->{$1};
    $ext_val1 = $2;
    if($ext_val1 =~ /(\S+)\/\s*\|\|\s*\(*\$\{?(\w+)\}?\s*=~\s*\/\^?(\w+)\/\s*\&\&\s*\$\{?(\w+)\}?\s*=~\s*\/\^?(\w+)/){
      print '_handle_if::ext_parse and >>',"($1,$2,$3,$4,$5)<<\n" if $DEBUG;
      print '_handle_if::ext_parse and error >>',"$ext_val1<<\n" if $DEBUG && (!defined $1 or !defined $3 or !defined $5);
      $allof_or_anyof = 'if anyof ( header :regex '.$ext_name1." \"$1\",\n";
      $ext_val1 = "$3-$5";
    }
  }

  if ( defined $ext_val1 ){
    print '_handle_if::ext_val1 >>',$ext_val1,"<<\n" if $DEBUG && defined $ext_val1;
    print '_handle_if::ext_val2 >>',$ext_val2,"<<\n" if $DEBUG && defined $ext_val2;

    # TODO make this line flexible
    if(not defined $allof_or_anyof){
      print 'if envelope :detail :matches "to" "',$ext_val1,'" {',"\n" if not defined $ext_name1;
      print 'if header :regex ',$ext_name1,' "',$ext_val1,'" {',"\n" if defined $ext_name1;
    }elsif(defined $allof_or_anyof){
      $allof_or_anyof .= '         envelope :detail :matches "to" "'.$ext_val1.'" ) {'."\n";
      print $allof_or_anyof;
    }
  }
  
} # end _handle_if

sub _handle_include($) {
  my $line = shift;

  # TODO will be added when needed
  return;
  print '_handle_include::line >>',$line,"<<\n" if $DEBUG;

  # _handle_include::line >>include $HOME/.mailfilters/vacation<<
  # $VAR1 = 'include $HOME';
  # $VAR2 = '.mailfilters';
  # $VAR3 = 'vacation';

  $line =~ s/.*include\s+\$//g;
  my @paths = split(/\//,$line);
  my $to_subst_variable = shift @paths;
  print '_handle_include::$to_subst_variable >>',$to_subst_variable,"<<\n" if $DEBUG;
  print '_handle_include:: ',Dumper(@paths),"\n" if $DEBUG;

  my $inc_dir = undef;
  if (exists  $vars->{$to_subst_variable}) {
    print '_handle_include::Variable::parinclude >>',$to_subst_variable,"<< found\n" if $DEBUG;
    $inc_dir = $vars->{$to_subst_variable};
  } elsif ( exists  $cmd_params->{$to_subst_variable}){
    print '_handle_include::Variable::cmd_params >>',$to_subst_variable,"<< found\n" if $DEBUG;
    $inc_dir = $cmd_params->{$to_subst_variable};
  }

  if($inc_dir){
#    my $inc_file = $inc_dir,'/',join('/',@paths);
#    print '_handle_include::include_path >>',$inc_file,"<<\n\n" if $DEBUG;
#    $array_ref = read_file( $inc_file, { chomp => 1 , array_ref => 1 }) if -f $inc_file && -s $inc_file && ! -d $inc_file;
  }
} # end _handle_if

sub _handle_to($) {
  my $line = shift;
  print '_handle_to::line >>',$line,"<<\n" if $DEBUG;

  # _handle_to::line >>  to $MY_MD/misc/smartwork/<<

  $line =~ s/\s+to\s+\$//g;
  # $MY_MD
  my @paths = split(/\//,$line);
  my $to_subst_variable = shift @paths;

  # $VAR1 = 'MY_MD';
  # $VAR2 = 'misc';
  # $VAR3 = 'smartwork';
  print '_handle_to:: ',Dumper(@paths),"\n" if $DEBUG;

#  print 'Variable >>',$paths[0],"<< found\n" if exists  $vars->{$paths[0]};
  print '_handle_to::Variable >>',$to_subst_variable,"<< found\n" if exists  $vars->{$to_subst_variable} && $DEBUG;
  print '  fileinto :create "',join('.',@paths),"\";\n}\n\n";

} # end _handle_if

sub _handle_var($) {
  my $line = shift;
  print '_handle_var::line >>',$line,"<<\n" if $DEBUG;

  $line =~ /(\S+)=(\S+)/;
  $vars->{$1} = $2;
} # end _handle_if
