* Sat Oct 11 2025 Brian Read <brianr@koozali.org> 11.0.0-5.sme
- Add Dovecot Sieve generation and application [SME: 13232]
This commit is contained in:
@@ -63,7 +63,7 @@ foreach my $userName (@users)
|
||||
next;
|
||||
}
|
||||
|
||||
for my $dotfile ( qw(.procmailrc .mailfilter) )
|
||||
for my $dotfile ( qw(.procmailrc .mailfilter .sievefilter) )
|
||||
{
|
||||
my $pathtohome = ($userName eq 'admin')? "/home/e-smith":"/home/e-smith/files/users/$userName";
|
||||
|
||||
@@ -87,4 +87,4 @@ foreach my $userName (@users)
|
||||
};
|
||||
}
|
||||
|
||||
exit (0);
|
||||
exit (0);
|
||||
@@ -2,45 +2,48 @@
|
||||
use esmith::config;
|
||||
use esmith::db;
|
||||
|
||||
use constant REGEX_DELIM => '/';
|
||||
|
||||
sub quote_regex {
|
||||
my ($pattern) = @_;
|
||||
my $delim = REGEX_DELIM;
|
||||
# Escape delimiter characters inside pattern
|
||||
$pattern =~ s/\Q$delim\E/\\$delim/g;
|
||||
return $delim . $pattern . $delim;
|
||||
}
|
||||
|
||||
my %conf;
|
||||
tie %conf, 'esmith::config';
|
||||
|
||||
my %accounts;
|
||||
tie %accounts, 'esmith::config', '/home/e-smith/db/accounts';
|
||||
|
||||
die "Username missing." unless defined ($USERNAME);
|
||||
die "Username missing." unless defined($USERNAME);
|
||||
|
||||
my $type = db_get_type(\%accounts, $USERNAME);
|
||||
|
||||
die
|
||||
"Account $USERNAME is not a user account; "
|
||||
. "update email forwarding failed.\n"
|
||||
unless $type eq 'user' || $USERNAME eq 'admin';
|
||||
unless $type eq 'user' || $USERNAME eq 'admin';
|
||||
|
||||
my %processmail;
|
||||
tie %processmail, 'esmith::config', '/home/e-smith/db/processmail';
|
||||
|
||||
# the syntax of imap folder names keeps changing
|
||||
my $sep = '.';
|
||||
|
||||
#get Global rules
|
||||
my @pmGlobRules = ();
|
||||
foreach (sort keys %processmail)
|
||||
{
|
||||
push (@pmGlobRules, $_)
|
||||
foreach (sort keys %processmail) {
|
||||
push(@pmGlobRules, $_)
|
||||
if (db_get_type(\%processmail, $_) eq 'pmGlobalRule');
|
||||
}
|
||||
|
||||
#if they have rules add them to the templete
|
||||
my $pmGlobRules = @pmGlobRules || '0';
|
||||
if ($pmGlobRules > 0)
|
||||
{
|
||||
if ($pmGlobRules > 0) {
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# ----start of Global rules--------\n";
|
||||
|
||||
my $pmGlobRule;
|
||||
foreach $pmGlobRule (sort {$a <=> $b} @pmGlobRules)
|
||||
{
|
||||
foreach my $pmGlobRule (sort { $a <=> $b } @pmGlobRules) {
|
||||
my $basis = db_get_prop(\%processmail, $pmGlobRule, "basis") || '';
|
||||
my $criterion = db_get_prop(\%processmail, $pmGlobRule, "criterion") || '';
|
||||
my $basis2 = db_get_prop(\%processmail, $pmGlobRule, "basis2") || '';
|
||||
@@ -52,113 +55,86 @@
|
||||
my $action = db_get_prop(\%processmail, $pmGlobRule, "action") || '';
|
||||
my $action2 = db_get_prop(\%processmail, $pmGlobRule, "action2") || '';
|
||||
|
||||
## headers include the basis in the criterion
|
||||
if ($basis eq 'headers')
|
||||
{
|
||||
if ($basis eq 'headers') {
|
||||
$basis = $criterion;
|
||||
$criterion = '';
|
||||
}
|
||||
|
||||
if ($basis2 eq 'headers')
|
||||
{
|
||||
if ($basis2 eq 'headers') {
|
||||
$basis2 = $criterion2;
|
||||
$criterion2 = '';
|
||||
}
|
||||
|
||||
## convert to procmail 'TO_' macro equivalent ??
|
||||
foreach ($basis, $basis2)
|
||||
{
|
||||
if ($_ eq 'TO_')
|
||||
{
|
||||
#$_ = '((Original-)?(Resent-)?(To|Cc|Bcc)|(X-Envelope|Apparently(-Resent)?)-To)';
|
||||
foreach ($basis, $basis2) {
|
||||
if ($_ eq 'TO_') {
|
||||
$_ = '(To|Cc)';
|
||||
}
|
||||
}
|
||||
|
||||
## construct the deliver line
|
||||
if ($action eq 'sort')
|
||||
{
|
||||
# to a folder
|
||||
$deliver1 = "to \"Maildir/"."$sep"."$deliver"."/\"";
|
||||
my $deliver1 = '';
|
||||
if ($action eq 'sort') {
|
||||
$deliver1 = "to \"Maildir/" . "$sep" . "$deliver" . "/\"";
|
||||
}
|
||||
elsif ($action eq 'forward')
|
||||
{
|
||||
# to an email
|
||||
$deliver1 = "to "."\"!$deliver\"";
|
||||
elsif ($action eq 'forward') {
|
||||
$deliver1 = "to " . "\"!$deliver\"";
|
||||
}
|
||||
elsif ($action eq 'delete')
|
||||
{
|
||||
# delete it, report, and add a blank line
|
||||
elsif ($action eq 'delete') {
|
||||
$deliver1 = "log \" --deleted --\" \n log \"\" \n exit";
|
||||
#$deliver1 = "log \"--- deleted --\" \n log \"From: $From \" \n log \"Subject: $Subject \" \n log \"\" \n exit";
|
||||
}
|
||||
else
|
||||
{
|
||||
# freeform
|
||||
else {
|
||||
$deliver1 = "$deliver";
|
||||
}
|
||||
|
||||
## construct the 2nd deliver line
|
||||
if ($action2 eq 'sort')
|
||||
{
|
||||
# to a folder
|
||||
$deliver2 = "\"Maildir/"."$sep"."$deliver2"."/\"";
|
||||
my $deliver2 = '';
|
||||
if ($action2 eq 'sort') {
|
||||
$deliver2 = "\"Maildir/" . "$sep" . "$deliver2" . "/\"";
|
||||
}
|
||||
elsif ($action2 eq 'forward')
|
||||
{
|
||||
# to an email
|
||||
elsif ($action2 eq 'forward') {
|
||||
$deliver2 = "\"!$deliver2\"";
|
||||
}
|
||||
|
||||
$OUT .= "\n";
|
||||
if ($secondtest eq '')
|
||||
{
|
||||
if ($basis =~ /(>|<)/)
|
||||
{
|
||||
if ($secondtest eq '') {
|
||||
if ($basis =~ /(>|<)/) {
|
||||
$OUT .= "if ( \$SIZE $basis $criterion )\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= "if ( /^"."$basis".".*$criterion/ )\n";
|
||||
else {
|
||||
my $regex = quote_regex("^$basis.*$criterion");
|
||||
$OUT .= "if ( $regex )\n";
|
||||
}
|
||||
$OUT .= "\{\n";
|
||||
$OUT .= "log \"--------- match user rule -- \"\n";
|
||||
$OUT .= "log \"--------- $basis $criterion -- \"\n";
|
||||
|
||||
}
|
||||
#basis2 can't test on size
|
||||
else
|
||||
{
|
||||
if ($basis =~ /(>|<)/)
|
||||
{
|
||||
$OUT .= "if (( \$SIZE $basis $criterion ) && ( /^"."$basis2".".*$criterion2/ ))\n";
|
||||
else {
|
||||
if ($basis =~ /(>|<)/) {
|
||||
$OUT .= "if (( \$SIZE $basis $criterion ) && ( /^" . "$basis2" . ".*$criterion2/ ))\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= "if (( /^"."$basis".".*$criterion/) && (/^"."$basis2".".*$criterion2/ ))\n";
|
||||
else {
|
||||
my $regex1 = quote_regex("^$basis.*$criterion");
|
||||
my $regex2 = quote_regex("^$basis2.*$criterion2");
|
||||
$OUT .= "if (( $regex1 ) && ( $regex2 ))\n";
|
||||
}
|
||||
$OUT .= "\{\n";
|
||||
$OUT .= "log \"--- match user rule ------------- \"\n";
|
||||
$OUT .= "log \"--- $basis $criterion & $basis2 $criterion2 -- \"\n";
|
||||
}
|
||||
|
||||
if ($copy eq 'no')
|
||||
{
|
||||
if ($copy eq 'no') {
|
||||
$OUT .= "$deliver1\n";
|
||||
$OUT .= "\}\n";
|
||||
}
|
||||
elsif ($copy eq 'yes' && $action2 eq 'inbox')
|
||||
{
|
||||
elsif ($copy eq 'yes' && $action2 eq 'inbox') {
|
||||
$OUT .= "cc Maildir\n";
|
||||
$OUT .= "$deliver1\n";
|
||||
$OUT .= "\}\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$OUT .= "cc $deliver2\n";
|
||||
$OUT .= "$deliver1\n";
|
||||
$OUT .= "\}\n";
|
||||
}
|
||||
}#foreach rule
|
||||
}#if rules exist
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,8 @@
|
||||
}
|
||||
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# User rule $pmRule\n";
|
||||
|
||||
if ($secondtest eq '')
|
||||
{
|
||||
if ($basis =~ /(>|<)/)
|
||||
@@ -159,6 +161,7 @@
|
||||
$OUT .= "$deliver1\n";
|
||||
$OUT .= "\}\n";
|
||||
}
|
||||
$OUT .= "# End of User rule $pmRule\n";
|
||||
}#foreach rule
|
||||
}#if rules exist
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,9 @@
|
||||
$secondtest = "* "."$basis2"."$criterion2"."\n";
|
||||
}
|
||||
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# User rule $pmRule";
|
||||
|
||||
if ($copy eq 'no')
|
||||
{
|
||||
$OUT .= "\n";
|
||||
@@ -162,6 +165,7 @@
|
||||
$OUT .= " $deliver2\n";
|
||||
$OUT .= "\}\n";
|
||||
}
|
||||
$OUT .= "# End of User rule $pmRule\n";
|
||||
}#foreach rule
|
||||
}#if rules exist
|
||||
}
|
||||
}
|
||||
@@ -18,15 +18,12 @@
|
||||
if ($EmailForward eq 'forward');
|
||||
}
|
||||
|
||||
if ($qmail{FilterType})
|
||||
{
|
||||
return '| /usr/bin/procmail ~/.procmailrc ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;'
|
||||
if ($qmail{FilterType} eq 'procmail');
|
||||
|
||||
return '| /usr/bin/maildrop ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;'
|
||||
if ($qmail{FilterType} eq 'maildrop' );
|
||||
}
|
||||
|
||||
return '# Procmail/Maildrop disabled for all users'
|
||||
}
|
||||
if ($qmail{FilterType})
|
||||
{
|
||||
return '| /usr/bin/procmail ~/.procmailrc ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;' if ($qmail{FilterType} eq 'procmail');
|
||||
return '| /usr/bin/maildrop ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;' if ($qmail{FilterType} eq 'maildrop');
|
||||
return '| /var/qmail/bin/preline -f /usr/libexec/dovecot/dovecot-lda -a "$RECIPIENT" -d "$USER" ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;' if ($qmail{FilterType} eq 'sieve');
|
||||
}
|
||||
return '# Procmail/Maildrop/sieve disabled for all users'
|
||||
}
|
||||
}
|
||||
@@ -22,15 +22,12 @@
|
||||
if ($EmailForward eq 'forward');
|
||||
}
|
||||
|
||||
if ($qmail{FilterType})
|
||||
{
|
||||
return '| /usr/bin/procmail ~/.procmailrc ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;'
|
||||
if ($qmail{FilterType} eq 'procmail');
|
||||
|
||||
return '| /usr/bin/maildrop ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;'
|
||||
if ($qmail{FilterType} eq 'maildrop' );
|
||||
}
|
||||
|
||||
return '# Procmail/Maildrop disabled for all users'
|
||||
if ($qmail{FilterType})
|
||||
{
|
||||
return '| /usr/bin/procmail ~/.procmailrc ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;' if ($qmail{FilterType} eq 'procmail');
|
||||
return '| /usr/bin/maildrop ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;' if ($qmail{FilterType} eq 'maildrop');
|
||||
return '| /var/qmail/bin/preline -f /usr/libexec/dovecot/dovecot-lda -a "$RECIPIENT" -d "$USER" ; if [ $? -ne 0 ] ; then exit -1; else exit 99; fi;' if ($qmail{FilterType} eq 'sieve');
|
||||
}
|
||||
return '# Procmail/Maildrop/Sieve disabled for all users'
|
||||
}
|
||||
}
|
||||
}
|
||||
22
root/etc/e-smith/templates-user/.sievefilter/00setup
Normal file
22
root/etc/e-smith/templates-user/.sievefilter/00setup
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
# vim: ft=perl:
|
||||
|
||||
use esmith::AccountsDB;
|
||||
use esmith::ConfigDB;
|
||||
|
||||
our $adb = esmith::AccountsDB->open_ro or die "Couldn't open AccountsDB";
|
||||
our $cdb = esmith::ConfigDB->open_ro or die "Couldn't open ConfigDB";
|
||||
|
||||
$user = $adb->get($USERNAME) or die "No user $USERNAME in AccountsDB";
|
||||
%props = $user->props;
|
||||
|
||||
our $sievesupport = $cdb->get_prop('sieve','status') || 'disabled';
|
||||
our $sieveuser = $props{Sieve} || 'enabled';
|
||||
our $zarafa1 = $props{zarafa} || 'disabled1';
|
||||
our $zarafa2 = ${'zarafa-server'}{GlobalForward} || 'disabled2';
|
||||
our $EmailForward = $props{EmailForward} || '';
|
||||
our $ForwardAddress = $props{ForwardAddress} || '';
|
||||
|
||||
$OUT = '';
|
||||
}
|
||||
|
||||
6
root/etc/e-smith/templates-user/.sievefilter/10header
Normal file
6
root/etc/e-smith/templates-user/.sievefilter/10header
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
$OUT .= "# ---- Dovecot Sieve: Defaults and requirements --------\n";
|
||||
$OUT .= "# Generated for user: $USERNAME\n";
|
||||
$OUT .= "# Sieve support (system): $sievesupport, Sieve (user): $sieveuser\n";
|
||||
$OUT .= "require [\"fileinto\", \"copy\", \"regex\", \"relational\", \"comparator-i;ascii-numeric\", \"duplicate\", \"envelope\", \"mime\"];\n";
|
||||
}
|
||||
28
root/etc/e-smith/templates-user/.sievefilter/15log
Normal file
28
root/etc/e-smith/templates-user/.sievefilter/15log
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
use esmith::config;
|
||||
use esmith::db;
|
||||
|
||||
my %processmail;
|
||||
tie %processmail, 'esmith::config', '/home/e-smith/db/processmail';
|
||||
|
||||
$OUT = '';
|
||||
|
||||
# control level of logging (comments only; Sieve has no direct logging)
|
||||
my $loglevel = db_get_prop(\%processmail, $USERNAME, "loglevel") || 'some';
|
||||
if ($loglevel eq 'none')
|
||||
{
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# ---- logging: none ------------------\n";
|
||||
}
|
||||
elsif ($loglevel eq 'some')
|
||||
{
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# ---- logging: some ------------------\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# ---- logging: verbose (debug) --------------\n";
|
||||
}
|
||||
}
|
||||
|
||||
9
root/etc/e-smith/templates-user/.sievefilter/25options
Normal file
9
root/etc/e-smith/templates-user/.sievefilter/25options
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# ---- delete duplicates (by Message-ID) -------\n";
|
||||
$OUT .= "if duplicate {\n";
|
||||
$OUT .= " discard;\n";
|
||||
$OUT .= " stop;\n";
|
||||
$OUT .= "}\n";
|
||||
}
|
||||
|
||||
236
root/etc/e-smith/templates-user/.sievefilter/40global
Normal file
236
root/etc/e-smith/templates-user/.sievefilter/40global
Normal file
@@ -0,0 +1,236 @@
|
||||
{
|
||||
use esmith::config;
|
||||
use esmith::db;
|
||||
|
||||
my %processmail;
|
||||
tie %processmail, 'esmith::config', '/home/e-smith/db/processmail';
|
||||
|
||||
#get Global rules
|
||||
my @pmGlobRules = ();
|
||||
foreach (sort keys %processmail)
|
||||
{
|
||||
push (@pmGlobRules, $_)
|
||||
if (db_get_type(\%processmail, $_) eq 'pmGlobalRule');
|
||||
}
|
||||
|
||||
#if they have rules add them to the templete
|
||||
my $pmGlobRules = @pmGlobRules || '0';
|
||||
if ($pmGlobRules > 0)
|
||||
{
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# --- start of Global Sieve rules ---------\n";
|
||||
|
||||
my $pmGlobRule;
|
||||
foreach $pmGlobRule (sort {$a <=> $b} @pmGlobRules)
|
||||
{
|
||||
my $basis = db_get_prop(\%processmail, $pmGlobRule, "basis") || '';
|
||||
my $criterion = db_get_prop(\%processmail, $pmGlobRule, "criterion") || '';
|
||||
my $basis2 = db_get_prop(\%processmail, $pmGlobRule, "basis2") || '';
|
||||
my $secondtest = db_get_prop(\%processmail, $pmGlobRule, "basis2") || '';
|
||||
my $criterion2 = db_get_prop(\%processmail, $pmGlobRule, "criterion2") || '';
|
||||
my $deliver = db_get_prop(\%processmail, $pmGlobRule, "deliver") || '';
|
||||
my $deliver2 = db_get_prop(\%processmail, $pmGlobRule, "deliver2") || '';
|
||||
my $copy = db_get_prop(\%processmail, $pmGlobRule, "copy") || '';
|
||||
my $action = db_get_prop(\%processmail, $pmGlobRule, "action") || '';
|
||||
my $action2 = db_get_prop(\%processmail, $pmGlobRule, "action2") || '';
|
||||
|
||||
# prepare/escape criteria for Sieve strings
|
||||
my $crit1 = $criterion; $crit1 =~ s/\\/\\\\/g; $crit1 =~ s/"/\\"/g;
|
||||
my $crit2 = $criterion2; $crit2 =~ s/\\/\\\\/g; $crit2 =~ s/"/\\"/g;
|
||||
|
||||
# build condition 1
|
||||
my $cond1 = '';
|
||||
if ($basis eq '<' || $basis eq '>')
|
||||
{
|
||||
my $num = $criterion; $num =~ s/\s+//g;
|
||||
$cond1 = ($basis eq '<') ? "size :under $num" : "size :over $num";
|
||||
}
|
||||
elsif ($basis eq 'TO_')
|
||||
{
|
||||
$cond1 = "anyof (address :all :contains [\"to\",\"cc\",\"bcc\"] \"$crit1\")";
|
||||
}
|
||||
elsif ($basis eq 'headers')
|
||||
{
|
||||
my $h = $criterion;
|
||||
if ($h =~ /^\s*\^?([A-Za-z0-9\-]+)\s*:\s*(.*)$/s)
|
||||
{
|
||||
my $hn = $1; my $hv = $2; $hv =~ s/\\/\\\\/g; $hv =~ s/"/\\"/g;
|
||||
$cond1 = "header :regex \"$hn\" \"$hv\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
$cond1 = "anyof (header :regex \"Subject\" \"$crit1\", address :all :regex [\"from\",\"to\",\"cc\"] \"$crit1\")";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
my %addr = map { $_ => 1 } qw(From To Cc Bcc Sender Reply-To Resent-From Resent-To Resent-Cc);
|
||||
if ($addr{$basis})
|
||||
{
|
||||
my $lb = lc $basis;
|
||||
$cond1 = "address :all :contains \"$lb\" \"$crit1\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
$cond1 = "header :contains \"$basis\" \"$crit1\"";
|
||||
}
|
||||
}
|
||||
|
||||
# build condition 2 if present
|
||||
my $cond2 = '';
|
||||
if ($secondtest ne '')
|
||||
{
|
||||
if ($basis2 eq '<' || $basis2 eq '>')
|
||||
{
|
||||
my $num2 = $criterion2; $num2 =~ s/\s+//g;
|
||||
$cond2 = ($basis2 eq '<') ? "size :under $num2" : "size :over $num2";
|
||||
}
|
||||
elsif ($basis2 eq 'TO_')
|
||||
{
|
||||
$cond2 = "anyof (address :all :contains [\"to\",\"cc\",\"bcc\"] \"$crit2\")";
|
||||
}
|
||||
elsif ($basis2 eq 'headers')
|
||||
{
|
||||
my $hh = $criterion2;
|
||||
if ($hh =~ /^\s*\^?([A-Za-z0-9\-]+)\s*:\s*(.*)$/s)
|
||||
{
|
||||
my $hn2 = $1; my $hv2 = $2; $hv2 =~ s/\\/\\\\/g; $hv2 =~ s/"/\\"/g;
|
||||
$cond2 = "header :regex \"$hn2\" \"$hv2\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
$cond2 = "anyof (header :regex \"Subject\" \"$crit2\", address :all :regex [\"from\",\"to\",\"cc\"] \"$crit2\")";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
my %addr2 = map { $_ => 1 } qw(From To Cc Bcc Sender Reply-To Resent-From Resent-To Resent-Cc);
|
||||
if ($addr2{$basis2})
|
||||
{
|
||||
my $lb2 = lc $basis2;
|
||||
$cond2 = "address :all :contains \"$lb2\" \"$crit2\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
$cond2 = "header :contains \"$basis2\" \"$crit2\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# mailbox names for sort/create
|
||||
my $mb1 = $deliver; $mb1 =~ s/"/\\"/g;
|
||||
my $mb2 = $deliver2; $mb2 =~ s/"/\\"/g;
|
||||
|
||||
my $mbox1 = ($mb1 eq 'junkmail') ? "INBOX.Junk" : "INBOX.$mb1";
|
||||
my $mbox2 = ($mb2 eq 'junkmail') ? "INBOX.Junk" : "INBOX.$mb2";
|
||||
|
||||
# begin rule
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# Global rule $pmGlobRule\n";
|
||||
if ($cond2 ne '')
|
||||
{
|
||||
$OUT .= "if allof ($cond1, $cond2) \{\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= "if $cond1 \{\n";
|
||||
}
|
||||
|
||||
# actions
|
||||
if ($copy eq 'no')
|
||||
{
|
||||
if ($action eq 'sort' || $action eq 'create')
|
||||
{
|
||||
$OUT .= " fileinto \"$mbox1\";\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
elsif ($action eq 'forward')
|
||||
{
|
||||
my $addr = $deliver; $addr =~ s/"/\\"/g;
|
||||
$OUT .= " redirect \"$addr\";\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
elsif ($action eq 'delete')
|
||||
{
|
||||
$OUT .= " discard;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= " # unsupported action \"$action\"; keeping in INBOX\n";
|
||||
$OUT .= " keep;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
}
|
||||
elsif ($copy eq 'yes' && $action2 eq 'inbox')
|
||||
{
|
||||
if ($action eq 'sort' || $action eq 'create')
|
||||
{
|
||||
$OUT .= " fileinto :copy \"$mbox1\";\n";
|
||||
$OUT .= " keep;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
elsif ($action eq 'forward')
|
||||
{
|
||||
my $addr = $deliver; $addr =~ s/"/\\"/g;
|
||||
$OUT .= " redirect :copy \"$addr\";\n";
|
||||
$OUT .= " keep;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
elsif ($action eq 'delete')
|
||||
{
|
||||
$OUT .= " discard;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= " # unsupported action \"$action\"; keeping in INBOX\n";
|
||||
$OUT .= " keep;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# two deliveries (copy + second action)
|
||||
if ($action eq 'sort' || $action eq 'create')
|
||||
{
|
||||
$OUT .= " fileinto :copy \"$mbox1\";\n";
|
||||
}
|
||||
elsif ($action eq 'forward')
|
||||
{
|
||||
my $addr = $deliver; $addr =~ s/"/\\"/g;
|
||||
$OUT .= " redirect :copy \"$addr\";\n";
|
||||
}
|
||||
elsif ($action eq 'delete')
|
||||
{
|
||||
$OUT .= " discard;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= " # unsupported primary action \"$action\"\n";
|
||||
}
|
||||
|
||||
if ($action2 eq 'sort')
|
||||
{
|
||||
$OUT .= " fileinto \"$mbox2\";\n";
|
||||
}
|
||||
elsif ($action2 eq 'forward')
|
||||
{
|
||||
my $addr2 = $deliver2; $addr2 =~ s/"/\\"/g;
|
||||
$OUT .= " redirect \"$addr2\";\n";
|
||||
}
|
||||
elsif ($action2 eq 'inbox')
|
||||
{
|
||||
$OUT .= " keep;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= " # unsupported secondary action \"$action2\"\n";
|
||||
}
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
|
||||
$OUT .= "\}\n";
|
||||
}#foreach rule
|
||||
}#if rules exist
|
||||
}
|
||||
436
root/etc/e-smith/templates-user/.sievefilter/60user
Normal file
436
root/etc/e-smith/templates-user/.sievefilter/60user
Normal file
@@ -0,0 +1,436 @@
|
||||
{
|
||||
use esmith::config;
|
||||
use esmith::db;
|
||||
|
||||
# Quote for Sieve string literals (escape " and \ for Sieve)
|
||||
sub sieve_quote {
|
||||
my ($s) = @_;
|
||||
$s //= '';
|
||||
$s =~ s/\\/\\\\/g; # backslash -> double backslash
|
||||
$s =~ s/"/\\"/g; # quote -> escaped quote
|
||||
return $s;
|
||||
}
|
||||
|
||||
# Prepare a regex pattern for embedding in a Sieve string (legacy fallback)
|
||||
sub sieve_regex_quote_basic {
|
||||
my ($s) = @_;
|
||||
$s //= '';
|
||||
$s =~ s/([\[\]\.])/\\$1/g; # make [ ] . literal in regex
|
||||
$s =~ s/\\/\\\\/g; # escape backslashes for Sieve string
|
||||
$s =~ s/"/\\"/g; # escape quotes for Sieve string
|
||||
return $s;
|
||||
}
|
||||
|
||||
# Pass through a true regex pattern but escape for Sieve string literal.
|
||||
sub sieve_regex_passthrough {
|
||||
my ($s) = @_;
|
||||
$s //= '';
|
||||
$s =~ s/\\/\\\\/g; # escape backslashes for Sieve string
|
||||
$s =~ s/"/\\"/g; # escape quotes for Sieve string
|
||||
return $s;
|
||||
}
|
||||
|
||||
# Normalize DB input for non-regex (contains) tests:
|
||||
# - remove user-added escapes for many common punctuation
|
||||
# - strip a leading "Subject.*" (case-insensitive) if present
|
||||
sub normalize_db_pattern {
|
||||
my ($s) = @_;
|
||||
$s //= '';
|
||||
$s =~ s/\\([\[\]\.\-\(\)\{\}\+\?\^\$\|])/$1/g; # unescape common punctuation
|
||||
$s =~ s/^\s*subject\s*\.\*\s*//i; # drop leading Subject.*
|
||||
$s =~ s/^\s+|\s+$//g; # trim
|
||||
return $s;
|
||||
}
|
||||
|
||||
# Simplify an email-like pattern to a plain substring for address tests.
|
||||
# Example: ".*user@domain\.tld" -> "user@domain.tld".
|
||||
sub simplify_email_value {
|
||||
my ($s) = @_;
|
||||
$s //= '';
|
||||
$s =~ s/^\s+|\s+$//g;
|
||||
return '' if $s eq '';
|
||||
$s =~ s/\.\*//g; # remove wildcard segments
|
||||
# unescape common punctuation including @
|
||||
$s =~ s/\\([@\[\]\.\-\(\)\{\}\+\?\^\$\|])/$1/g;
|
||||
$s =~ s/^\s+|\s+$//g;
|
||||
return $s;
|
||||
}
|
||||
|
||||
# Extract a domain from a simplified email-like string.
|
||||
# Returns '' if no clear domain found.
|
||||
sub extract_domain {
|
||||
my ($s) = @_;
|
||||
$s //= '';
|
||||
$s =~ s/^\s+|\s+$//g;
|
||||
return '' if $s eq '';
|
||||
if ($s =~ /@([^@\s<>"',;]+)/) {
|
||||
my $d = $1;
|
||||
$d =~ s/^[<"]+|[>"]+$//g;
|
||||
$d =~ s/[,"';].*$//;
|
||||
return lc $d;
|
||||
}
|
||||
# bare domain case (no @)
|
||||
if ($s =~ /^[A-Za-z0-9](?:[A-Za-z0-9\.\-]*[A-Za-z0-9])?\.[A-Za-z0-9\-]{2,}$/) {
|
||||
return lc $s;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
# Build a robust Subject contains test:
|
||||
# - raw header :contains "Subject" original
|
||||
# - MIME-decoded header :mime :contains "Subject" underscore->space variant
|
||||
sub build_subject_contains_anyof {
|
||||
my ($p) = @_;
|
||||
my $p_us2sp = $p; $p_us2sp =~ s/_/ /g;
|
||||
return "anyof (header :contains \"Subject\" \"$p\", header :mime :contains \"Subject\" \"$p_us2sp\")";
|
||||
}
|
||||
|
||||
# Build Subject+addresses fallback anyof clause
|
||||
sub build_subject_addr_contains_anyof {
|
||||
my ($p) = @_;
|
||||
my $p_us2sp = $p; $p_us2sp =~ s/_/ /g;
|
||||
return "anyof (header :contains \"Subject\" \"$p\", header :mime :contains \"Subject\" \"$p_us2sp\", address :all :contains [\"from\",\"to\",\"cc\"] \"$p\")";
|
||||
}
|
||||
|
||||
my %processmail;
|
||||
tie %processmail, 'esmith::config', '/home/e-smith/db/processmail';
|
||||
|
||||
# get users rules
|
||||
my @pmRules = ();
|
||||
foreach (sort keys %processmail)
|
||||
{
|
||||
push (@pmRules, $_)
|
||||
if (db_get_type(\%processmail, $_) eq $USERNAME);
|
||||
}
|
||||
|
||||
# if they have rules add them to the template
|
||||
my $pmRules = @pmRules || '0';
|
||||
if ($pmRules > 0)
|
||||
{
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# ---- user Sieve rules (".$pmRules.")------------------\n";
|
||||
|
||||
my $pmRule;
|
||||
foreach $pmRule (sort @pmRules)
|
||||
{
|
||||
my $basis = db_get_prop(\%processmail, $pmRule, "basis") || '';
|
||||
my $criterion = db_get_prop(\%processmail, $pmRule, "criterion") || '';
|
||||
my $basis2 = db_get_prop(\%processmail, $pmRule, "basis2") || '';
|
||||
my $secondtest = db_get_prop(\%processmail, $pmRule, "basis2") || '';
|
||||
my $criterion2 = db_get_prop(\%processmail, $pmRule, "criterion2") || '';
|
||||
my $deliver = db_get_prop(\%processmail, $pmRule, "deliver") || '';
|
||||
my $deliver2 = db_get_prop(\%processmail, $pmRule, "deliver2") || '';
|
||||
my $copy = db_get_prop(\%processmail, $pmRule, "copy") || '';
|
||||
my $action = db_get_prop(\%processmail, $pmRule, "action") || '';
|
||||
my $action2 = db_get_prop(\%processmail, $pmRule, "action2") || '';
|
||||
|
||||
# Normalize DB criteria (for contains/fallback paths)
|
||||
my $norm1 = normalize_db_pattern($criterion);
|
||||
my $norm2 = normalize_db_pattern($criterion2);
|
||||
|
||||
# Prepare strings for contains tests
|
||||
my $crit1 = sieve_quote($norm1);
|
||||
my $crit2 = sieve_quote($norm2);
|
||||
|
||||
# build condition 1
|
||||
my $cond1 = '';
|
||||
if ($basis eq '<' || $basis eq '>')
|
||||
{
|
||||
my $num = $criterion; $num =~ s/\s+//g;
|
||||
$cond1 = ($basis eq '<') ? "size :under $num" : "size :over $num";
|
||||
}
|
||||
elsif ($basis eq 'TO_')
|
||||
{
|
||||
$cond1 = "anyof (address :all :contains [\"to\",\"cc\",\"bcc\"] \"$crit1\")";
|
||||
}
|
||||
elsif ($basis eq 'headers')
|
||||
{
|
||||
# Use raw DB value to preserve regex meta (.*, [], \., etc.) for parsing
|
||||
my $raw = $criterion // '';
|
||||
my $h = $raw; $h =~ s/^\s+|\s+$//g;
|
||||
|
||||
# Parse "HeaderName(s): value" (accept (From|To):... or \(From\|To\):...)
|
||||
if ($h =~ /^\s*\^?\s*(.*?)\s*:\s*(.*)$/s)
|
||||
{
|
||||
my ($names, $hv) = ($1, $2);
|
||||
$names =~ s/^\s*(?:\\?\()\s*//; # optional leading ( or \(
|
||||
$names =~ s/\s*(?:\\?\))\s*$//; # optional trailing ) or \)
|
||||
$names =~ s/\\\|/|/g; # treat \| as |
|
||||
my @hn = split /\|/, $names;
|
||||
@hn = grep { defined $_ && $_ ne '' } @hn;
|
||||
|
||||
if (@hn) {
|
||||
my %addr = map { $_ => 1 } qw(From To Cc Bcc Sender Reply-To Resent-From Resent-To Resent-Cc);
|
||||
my $all_addr = 1; for my $n (@hn) { $all_addr &&= exists $addr{$n}; }
|
||||
my $hv_simple = simplify_email_value($hv);
|
||||
my $hv_domain = extract_domain($hv_simple);
|
||||
|
||||
if ($all_addr && $hv_domain ne '') {
|
||||
my @lb = map { lc $_ } @hn;
|
||||
my $hn_list = join '","', @lb;
|
||||
my $dom_q = sieve_quote($hv_domain);
|
||||
$cond1 = "address :domain :is [\"$hn_list\"] \"$dom_q\"";
|
||||
} else {
|
||||
# Fall back to true regex against listed headers
|
||||
my @hn_q = map { sieve_quote($_) } @hn;
|
||||
my $hn_list = join '","', @hn_q;
|
||||
my $hv_re = sieve_regex_passthrough($hv);
|
||||
$cond1 = "header :regex [\"$hn_list\"] \"$hv_re\"";
|
||||
}
|
||||
}
|
||||
else {
|
||||
# No valid header names extracted: prefer address match only if email/domain-like
|
||||
my $hv_simple = simplify_email_value($h);
|
||||
my $hv_domain = extract_domain($hv_simple);
|
||||
if ($hv_domain ne '') {
|
||||
my $dq = sieve_quote($hv_domain);
|
||||
$cond1 = "address :domain :is [\"from\",\"to\",\"cc\"] \"$dq\"";
|
||||
} else {
|
||||
my $p = sieve_quote($norm1);
|
||||
$cond1 = build_subject_addr_contains_anyof($p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# No "Header: value" structure: prefer address match only if email/domain-like
|
||||
my $hv_simple = simplify_email_value($h);
|
||||
my $hv_domain = extract_domain($hv_simple);
|
||||
if ($hv_domain ne '') {
|
||||
my $dq = sieve_quote($hv_domain);
|
||||
$cond1 = "address :domain :is [\"from\",\"to\",\"cc\"] \"$dq\"";
|
||||
} else {
|
||||
my $p = sieve_quote($norm1);
|
||||
$cond1 = build_subject_addr_contains_anyof($p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# Non-"headers" basis (explicit header names)
|
||||
if (lc($basis) eq 'subject') {
|
||||
my $p = $crit1;
|
||||
$cond1 = build_subject_contains_anyof($p);
|
||||
} else {
|
||||
my %addr = map { $_ => 1 } qw(From To Cc Bcc Sender Reply-To Resent-From Resent-To Resent-Cc);
|
||||
if ($addr{$basis})
|
||||
{
|
||||
my $lb = lc $basis;
|
||||
$cond1 = "address :all :contains \"$lb\" \"$crit1\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
$cond1 = "header :contains \"$basis\" \"$crit1\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# build condition 2 if present
|
||||
my $cond2 = '';
|
||||
if ($secondtest ne '')
|
||||
{
|
||||
if ($basis2 eq '<' || $basis2 eq '>')
|
||||
{
|
||||
my $num2 = $criterion2; $num2 =~ s/\s+//g;
|
||||
$cond2 = ($basis2 eq '<') ? "size :under $num2" : "size :over $num2";
|
||||
}
|
||||
elsif ($basis2 eq 'TO_')
|
||||
{
|
||||
$cond2 = "anyof (address :all :contains [\"to\",\"cc\",\"bcc\"] \"$crit2\")";
|
||||
}
|
||||
elsif ($basis2 eq 'headers')
|
||||
{
|
||||
my $raw2 = $criterion2 // '';
|
||||
my $hh = $raw2; $hh =~ s/^\s+|\s+$//g;
|
||||
|
||||
if ($hh =~ /^\s*\^?\s*(.*?)\s*:\s*(.*)$/s)
|
||||
{
|
||||
my ($names2, $hv2) = ($1, $2);
|
||||
$names2 =~ s/^\s*(?:\\?\()\s*//;
|
||||
$names2 =~ s/\s*(?:\\?\))\s*$//;
|
||||
$names2 =~ s/\\\|/|/g;
|
||||
my @hn2 = split /\|/, $names2;
|
||||
@hn2 = grep { defined $_ && $_ ne '' } @hn2;
|
||||
|
||||
if (@hn2) {
|
||||
my %addr2 = map { $_ => 1 } qw(From To Cc Bcc Sender Reply-To Resent-From Resent-To Resent-Cc);
|
||||
my $all_addr2 = 1; for my $n2 (@hn2) { $all_addr2 &&= exists $addr2{$n2}; }
|
||||
my $hv2_simple = simplify_email_value($hv2);
|
||||
my $hv2_domain = extract_domain($hv2_simple);
|
||||
|
||||
if ($all_addr2 && $hv2_domain ne '') {
|
||||
my @lb2 = map { lc $_ } @hn2;
|
||||
my $hn2_list = join '","', @lb2;
|
||||
my $dq2 = sieve_quote($hv2_domain);
|
||||
$cond2 = "address :domain :is [\"$hn2_list\"] \"$dq2\"";
|
||||
} else {
|
||||
my @hn2_q = map { sieve_quote($_) } @hn2;
|
||||
my $hn2_list = join '","', @hn2_q;
|
||||
my $hv2_re = sieve_regex_passthrough($hv2);
|
||||
$cond2 = "header :regex [\"$hn2_list\"] \"$hv2_re\"";
|
||||
}
|
||||
}
|
||||
else {
|
||||
# No valid header names: prefer address match only if email/domain-like
|
||||
my $hv2_simple = simplify_email_value($hh);
|
||||
my $hv2_domain = extract_domain($hv2_simple);
|
||||
if ($hv2_domain ne '') {
|
||||
my $dq2 = sieve_quote($hv2_domain);
|
||||
$cond2 = "address :domain :is [\"from\",\"to\",\"cc\"] \"$dq2\"";
|
||||
} else {
|
||||
my $p2 = sieve_quote($norm2);
|
||||
$cond2 = build_subject_addr_contains_anyof($p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# No "Header: value" structure: prefer address match only if email/domain-like
|
||||
my $hv2_simple = simplify_email_value($hh);
|
||||
my $hv2_domain = extract_domain($hv2_simple);
|
||||
if ($hv2_domain ne '') {
|
||||
my $dq2 = sieve_quote($hv2_domain);
|
||||
$cond2 = "address :domain :is [\"from\",\"to\",\"cc\"] \"$dq2\"";
|
||||
} else {
|
||||
my $p2 = sieve_quote($norm2);
|
||||
$cond2 = build_subject_addr_contains_anyof($p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lc($basis2) eq 'subject') {
|
||||
my $p2 = $crit2;
|
||||
$cond2 = build_subject_contains_anyof($p2);
|
||||
} else {
|
||||
my %addr2 = map { $_ => 1 } qw(From To Cc Bcc Sender Reply-To Resent-From Resent-To Resent-Cc);
|
||||
if ($addr2{$basis2})
|
||||
{
|
||||
my $lb2 = lc $basis2;
|
||||
$cond2 = "address :all :contains \"$lb2\" \"$crit2\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
$cond2 = "header :contains \"$basis2\" \"$crit2\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# mailbox names for sort/create (sanitize: turn "/" into "." to avoid invalid names)
|
||||
my $mb1 = $deliver; $mb1 =~ s/"/\\"/g; $mb1 =~ s|/|.|g;
|
||||
my $mb2 = $deliver2; $mb2 =~ s/"/\\"/g; $mb2 =~ s|/|.|g;
|
||||
|
||||
my $mbox1 = ($mb1 eq 'junkmail') ? "Junk" : "$mb1";
|
||||
my $mbox2 = ($mb2 eq 'junkmail') ? "Junk" : "$mb2";
|
||||
|
||||
# begin rule
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# User rule $pmRule\n";
|
||||
if ($cond2 ne '')
|
||||
{
|
||||
$OUT .= "if allof ($cond1, $cond2) \{\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= "if $cond1 \{\n";
|
||||
}
|
||||
|
||||
# actions
|
||||
if ($copy eq 'no')
|
||||
{
|
||||
if ($action eq 'sort' || $action eq 'create')
|
||||
{
|
||||
$OUT .= " fileinto \"$mbox1\";\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
elsif ($action eq 'forward')
|
||||
{
|
||||
my $addr = $deliver; $addr =~ s/"/\\"/g;
|
||||
$OUT .= " redirect \"$addr\";\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
elsif ($action eq 'delete')
|
||||
{
|
||||
$OUT .= " discard;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= " # unsupported action \"$action\"; keeping in INBOX\n";
|
||||
$OUT .= " keep;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
}
|
||||
elsif ($copy eq 'yes' && $action2 eq 'inbox')
|
||||
{
|
||||
if ($action eq 'sort' || $action eq 'create')
|
||||
{
|
||||
$OUT .= " fileinto :copy \"$mbox1\";\n";
|
||||
$OUT .= " keep;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
elsif ($action eq 'forward')
|
||||
{
|
||||
my $addr = $deliver; $addr =~ s/"/\\"/g;
|
||||
$OUT .= " redirect :copy \"$addr\";\n";
|
||||
$OUT .= " keep;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
elsif ($action eq 'delete')
|
||||
{
|
||||
$OUT .= " discard;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= " # unsupported action \"$action\"; keeping in INBOX\n";
|
||||
$OUT .= " keep;\n";
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
# two deliveries (copy + second action)
|
||||
if ($action eq 'sort' || $action eq 'create')
|
||||
{
|
||||
$OUT .= " fileinto :copy \"$mbox1\";\n";
|
||||
}
|
||||
elsif ($action eq 'forward')
|
||||
{
|
||||
my $addr = $deliver; $addr =~ s/"/\\"/g;
|
||||
$OUT .= " redirect :copy \"$addr\";\n";
|
||||
}
|
||||
elsif ($action eq 'delete')
|
||||
{
|
||||
$OUT .= " discard;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= " # unsupported primary action \"$action\"\n";
|
||||
}
|
||||
|
||||
if ($action2 eq 'sort')
|
||||
{
|
||||
$OUT .= " fileinto \"$mbox2\";\n";
|
||||
}
|
||||
elsif ($action2 eq 'forward')
|
||||
{
|
||||
my $addr2 = $deliver2; $addr2 =~ s/"/\\"/g;
|
||||
$OUT .= " redirect \"$addr2\";\n";
|
||||
}
|
||||
elsif ($action2 eq 'inbox')
|
||||
{
|
||||
$OUT .= " keep;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$OUT .= " # unsupported secondary action \"$action2\"\n";
|
||||
}
|
||||
$OUT .= " stop;\n";
|
||||
}
|
||||
$OUT .= "\}\n";
|
||||
$OUT .= "# End of User rule $pmRule\n";
|
||||
}#foreach rule
|
||||
}#if rules exist
|
||||
}
|
||||
7
root/etc/e-smith/templates-user/.sievefilter/90default
Normal file
7
root/etc/e-smith/templates-user/.sievefilter/90default
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# ---- to the inbox (implicit keep if no rules matched) ------------------\n";
|
||||
$OUT .= "keep;\n";
|
||||
$OUT .= "\n";
|
||||
$OUT .= "# ---- end of rules ------------------\n";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
|
||||
$OUT .=<<"END"
|
||||
plugin {
|
||||
sieve = file:/home/e-smith/files/users/%u/.sievefilter
|
||||
}
|
||||
END
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
$OUT .=<<"END"
|
||||
plugin {
|
||||
sieve_extensions = +fileinto +copy +regex +mime +body +duplicate
|
||||
}
|
||||
END
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,29 @@ my $pdb;
|
||||
#my $hdb
|
||||
#my $ddb
|
||||
|
||||
# Extra routine
|
||||
sub do_extras {
|
||||
#my $fred = 1/0;
|
||||
my $c = shift;
|
||||
if (defined $c->param("ApplyRules")){
|
||||
$user = $c->get_panel_user();
|
||||
$res = qx(sieve-filter -W -u $user -e ~$user/.sievefilter INBOX expunge 2>&1 | tee /var/log/sieve-filter.log | grep -v "warning" | grep -v "left message in mailbox 'INBOX'" | wc -l);
|
||||
$ms_data{command_result} = ($res == 0) ? 'none' : $res;
|
||||
$c->do_display('TABLE');
|
||||
} elsif (defined $c->param("ReSerialise")) {
|
||||
|
||||
} elsif (defined $c->param("GenerateRule")) {
|
||||
$res = qx(signal-event mailsorting-conf)
|
||||
}
|
||||
|
||||
#my $referer = $c->req->headers->referrer;
|
||||
#if ($referer) {
|
||||
## Redirect back to the referring URL
|
||||
#$c->redirect_to($referer);
|
||||
#} else {
|
||||
# }
|
||||
}
|
||||
|
||||
# Validation routines - parameters for each panel
|
||||
|
||||
sub validate_TABLE {
|
||||
@@ -106,18 +129,61 @@ my $pdb;
|
||||
sub get_data_for_panel_TABLE {
|
||||
# Return a hash with the fields required which will be loaded into the shared data
|
||||
my $c = shift;
|
||||
$cdb = esmith::ConfigDB::UTF8->open();
|
||||
$adb = esmith::AccountsDB::UTF8->open || die "Couldn't open accounts db";
|
||||
my $PanelUser = $c->get_panel_user();
|
||||
# Count number of emails in top level INBOX
|
||||
my $inbox_dir;
|
||||
if ($PanelUser eq 'admin'){
|
||||
$inbox_dir = "/home/e-smith/Maildir/cur/";
|
||||
} else {
|
||||
$inbox_dir = "/home/e-smith/files/users/$PanelUser/Maildir/cur/";
|
||||
}
|
||||
opendir(my $dh, $inbox_dir) or die "Cannot open directory: $!";
|
||||
my @files = grep { -f "$inbox_dir/$_" && !/^\.{1,2}$/ } readdir($dh);
|
||||
closedir($dh);
|
||||
my $file_count = scalar @files;
|
||||
# Get a list of the users.
|
||||
my @users = $adb->get('admin');
|
||||
push @users, $adb->users();
|
||||
my @actualusers;
|
||||
|
||||
foreach my $user (@users) {
|
||||
push @actualusers, [ $user->key()." - ".$user->prop('FirstName') . " " . $user->prop('LastName'),$user->key()];
|
||||
}
|
||||
|
||||
my $actualusers_ref = \@actualusers; # array reference of key => firstname.lastname hashrefs
|
||||
|
||||
my %ret = (
|
||||
'Data1'=>'Data for TABLE', #Example
|
||||
# fields from Inputs in TABLE $fields['TABLE']
|
||||
'account'=>$c->get_panel_user(),
|
||||
'account'=> $PanelUser,
|
||||
'username'=> $c->get_full_name(),
|
||||
|
||||
'FilterType' => $c->get_filtertype($PanelUser),
|
||||
'Geekmode' => $adb->get_prop($PanelUser, "Geekmode") || FALSE,
|
||||
'EmailCount' => $file_count,
|
||||
'users' => $actualusers_ref
|
||||
);
|
||||
return %ret;
|
||||
}
|
||||
|
||||
sub get_filtertype{
|
||||
my $c = shift;
|
||||
my $PanelUser = shift;
|
||||
$cdb = esmith::ConfigDB::UTF8->open();
|
||||
$adb = esmith::AccountsDB::UTF8->open || die "Couldn't open accounts db";
|
||||
#Either the filtertype is set per user or it is global
|
||||
my $globalFilterType = $cdb->get_prop('qmail', "FilterType");
|
||||
if (! $globalFilterType) {
|
||||
return $adb->get_prop($PanelUser,'FilterType') || ''
|
||||
} else {
|
||||
return $globalFilterType;
|
||||
}
|
||||
}
|
||||
|
||||
sub get_data_for_panel_RULES {
|
||||
# Return a hash with the fields required which will be loaded into the shared data
|
||||
my $cdb = esmith::ConfigDB::UTF8->open();
|
||||
my $c = shift;
|
||||
my $pdb = esmith::ConfigDB::UTF8->open('processmail') or die "Could not open processmail DB\n";
|
||||
my $PanelUser = $c->get_panel_user();
|
||||
@@ -142,10 +208,18 @@ sub get_data_for_panel_RULES {
|
||||
my $rule_info = get_rule_from_db($c, $rule);
|
||||
# and add heading message
|
||||
$rule_info->{topmessage} = $c->l('ms_You_can_change_the_order');
|
||||
|
||||
my $FilterType = $c->get_filtertype($PanelUser);
|
||||
my $FilterRule = 'No filter configured';
|
||||
my $DBRemoveRule = "Db entries:".$c->esmith_db_record_to_multiline($pdb,$key,$PanelUser);
|
||||
$FilterRule = "\nProcmail Rule:\n--------------\n".$c->get_procmail_rule($pdb,$key,$PanelUser) if $FilterType eq 'procmail';
|
||||
$FilterRule = "\nMaildrop Rule:\n--------------\n".$c->get_maildrop_rule($pdb,$key,$PanelUser) if $FilterType eq 'maildrop';
|
||||
$FilterRule = "\nDovecot-Sieve Rule:\n----------------------\n".$c->get_sieve_rule($pdb,$key,$PanelUser) if $FilterType eq 'sieve';
|
||||
|
||||
# Add/override any fields specific to the panel as needed
|
||||
my %ret = (
|
||||
%$rule_info,
|
||||
'FilterRule'=> $FilterRule
|
||||
);
|
||||
|
||||
return %ret;
|
||||
@@ -154,12 +228,21 @@ sub get_data_for_panel_RULES {
|
||||
sub get_data_for_panel_REMOVE {
|
||||
# Return a hash with the fields required which will be loaded into the shared data
|
||||
my $c = shift;
|
||||
my $cdb = esmith::ConfigDB::UTF8->open();
|
||||
my $pdb = esmith::ConfigDB::UTF8->open('processmail') or die "Could not open processmail DB\n";
|
||||
my $PanelUser = $c->get_panel_user();
|
||||
my $key = $c->param('Selected');
|
||||
my $FilterType = $c->get_filtertype($PanelUser);
|
||||
my $FilterRule = 'No filter configured';
|
||||
my $DBRemoveRule = "Db entries:".$c->esmith_db_record_to_multiline($pdb,$key);
|
||||
$FilterRule = "\nProcmail Rule:\n--------------\n".$c->get_procmail_rule($pdb,$key,$PanelUser) if $FilterType eq 'procmail';
|
||||
$FilterRule = "\nMaildrop Rule:\n--------------\n".$c->get_maildrop_rule($pdb,$key,$PanelUser) if $FilterType eq 'maildrop';
|
||||
$FilterRule = "\nDovecot-Sieve Rule:\n----------------------\n".$c->get_sieve_rule($pdb,$key,$PanelUser) if $FilterType eq 'sieve';
|
||||
|
||||
my %ret = (
|
||||
# fields from Inputs in REMOVE $fields['REMOVE']
|
||||
'RemoveRule'=>"Db entries:\n------------\n".$c->esmith_db_record_to_multiline($pdb,$key)."\n\nProcmail Rule:\n--------------".$c->get_procmail_rule($pdb,$key),
|
||||
'RemoveRule'=> $DBRemoveRule,
|
||||
'FilterRule' => $FilterRule
|
||||
);
|
||||
return %ret;
|
||||
}
|
||||
@@ -338,6 +421,7 @@ sub get_getAllRules {
|
||||
my $PanelUser = $c->get_panel_user();
|
||||
my $rec = $pdb->get($rule); # || return "Rule:$rule not found";
|
||||
$rec->delete;
|
||||
$c->app->log->info("Running: "."/sbin/e-smith/signal-event mailsorting-conf $PanelUser");
|
||||
unless ( system ("/sbin/e-smith/signal-event mailsorting-conf $PanelUser") == 0 )
|
||||
{ return $self->error('ERROR_UPDATING'); }
|
||||
return 'ok';
|
||||
@@ -355,7 +439,21 @@ sub create_link{
|
||||
sub get_panel_user
|
||||
{
|
||||
my $c = shift;
|
||||
return $c->session->{username};
|
||||
my $adb = esmith::AccountsDB::UTF8->open();
|
||||
if (!$c->is_admin){
|
||||
return $c->session->{username};
|
||||
} else {
|
||||
if (my $thisUser = $c->param('account')) {
|
||||
#Check it is a valid user
|
||||
if ($adb->get_prop($thisUser, "FirstName")){
|
||||
return $thisUser
|
||||
} else {
|
||||
return $c->session->{username}
|
||||
}
|
||||
} else {
|
||||
return $c->session->{username}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub get_full_name{
|
||||
@@ -520,6 +618,7 @@ sub save_rule
|
||||
foreach ("criterion","criterion2","action","action2","copy","basis","basis2","deliver","deliver2" )
|
||||
{ $pdb->set_prop($rule, "$_", $filtered{$_}); }
|
||||
|
||||
$c->app->log->info("Running: "."/sbin/e-smith/signal-event mailsorting-conf $PanelUser");
|
||||
unless ( system ("/sbin/e-smith/signal-event mailsorting-conf $PanelUser") == 0 )
|
||||
{ return 'ERROR_UPDATING'; }
|
||||
|
||||
@@ -538,7 +637,7 @@ sub esmith_db_record_to_multiline {
|
||||
next unless defined $value && $value ne '';
|
||||
push @lines, "$field: $value";
|
||||
}
|
||||
return join("\n", @lines);
|
||||
return "$key:"."\n------------\n".join("\n", @lines);
|
||||
}
|
||||
|
||||
sub get_esmith_db_record_hashref {
|
||||
@@ -549,54 +648,107 @@ sub get_esmith_db_record_hashref {
|
||||
return \%props; # return a hashref
|
||||
}
|
||||
|
||||
sub get_maildrop_rule {
|
||||
my $c = shift;
|
||||
my ($pdb, $key,$PanelUser) = @_;
|
||||
return $c->extract_rule_from_file("~$PanelUser/.mailfilter",$key)
|
||||
}
|
||||
|
||||
sub expand_tilde {
|
||||
my ($filename) = @_;
|
||||
# Special case for ~admin and ~admin/...
|
||||
$filename =~ s{\A~admin(?:/|\z)}{/home/e-smith/};
|
||||
# Generic ~ and ~user handling
|
||||
$filename =~ s{\A~([^/]*)}
|
||||
{ $1 ? (getpwnam($1))[7] : ($ENV{HOME} || (getpwuid($<))[7]) }ex;
|
||||
return $filename;
|
||||
}
|
||||
|
||||
sub extract_rule_from_file {
|
||||
my ($c,$filename, $rule_id) = @_;
|
||||
$c->app->log->info("get rule:$filename");
|
||||
|
||||
# Expand ~ to full home directory path if present
|
||||
$filename = expand_tilde($filename);
|
||||
$c->app->log->info("get rule:$filename");
|
||||
|
||||
# Read entire file content
|
||||
open my $fh, '<', $filename or die "Could not open file '$filename': $!";
|
||||
local $/; # Enable slurp mode to read whole file
|
||||
my $content = <$fh>;
|
||||
close $fh;
|
||||
|
||||
# Prepare start and end markers for the rule
|
||||
my $start_marker = "# User rule $rule_id";
|
||||
my $end_marker = "# End of User rule $rule_id";
|
||||
|
||||
# Extract the text block between the markers
|
||||
if ($content =~ /(\Q$start_marker\E.*?\Q$end_marker\E)/s) {
|
||||
return $1; # return matched block including markers
|
||||
} else {
|
||||
return "No matching rule found $filename $rule_id"
|
||||
}
|
||||
}
|
||||
|
||||
sub get_sieve_rule {
|
||||
my $c = shift;
|
||||
my ($pdb, $key,$PanelUser) = @_;
|
||||
return $c->extract_rule_from_file("~$PanelUser/.sievefilter",$key)
|
||||
}
|
||||
|
||||
sub get_procmail_rule {
|
||||
my $c = shift;
|
||||
# Logic taken from template expansion
|
||||
my ($pdb, $key) = @_;
|
||||
|
||||
my $basis = $pdb->get_prop($key, "basis") || '';
|
||||
my $criterion = $pdb->get_prop( $key, "criterion") || '';
|
||||
my $basis2 = $pdb->get_prop( $key, "basis2") || '';
|
||||
my $secondtest_orig = $pdb->get_prop( $key, "basis2") || '';
|
||||
my $criterion2 = $pdb->get_prop( $key, "criterion2") || '';
|
||||
my $deliver = $pdb->get_prop( $key, "deliver") || '';
|
||||
my $deliver2 = $pdb->get_prop( $key, "deliver2") || '';
|
||||
my $copy = $pdb->get_prop( $key, "copy") || '';
|
||||
my $action = $pdb->get_prop( $key, "action") || '';
|
||||
my $action2 = $pdb->get_prop( $key, "action2") || '';
|
||||
|
||||
# Process basis fields
|
||||
foreach my $b (\$basis, \$basis2) {
|
||||
$$b = $c->process_basis($$b);
|
||||
}
|
||||
|
||||
# Handle spaces in deliver addresses
|
||||
unless (($zarafa1 eq 'enabled') || ($zarafa2 eq 'enabled')) {
|
||||
$_ =~ s/ /\\ /g for ($deliver, $deliver2);
|
||||
}
|
||||
|
||||
# Build delivery paths
|
||||
$deliver = $c->build_delivery_path($action, $deliver, $USERNAME);
|
||||
$deliver2 = $c->build_delivery_path($action2, $deliver2, $USERNAME) if $action2;
|
||||
|
||||
# Construct second test line
|
||||
my $secondtest = $secondtest_orig ? "* $basis2$criterion2\n" : '';
|
||||
|
||||
# Build rule string
|
||||
my $rule = "\n";
|
||||
if ($copy eq 'no') {
|
||||
$rule .= ":0\n* $basis$criterion\n$secondtest$deliver\n";
|
||||
}
|
||||
elsif ($copy eq 'yes' && $action2 eq 'inbox') {
|
||||
$rule .= ":0 c\n* $basis$criterion\n$secondtest$deliver\n";
|
||||
}
|
||||
else {
|
||||
$rule .= ":0\n* $basis$criterion\n$secondtest\{\n"
|
||||
. " :0 c\n $deliver\n\n :0\n $deliver2\n\}\n";
|
||||
}
|
||||
|
||||
return $rule;
|
||||
my ($pdb, $key,$PanelUser) = @_;
|
||||
return $c->extract_rule_from_file("~$PanelUser/.procmailrc",$key)
|
||||
}
|
||||
# No longer needed - rule extracted from .procmailrc file.
|
||||
#my $c = shift;
|
||||
## Logic taken from template expansion (dangerous!)
|
||||
#my ($pdb, $key) = @_;
|
||||
|
||||
#my $basis = $pdb->get_prop($key, "basis") || '';
|
||||
#my $criterion = $pdb->get_prop( $key, "criterion") || '';
|
||||
#my $basis2 = $pdb->get_prop( $key, "basis2") || '';
|
||||
#my $secondtest_orig = $pdb->get_prop( $key, "basis2") || '';
|
||||
#my $criterion2 = $pdb->get_prop( $key, "criterion2") || '';
|
||||
#my $deliver = $pdb->get_prop( $key, "deliver") || '';
|
||||
#my $deliver2 = $pdb->get_prop( $key, "deliver2") || '';
|
||||
#my $copy = $pdb->get_prop( $key, "copy") || '';
|
||||
#my $action = $pdb->get_prop( $key, "action") || '';
|
||||
#my $action2 = $pdb->get_prop( $key, "action2") || '';
|
||||
|
||||
## Process basis fields
|
||||
#foreach my $b (\$basis, \$basis2) {
|
||||
#$$b = $c->process_basis($$b);
|
||||
#}
|
||||
|
||||
## Handle spaces in deliver addresses
|
||||
#unless (($zarafa1 eq 'enabled') || ($zarafa2 eq 'enabled')) {
|
||||
#$_ =~ s/ /\\ /g for ($deliver, $deliver2);
|
||||
#}
|
||||
|
||||
## Build delivery paths
|
||||
#$deliver = $c->build_delivery_path($action, $deliver, $USERNAME);
|
||||
#$deliver2 = $c->build_delivery_path($action2, $deliver2, $USERNAME) if $action2;
|
||||
|
||||
## Construct second test line
|
||||
#my $secondtest = $secondtest_orig ? "* $basis2$criterion2\n" : '';
|
||||
|
||||
## Build rule string
|
||||
#my $rule = "\n";
|
||||
#if ($copy eq 'no') {
|
||||
#$rule .= ":0\n* $basis$criterion\n$secondtest$deliver\n";
|
||||
#}
|
||||
#elsif ($copy eq 'yes' && $action2 eq 'inbox') {
|
||||
#$rule .= ":0 c\n* $basis$criterion\n$secondtest$deliver\n";
|
||||
#}
|
||||
#else {
|
||||
#$rule .= ":0\n* $basis$criterion\n$secondtest\{\n"
|
||||
#. " :0 c\n $deliver\n\n :0\n $deliver2\n\}\n";
|
||||
#}
|
||||
|
||||
#return $rule;
|
||||
#}
|
||||
|
||||
sub process_basis {
|
||||
my ($c,$basis) = @_;
|
||||
|
||||
@@ -10,10 +10,11 @@ package SrvMngr::Controller::Mailsorting;
|
||||
# navigation : 6000 1200
|
||||
# menucat : U
|
||||
#
|
||||
# name : mailsorting, method : get, url : /mailsorting, ctlact : Mailsorting#main
|
||||
# name : mailsortingu, method : post, url : /mailsortingu, ctlact : Mailsorting#do_update
|
||||
# name : mailsortingd, method : get, url : /mailsortingd, ctlact : Mailsorting#do_display
|
||||
# name : mailsorting, method : get, url : /mailsorting, ctlact : Mailsorting#main
|
||||
# name : mailsortingu, method : post, url : /mailsortingu, ctlact : Mailsorting#do_update
|
||||
# name : mailsortingd, method : get, url : /mailsortingd, ctlact : Mailsorting#do_display
|
||||
# name : mailsortinge, method : post, url : /mailsortinge, ctlact : Mailsorting#do_update
|
||||
# name : mailsortingx, method : get, url : /mailsortingx, ctlact : Mailsorting#do_extras
|
||||
#
|
||||
# routes : end
|
||||
#
|
||||
@@ -52,7 +53,7 @@ my $ndb;
|
||||
my $hdb;
|
||||
my $ddb;
|
||||
|
||||
my %ms_data;
|
||||
our %ms_data;
|
||||
|
||||
require '/usr/share/smanager/lib/SrvMngr/Controller/Mailsorting-Custom.pm'; #The code that is to be added by the developer
|
||||
|
||||
@@ -121,6 +122,12 @@ sub do_update {
|
||||
|
||||
my $c = shift;
|
||||
$c->app->log->info($c->log_req);
|
||||
$c->app->log->info("Updating:".$c->param('user'));
|
||||
|
||||
#$c->app->log->info($c->dumper($c->req->body_params->to_hash));
|
||||
#$c->app->log->info($c->dumper($c->req->query_params->to_hash));
|
||||
#$c->app->log->info($c->dumper($c->req->params->to_hash));
|
||||
|
||||
my $modul = '';
|
||||
|
||||
#The most common ones - you might want to comment out any not used.
|
||||
@@ -250,6 +257,11 @@ sub do_display {
|
||||
|
||||
my ($c,$trt) = @_;
|
||||
$c->app->log->info($c->log_req);
|
||||
|
||||
#$c->app->log->info($c->dumper($c->req->body_params->to_hash));
|
||||
#$c->app->log->info($c->dumper($c->req->query_params->to_hash));
|
||||
#$c->app->log->info($c->dumper($c->req->params->to_hash));
|
||||
|
||||
|
||||
#The most common ones - you might want to comment out any not used.
|
||||
$cdb = esmith::ConfigDB::UTF8->open() || die("Couldn't open config db");
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
'ms_deliver2' => 'deliver',
|
||||
'ms_basis2' => 'basis',
|
||||
'ms_Mail_sorting_rules' => 'Mail sorting rules',
|
||||
'ms_User_Name' => 'User Name',
|
||||
'ms_User_Name' => 'Full Name',
|
||||
'ms_folder' => 'Folder (if sorting)',
|
||||
'ms_action' => '2nd Action',
|
||||
'ms_copy' => 'Copy',
|
||||
@@ -51,4 +51,13 @@
|
||||
'ms_deliver2_email' => '2nd action Delivery email (if forwarding)',
|
||||
'ms_key' => 'Order of rule execution',
|
||||
'ms_new_record' => 'Select the part of the email to be tested. You can match against part of an email address, header, subject, or the email size. Size is in bytes. Delete, sort or Forward any matches. The second match is optional, but if used both rules must match',
|
||||
'ms_ERROR_FORWARD_NO_EMAIL' => 'No email address provided for forwarding',
|
||||
'ms_ERROR_FORWARD_NO_EMAIL' => 'No email address provided for forwarding',
|
||||
'ms_No_filtering' => 'No email filtering is configured',
|
||||
'ms_This_account_is_in_geek_mode' => 'This accounts email filtering is solely according to the contents of .procmailrc, .mailfilter or .sievefilter no automatic generation of those files will occur',
|
||||
'ms_Filtering_is_being_done_by' => 'Email filtering is using: ',
|
||||
'ms_Number_of_Emails' => 'Number of Emails in INBOX: ',
|
||||
'ms_Re_Serialise' => 'Re Serialise the Rules',
|
||||
'ms_Apply_Sieve' => 'Apply rules to INBOX',
|
||||
'ms_Result_of_command' => 'Number emails affected: ',
|
||||
'ms_Filter_contents' => 'Corresponding filter contents',
|
||||
'ms_Generate_Filter' => 'Generate filter rule'
|
||||
@@ -32,6 +32,16 @@ Generated by: SM2Gen version:0.9(20Jan2025) Chameleon version:4.5.4 On Python:3.
|
||||
.inline-buttons .link:active {
|
||||
background-color: #c0c0c0; /* Even darker shade on click */
|
||||
}
|
||||
|
||||
textarea {
|
||||
field-sizing: content;
|
||||
min-width: 400px;
|
||||
font-family: Verdana, Tahoma, sans-serif;
|
||||
font-size: 11.5px;
|
||||
font-weight: normal;
|
||||
color: black;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.Mailsorting-panel {}
|
||||
|
||||
@@ -3,3 +3,45 @@
|
||||
//
|
||||
$(document).ready(function() {
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// When the select changes, copy selected text (full name) to username field
|
||||
//document.getElementById('account_select').addEventListener('change', function() {
|
||||
//var select = this;
|
||||
//var selectedOption = select.options[select.selectedIndex];
|
||||
//document.getElementById('username_text').value = selectedOption.text;
|
||||
//});
|
||||
|
||||
// On button click, reload page with ?user=<accountname>
|
||||
const applyBtn = document.getElementById('applyBtn');
|
||||
if (applyBtn) {
|
||||
applyBtn.addEventListener('click', function() {
|
||||
const select = document.getElementById('account_select');
|
||||
const account = select ? select.value : '';
|
||||
const url = new URL('smanager/mailsorting', window.location.origin);
|
||||
if (account) {
|
||||
url.searchParams.set('account', account);
|
||||
}
|
||||
window.location.href = url.toString();
|
||||
});
|
||||
}
|
||||
|
||||
const backBtn = document.getElementById('backBtn');
|
||||
if (backBtn) {
|
||||
backBtn.addEventListener('click', function() {
|
||||
const url = new URL('smanager/mailsorting', window.location.origin);
|
||||
window.location.href = url.toString();
|
||||
});
|
||||
}
|
||||
|
||||
const generateBtn = document.getElementById('generateBtn');
|
||||
if (generateBtn) {
|
||||
generateBtn.addEventListener('click', function() {
|
||||
const url = new URL('smanager/mailsortingx?GenerateRule', window.location.origin);
|
||||
//alert(url.toString());
|
||||
window.location.href = url.toString();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
<br />
|
||||
%}
|
||||
|
||||
|
||||
%#Routing to partials according to trt parameter.
|
||||
%#This ought to be cascading if/then/elsif, but is easier to just stack the if/then's rather like a case statement'
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
%# Generated by SM2Gen version:0.9(20Jan2025) Chameleon version:4.5.4 On Python:3.12.3 at 2025-05-04 12:59:05
|
||||
%#
|
||||
<div id="Mailsorting-REMOVE" class="partial Mailsorting-REMOVE">
|
||||
<script>
|
||||
window.onload = function() {
|
||||
SelectInput();
|
||||
};
|
||||
</script>
|
||||
% if (config->{debug} == 1) {
|
||||
<pre>
|
||||
@@ -18,6 +14,7 @@
|
||||
%= hidden_field 'trt' => $ms_data->{trt}
|
||||
%# die("here");
|
||||
%= hidden_field 'Selected' => $c->param('Selected')
|
||||
%= hidden_field ''account'' => $c->param('account')
|
||||
|
||||
%# Inputs etc in here.
|
||||
|
||||
@@ -29,7 +26,14 @@
|
||||
%=l('ms_Rule_contents')
|
||||
</span><span class=data>
|
||||
% param 'RemoveRule' => $ms_data->{RemoveRule} unless param 'RemoveRule';
|
||||
%= text_area 'RemoveRule', cols=>60, rows=>20, Readonly=>'true'
|
||||
%= text_area 'RemoveRule', cols=>60, rows=>12, Readonly=>'true'
|
||||
</span><br>
|
||||
|
||||
<span class=label>
|
||||
%=l('ms_Filter_contents')
|
||||
</span><span class=data>
|
||||
% param 'FilterRule' => $ms_data->{FilterRule} unless param 'FilterRule';
|
||||
%= text_area 'FilterRule', cols=>60, rows=>12, Readonly=>'true'
|
||||
</span><br>
|
||||
|
||||
<span class='data'>
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
%# Generated by SM2Gen version:0.9(20Jan2025) Chameleon version:4.5.4 On Python:3.12.3 at 2025-05-04 12:59:05
|
||||
%#
|
||||
<div id="Mailsorting-RULES" class="partial Mailsorting-RULES">
|
||||
<script>
|
||||
window.onload = function() {
|
||||
SelectInput();
|
||||
};
|
||||
</script>
|
||||
% if (config->{debug} == 1) {
|
||||
<pre>
|
||||
%= dumper $ms_data
|
||||
@@ -17,6 +12,8 @@
|
||||
% param 'trt' => $ms_data->{trt} unless param 'trt';
|
||||
%= hidden_field 'trt' => $ms_data->{trt}
|
||||
%= hidden_field 'oldkey' => $ms_data->{oldkey}
|
||||
%= hidden_field 'account' => $c->param('account')
|
||||
|
||||
%# Inputs etc in here.
|
||||
|
||||
<h1 class='head'><%=l('ms_Mail_sorting_rules')%></h1>
|
||||
@@ -113,10 +110,29 @@
|
||||
% param 'key' => $ms_data->{key} unless param 'key';
|
||||
%= text_field 'key', size => '50', class => 'textinput key' , pattern=>'.*' , placeholder=>'key', title =>'Pattern regex mismatch', id => 'key_text'
|
||||
<br></span></p>
|
||||
|
||||
|
||||
<br /><br />
|
||||
|
||||
<!--
|
||||
<p><span class=label>
|
||||
%=l('')
|
||||
</span><span class=data>
|
||||
<button type="button" id="generateBtn"><%=l('ms_Generate_Filter')%></button>
|
||||
<br></span></p>
|
||||
-->
|
||||
|
||||
<p><span class=label>
|
||||
%=l('ms_Filter_contents')
|
||||
</span><span class=data>
|
||||
% param 'FilterRule' => $ms_data->{FilterRule} unless param 'FilterRule';
|
||||
%= text_area 'FilterRule', cols=>60, rows=>12, Readonly=>'true'
|
||||
</span></p>
|
||||
|
||||
<span class='data'>
|
||||
%= submit_button l('ms_Save'), class => 'action subm12'
|
||||
</span>
|
||||
|
||||
|
||||
|
||||
%# Probably finally by a submit.
|
||||
%end
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
%# Generated by SM2Gen version:0.9(20Jan2025) Chameleon version:4.5.4 On Python:3.12.3 at 2025-05-04 12:59:05
|
||||
%#
|
||||
<div id="Mailsorting-TABLE" class="partial Mailsorting-TABLE">
|
||||
<script>
|
||||
window.onload = function() {
|
||||
SelectInput();
|
||||
};
|
||||
</script>
|
||||
% if (config->{debug} == 1) {
|
||||
<pre>
|
||||
%= dumper $ms_data
|
||||
@@ -20,6 +15,7 @@
|
||||
%= form_for "mailsortingu" => (method => 'POST') => begin
|
||||
% param 'trt' => $ms_data->{trt} unless param 'trt';
|
||||
%= hidden_field 'trt' => $ms_data->{trt}
|
||||
%= hidden_field 'account' => $c->{account}
|
||||
%# Inputs etc in here.
|
||||
|
||||
<h1 class='head'><%=l('ms_Mail_sorting_rules')%></h1>
|
||||
@@ -27,28 +23,85 @@
|
||||
<p class='paragraph para1'>
|
||||
%=l('ms_Rules_are_executed_as_email')
|
||||
</p>
|
||||
|
||||
% if (! $c->is_admin){
|
||||
% # Single user mode
|
||||
<p><span class=label>
|
||||
%=l('ms_Account')
|
||||
</span><span class=data>
|
||||
% param 'account' => $ms_data->{account} unless param 'account';
|
||||
%= text_field 'account', size => '50', class => 'textinput account' , readonly => 'readonly', pattern=>'.*' , placeholder=>'account', title =>'Pattern regex mismatch', id => 'account_text'
|
||||
<br></span></p>
|
||||
|
||||
<p>
|
||||
<span class="label">
|
||||
%= l('ms_User_Name')
|
||||
</span>
|
||||
<span class="data">
|
||||
% param 'username' => $ms_data->{username} unless param 'username';
|
||||
%= text_field 'username', size => '50', class => 'textinput username', readonly => 'readonly', pattern=>'.*', placeholder=>'username', title =>'Pattern regex mismatch', id => 'username_text'
|
||||
<br>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
% } else {
|
||||
% # Called by Admin
|
||||
<p>
|
||||
<span class="label">
|
||||
%= l('ms_Account')
|
||||
</span>
|
||||
<span class="data">
|
||||
% param 'account' => $ms_data->{account} unless param 'account';
|
||||
%= select_field 'account' => $ms_data->{users}, class => 'input', id => 'account_select'
|
||||
<button type="button" id="applyBtn">Switch User</button>
|
||||
<button type="button" id="backBtn">Back to admin</button>
|
||||
<br>
|
||||
</span>
|
||||
</p>
|
||||
% }
|
||||
|
||||
<p><span class=label>
|
||||
%=l('ms_Account')
|
||||
</span><span class=data>
|
||||
% param 'account' => $ms_data->{account} unless param 'account';
|
||||
%= text_field 'account', size => '50', class => 'textinput account' , pattern=>'.*' , placeholder=>'account', title =>'Pattern regex mismatch', id => 'account_text'
|
||||
<br></span></p>
|
||||
|
||||
<p><span class=label>
|
||||
%=l('ms_User_Name')
|
||||
</span><span class=data>
|
||||
% param 'username' => $ms_data->{username} unless param 'username';
|
||||
%= text_field 'username', size => '50', class => 'textinput username' , pattern=>'.*' , placeholder=>'username', title =>'Pattern regex mismatch', id => 'username_text'
|
||||
<br></span></p>
|
||||
|
||||
|
||||
% if ($ms_data->{Geekmode}){
|
||||
<p class='paragraph para1'>
|
||||
%= l('ms_This_account_is_in_geek_mode')
|
||||
</p>
|
||||
%}
|
||||
|
||||
% my $filterstr = $ms_data->{FilterType} eq 'procmail' ? 'Procmail' :
|
||||
% $ms_data->{FilterType} eq 'maildrop' ? 'Maildrop' :
|
||||
% $ms_data->{FilterType} eq 'sieve' ? 'Dovecot Sieve' :
|
||||
% l('ms_No_filtering');
|
||||
|
||||
<p class='paragraph para1'>
|
||||
%== $c->l('ms_Filtering_is_being_done_by')."<b>".$filterstr."</b>";
|
||||
</p>
|
||||
<p class='paragraph para1'>
|
||||
%== $c->l('ms_Number_of_Emails')."<b>".$ms_data->{EmailCount}."</b>";
|
||||
</p>
|
||||
|
||||
% if (defined $ms_data->{command_result}) {
|
||||
<p class='paragraph para1'>
|
||||
<%= $c->l('ms_Result_of_command') %><b><%= $ms_data->{command_result} %></b>
|
||||
</p>
|
||||
% }
|
||||
|
||||
<br />
|
||||
|
||||
<div class = 'inline-buttons'>
|
||||
<a href='mailsortingd?trt=RULES' class='link link1'>
|
||||
%= l('ms_Add_new_rule')
|
||||
</a>
|
||||
% if ($filterstr eq "Dovecot Sieve"){
|
||||
<a href='mailsortingx?ApplyRules' class='link link1'>
|
||||
%= l('ms_Apply_Sieve');
|
||||
</a>
|
||||
% }
|
||||
%# <a href='mailsortingx?ReSerialise' class='link link1'>
|
||||
%#= l('ms_Re_Serialise');
|
||||
%# </a>
|
||||
|
||||
</div>
|
||||
%#= link_to l('ms_Add_new_rule'), 'mailsortinge?trt=RULES' , class=>'link link1'
|
||||
|
||||
|
||||
<h2 class='subh'><%=l('ms_Current_rules')%></h2>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Summary: Lets users configure procmail or maildrop rules.
|
||||
%define name smeserver-mailsorting
|
||||
Name: %{name}
|
||||
%define version 11.0.0
|
||||
%define release 4
|
||||
%define release 5
|
||||
Version: %{version}
|
||||
Release: %{release}%{?dist}
|
||||
License: GPL
|
||||
@@ -32,6 +32,9 @@ SME Server enhancement to enable procmail or maildrop filtering for users.
|
||||
Optionally provides user panels where users can create mail rules for themselves
|
||||
|
||||
%changelog
|
||||
* Sat Oct 11 2025 Brian Read <brianr@koozali.org> 11.0.0-5.sme
|
||||
- Add Dovecot Sieve generation and application [SME: 13232]
|
||||
|
||||
* Mon Oct 06 2025 Brian Read <brianr@koozali.org> 11.0.0-4.sme
|
||||
- Add UTF8 and avoid potential DB caching problems [SME: 13209]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user