diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cbb3a13
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.rpm
+*.log
+*spec-20*
+*.tar.gz
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..67656b4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+# Makefile for source rpm: smeserver-learn
+# $Id: Makefile,v 1.1 2020/12/20 11:47:11 brianr Exp $
+NAME := smeserver-learn
+SPECFILE = $(firstword $(wildcard *.spec))
+
+define find-makefile-common
+for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done
+endef
+
+MAKEFILE_COMMON := $(shell $(find-makefile-common))
+
+ifeq ($(MAKEFILE_COMMON),)
+# attept a checkout
+define checkout-makefile-common
+test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2
+endef
+
+MAKEFILE_COMMON := $(shell $(checkout-makefile-common))
+endif
+
+include $(MAKEFILE_COMMON)
diff --git a/README.md b/README.md
index fae3789..2ec9bf8 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,16 @@
-# smeserver-learn
+# smeserver-learn
-SMEServer Koozali developed git repo for smeserver-learn smecontribs
\ No newline at end of file
+SMEServer Koozali developed git repo for smeserver-learn smecontribs
+
+## Wiki
+
https://wiki.koozali.org/Learn
+
https://wiki.koozali.org/Learn/fr
+
+## Bugzilla
+Show list of outstanding bugs: [here](https://bugs.koozali.org/buglist.cgi?component=smeserver-learn&product=SME%20Contribs&query_format=advanced&limit=0&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=CONFIRMED)
+
+## Description
+
+
*This description has been generated by an LLM AI system and cannot be relied on to be fully correct.*
+*Once it has been checked, then this comment will be deleted*
+
diff --git a/contriborbase b/contriborbase
new file mode 100644
index 0000000..9b7fd51
--- /dev/null
+++ b/contriborbase
@@ -0,0 +1 @@
+contribs10
diff --git a/createlinks b/createlinks
new file mode 100644
index 0000000..facd866
--- /dev/null
+++ b/createlinks
@@ -0,0 +1,14 @@
+#! /usr/bin/perl -w
+
+use esmith::Build::CreateLinks qw(:all);
+
+
+templates2events("/etc/cron.d/Learn", qw(email-update bootstrap-console-save ));
+safe_touch('root/etc/e-smith/db/configuration/defaults/Learn/Exclude');
+safe_touch('root/etc/e-smith/db/configuration/defaults/Learn/Include');
+
+my $event="smeserver-learn-update";
+templates2events("/etc/cron.d/Learn",$event);
+templates2events("/etc/mail/spamassassin/local.cf",$event);
+event_link("LearnSpam-init", $event, "04");
+event_link("LearnSpam-run", $event, "30");
diff --git a/root/etc/cron.d/Learn b/root/etc/cron.d/Learn
new file mode 100644
index 0000000..c1fa455
--- /dev/null
+++ b/root/etc/cron.d/Learn
@@ -0,0 +1 @@
+# Learn auto-exec disabled
diff --git a/root/etc/e-smith/db/configuration/defaults/Learn/Verbose b/root/etc/e-smith/db/configuration/defaults/Learn/Verbose
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/Learn/Verbose
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/db/configuration/defaults/Learn/cron b/root/etc/e-smith/db/configuration/defaults/Learn/cron
new file mode 100644
index 0000000..f1931f6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/Learn/cron
@@ -0,0 +1 @@
+daily
diff --git a/root/etc/e-smith/db/configuration/defaults/Learn/type b/root/etc/e-smith/db/configuration/defaults/Learn/type
new file mode 100644
index 0000000..24e1098
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/Learn/type
@@ -0,0 +1 @@
+service
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsHam/LearnNew b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/LearnNew
new file mode 100644
index 0000000..7a68b11
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/LearnNew
@@ -0,0 +1 @@
+disabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsHam/RemoveSPAMTag b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/RemoveSPAMTag
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/RemoveSPAMTag
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsHam/Uniq b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/Uniq
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/Uniq
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsHam/dir b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/dir
new file mode 100644
index 0000000..dba009a
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/dir
@@ -0,0 +1 @@
+LearnAsHam
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsHam/status b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/status
new file mode 100644
index 0000000..7a68b11
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/status
@@ -0,0 +1 @@
+disabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsHam/tag b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/tag
new file mode 100644
index 0000000..c9f2ad2
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/tag
@@ -0,0 +1 @@
+[HAM]
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsHam/type b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/type
new file mode 100644
index 0000000..24e1098
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsHam/type
@@ -0,0 +1 @@
+service
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/DelayToMove b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/DelayToMove
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/DelayToMove
@@ -0,0 +1 @@
+0
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/DeleteAfterLearn b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/DeleteAfterLearn
new file mode 100644
index 0000000..7a68b11
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/DeleteAfterLearn
@@ -0,0 +1 @@
+disabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/LearnNew b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/LearnNew
new file mode 100644
index 0000000..7a68b11
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/LearnNew
@@ -0,0 +1 @@
+disabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/SpamLinks b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/SpamLinks
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/SpamLinks
@@ -0,0 +1 @@
+
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/Uniq b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/Uniq
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/Uniq
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/dir b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/dir
new file mode 100644
index 0000000..0ac03ea
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/dir
@@ -0,0 +1 @@
+LearnAsSpam
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/status b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/status
new file mode 100644
index 0000000..7a68b11
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/status
@@ -0,0 +1 @@
+disabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/tag b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/tag
new file mode 100644
index 0000000..669d1b9
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/tag
@@ -0,0 +1 @@
+[SPAM]
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/type b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/type
new file mode 100644
index 0000000..24e1098
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnAsSpam/type
@@ -0,0 +1 @@
+service
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnInWL/LearnNew b/root/etc/e-smith/db/configuration/defaults/LearnInWL/LearnNew
new file mode 100644
index 0000000..7a68b11
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnInWL/LearnNew
@@ -0,0 +1 @@
+disabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnInWL/RemoveSPAMTag b/root/etc/e-smith/db/configuration/defaults/LearnInWL/RemoveSPAMTag
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnInWL/RemoveSPAMTag
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnInWL/Uniq b/root/etc/e-smith/db/configuration/defaults/LearnInWL/Uniq
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnInWL/Uniq
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnInWL/dir b/root/etc/e-smith/db/configuration/defaults/LearnInWL/dir
new file mode 100644
index 0000000..b87af59
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnInWL/dir
@@ -0,0 +1 @@
+LearnInWL
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnInWL/status b/root/etc/e-smith/db/configuration/defaults/LearnInWL/status
new file mode 100644
index 0000000..7a68b11
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnInWL/status
@@ -0,0 +1 @@
+disabled
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnInWL/tag b/root/etc/e-smith/db/configuration/defaults/LearnInWL/tag
new file mode 100644
index 0000000..68c9e7e
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnInWL/tag
@@ -0,0 +1 @@
+[WL]
diff --git a/root/etc/e-smith/db/configuration/defaults/LearnInWL/type b/root/etc/e-smith/db/configuration/defaults/LearnInWL/type
new file mode 100644
index 0000000..24e1098
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/defaults/LearnInWL/type
@@ -0,0 +1 @@
+service
diff --git a/root/etc/e-smith/db/configuration/force/spamassassin/UseBayes b/root/etc/e-smith/db/configuration/force/spamassassin/UseBayes
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/force/spamassassin/UseBayes
@@ -0,0 +1 @@
+1
diff --git a/root/etc/e-smith/db/configuration/force/spamassassin/status b/root/etc/e-smith/db/configuration/force/spamassassin/status
new file mode 100644
index 0000000..86981e6
--- /dev/null
+++ b/root/etc/e-smith/db/configuration/force/spamassassin/status
@@ -0,0 +1 @@
+enabled
diff --git a/root/etc/e-smith/events/actions/LearnSpam-init b/root/etc/e-smith/events/actions/LearnSpam-init
new file mode 100644
index 0000000..f3a812b
--- /dev/null
+++ b/root/etc/e-smith/events/actions/LearnSpam-init
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# default is 0, if kept to 0 we can not play there
+/sbin/e-smith/db configuration setprop spamassassin UseBayes 1
+
+# default is empty, we shall set it if we want to have a run
+/sbin/e-smith/config getprop spamassassin BayesAutoLearnThresholdSpam >/dev/null|| /sbin/e-smith/config setprop spamassassin BayesAutoLearnThresholdSpam 6.00
+/sbin/e-smith/config getprop spamassassin BayesAutoLearnThresholdNonspam >/dev/null|| /sbin/e-smith/config setprop spamassassin BayesAutoLearnThresholdNonspam 0.10
+
+# we really need spamassassin running
+/sbin/e-smith/config setprop spamassassin status enabled
+
+# default is 0 for no reject
+#/sbin/e-smith/config setprop spamassassin RejectLevel 12
+
+# default is 5
+#/sbin/e-smith/config setprop spamassassin TagLevel 4
+
+# default is medium
+#/sbin/e-smith/config setprop spamassassin Sensitivity custom
diff --git a/root/etc/e-smith/events/actions/LearnSpam-run b/root/etc/e-smith/events/actions/LearnSpam-run
new file mode 100644
index 0000000..a2cccda
--- /dev/null
+++ b/root/etc/e-smith/events/actions/LearnSpam-run
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+sa-learn --sync --dbpath /var/spool/spamd/.spamassassin -u spamd
+chown spamd.spamd /var/spool/spamd/.spamassassin/bayes_*
+chown spamd.spamd /var/spool/spamd/.spamassassin/bayes.mutex
+chmod 640 /var/spool/spamd/.spamassassin/bayes_*
diff --git a/root/etc/e-smith/templates.metadata/etc/cron.d/Learn b/root/etc/e-smith/templates.metadata/etc/cron.d/Learn
new file mode 100644
index 0000000..3709f0b
--- /dev/null
+++ b/root/etc/e-smith/templates.metadata/etc/cron.d/Learn
@@ -0,0 +1 @@
+PERMS=0644
diff --git a/root/etc/e-smith/templates/etc/cron.d/Learn/Learn b/root/etc/e-smith/templates/etc/cron.d/Learn/Learn
new file mode 100644
index 0000000..4f0c435
--- /dev/null
+++ b/root/etc/e-smith/templates/etc/cron.d/Learn/Learn
@@ -0,0 +1,17 @@
+{
+ my $freq = $Learn{cron} || 'none';
+ my $min = int(rand(60));
+ my $hour = int(rand(5));
+ my $learn = '/usr/bin/Learn.pl';
+ if ($freq eq 'hourly') {
+ return "$min * * * * root $learn\n";
+ } elsif ($freq eq 'daily') {
+ return "$min $hour * * * root $learn\n";
+ } elsif ($freq eq 'weekly') {
+ return "$min $hour * * 0 root $learn\n";
+ } elsif ($freq eq 'monthly') {
+ return "$min $hour 15 * * root $learn\n";
+ } else {
+ return "# Learn auto-exec disabled\n";
+ }
+}
diff --git a/root/usr/bin/Learn.pl b/root/usr/bin/Learn.pl
new file mode 100755
index 0000000..8bae448
--- /dev/null
+++ b/root/usr/bin/Learn.pl
@@ -0,0 +1,291 @@
+#!/usr/bin/perl
+
+#############################################################################
+#
+# This script has been developed
+# by Jesper Knudsen at http://sme.swerts-knudsen.dk
+# And modified by Emmanuel Jooris at http://sme.firewall-services.com
+#
+# Revision History:
+#
+# January 18, 2006: Initial version
+# June 06, 2008: Modification for add LearnAsHam and LearnInWL
+# March 06, 2016: adding tmp and new subdir, tag filtering, improvements... JP Pialasse
+#############################################################################
+
+use Sys::Hostname;
+use Email::Simple;
+use esmith::AccountsDB;
+use esmith::ConfigDB;
+use Digest::MD5 qw(md5 md5_hex md5_base64);
+use File::Find;
+use File::Copy;
+use File::Copy::Recursive;
+use File::Basename;
+use File::Path;
+use Encode qw/encode decode/;
+use utf8;
+
+my $hostname = hostname();
+
+#Opening databases
+my $adb = esmith::AccountsDB->open_ro()
+ or die "Could not open AccountsDB ( reason : ".esmith::DB->error." )\n";
+my $sadb = esmith::ConfigDB->open_ro()
+ or die "Could not open ConfigurationDB ( reason : ".esmith::DB->error." )\n";
+
+# some variables of interest
+my $subjectTAG= $sadb->get_prop("spamassassin", "Subject");
+my $tag= $sadb->get_prop("LearnAsSpam", "tag");
+my $MessageRetentionTime = $sadb->get_prop('spamassassin', '$MessageRetentionTime')|| "15";
+my $DelayToMove = $sadb->get_prop("LearnAsSpam", "DelayToMove") || $MessageRetentionTime ; # delay in day before moving files
+$DelayToMove= $DelayToMove<$MessageRetentionTime ? $DelayToMove : 0; # set 0 to disable
+my $agesecs=60*60*24*$DelayToMove; #converts $agedays to seconds
+my $daysago=time-$agesecs; #the time stamp of $agedays ago in seconds
+my $daysago2=localtime($daysago); #the time stamp of $agedays ago in words - mainly for printing
+my $SpamLinks = $sadb->get_prop("LearnAsSpam", "SpamLinks") || "";
+my @files;
+
+#getting user list
+my @users = $adb->users;
+#adding admin
+my @admin = $adb->get('admin');
+push @users ,@admin;
+
+# getting WL before running
+open(SADB, "/home/e-smith/db/spamassassin");
+binmode(SADB);
+my $md5 = Digest::MD5->new->addfile(SADB)->b64digest;
+close(SADB);
+
+#Verbose mode
+my $verbose= $sadb->get_prop("Learn", "Verbose") || "enabled";
+my $outputfile=($verbose eq "disabled")? "/dev/null": '/tmp/LearnLog.txt';
+my $LOG;
+open ( $LOG, '+>', $outputfile) unless ($verbose eq "enabled");
+# select new filehandle
+select $LOG unless ($verbose eq "enabled");
+my $currentuser;
+
+# Running for every user
+foreach my $user (@users) {
+ my $firstname = $user->prop('FirstName');
+ my $lastname = $user->prop('LastName');
+ my $key = $user->key;
+ # verification if user included or excluded
+ my @include= split(',',$sadb->get_prop("Learn", "Include")) if $sadb->get_prop("Learn", "Include");
+ my @exclude= split(',',$sadb->get_prop("Learn", "Exclude")) if $sadb->get_prop("Learn", "Exclude");
+ next if ( (@exclude) and @found = grep { $_ eq $key } @exclude );
+ next unless ( ! (@include) or @found = grep { $_ eq $key } @include );
+
+ $currentuser = sprintf("Checking for user (%s): %s %s\n", $key,$firstname, $lastname);
+ print $currentuser unless ($verbose eq "active");
+
+ my $MailDir = ($key eq "admin")? "/home/e-smith" . "/Maildir" : "/home/e-smith/files/users/" . $key . "/Maildir";
+
+ my @modes = ("LearnAsSpam","LearnAsHam","LearnInWL");
+ foreach my $mode (@modes) {
+ # verificating mode is enabled
+ if ($sadb->get_prop($mode, "status") ne "enabled") { next; }
+
+ # getting dir name to search according to actual mode
+ my $dirname = $sadb->get_prop($mode, "dir");
+ if ( !(defined($dirname)) ) {
+ Vact();print "Errors in DB, dir subkey not present for key $mode\n";
+ next;
+ }
+ # adding heading periode if missing as this is an IMAP folder
+ if ($sadb->get_prop($mode, "Uniq") eq "enabled") {
+ $dirname= (substr($dirname, 0, 1) eq '.')? $dirname: ".$dirname";
+ }
+
+ #searching dir : fix to avoid to get multiple dir as default
+ opendir(LOGDIR, $MailDir);
+ my @logdirs = ($sadb->get_prop($mode, "Uniq") eq "enabled")? sort grep { /^$dirname$/ } readdir(LOGDIR) :sort grep { /$dirname/ } readdir(LOGDIR);
+ closedir(LOGDIR);
+
+ ($login,$pass,$uid,$gid) = getpwnam($key) or die "$key not in passwd file";
+ # mk dir if not exist
+ if (! @logdirs and ($sadb->get_prop($mode, "Uniq") eq "enabled")) {
+ Vact();print "+->mkdir :". $MailDir . "/" . $dirname . "\n";
+ foreach $a ('','/cur','/tmp','/new'){
+ mkdir $MailDir . "/" . $dirname . "$a";
+ chown $uid,$gid,$MailDir . "/" . $dirname . "$a";
+ }
+ };
+
+ my $junkdir = $MailDir . "/.junkmail";
+ unless ( -d $junkdir ) {
+ Vact();print "+->mkdir :". $junkdir . "\n";
+ foreach $a ('','/cur','/tmp','/new'){
+ mkdir $junkdir . "/" . "$a";
+ chown $uid,$gid,$junkdir . "/" . "$a";
+ }
+
+ }
+
+ # create junkmail links if necessary
+ my $Spamlinks = $user->prop('SpamLinks') || "";
+ if ($mode eq "LearnAsSpam" and ($SpamLinks or $Spamlinks ) ) {
+ $Spamlinks= $SpamLinks ? $SpamLinks .",". $Spamlinks : $Spamlinks;
+ $Spamlinks =~ s/,$//;
+ foreach $a (split(",",$Spamlinks)){
+ $a=(substr($a, 0, 1) eq '.')? $a: ".$a";
+ my $Link= $MailDir . "/" . $a;
+ next if ( $Link eq ".." || $Link eq "." || $Link eq "" );
+ next if ( -l $Link);
+ if (-d $Link) {
+ Vact(); print "+->move previous dir $Link content to $junkdir\n";
+ File::Copy::Recursive::dirmove($Link, $junkdir) or die "Can't move folder in place of our wanted link from %Link to $junkdir: $!";
+ #side effect : ownership is lost
+ @files = ( glob( $junkdir . '/cur/*' ),glob( $junkdir . '/new/*' ), glob( $junkdir . '/tmp/*' ) );
+ chown $uid, $gid, @files;
+ };
+ if (! -e $Link) {
+ Vact(); print "+->create link $Link on $junkdir\n";
+ symlink($junkdir, $Link) or die "Can't create symlink from $Link to $junkdir: $!";
+ };
+ }
+ }
+
+ # moving junkmail content if we want to check it another time before deleting
+ # sometime files will get there whithout any spamassassin check ( MUA moving it here)
+ if ($mode eq "LearnAsSpam" and $sadb->get_prop($mode, "DeleteAfterLearn") eq "enabled" and $DelayToMove >0) {
+ my $SpamDir = $MailDir . "/" . $logdir . "/cur/";
+ return unless (-e $SpamDir and -d $SpamDir);
+ @files=();
+ find(\&wanted, $junkdir ."/cur" );
+ find(\&wanted, $junkdir ."/new" ) if ($sadb->get_prop($mode, "LearnNew") eq "enabled" or $sadb->get_prop($mode, "LearnNew") eq "junkmail");
+ sub wanted {
+ $filesecs = (stat("$File::Find::dir/$_"))[9]; #GETS THE 9TH ELEMENT OF file STAT - THE MODIFIED TIME
+ $filesecs2=localtime($filesecs);
+ if ($filesecs<$daysago && -f){ #-f=regular files
+ push (@files,"$File::Find::dir/$_");
+ push (@files,"$filesecs2");
+ }
+ }
+ my %filehash=@files; #puts the array into a hash for easy printing. Key=file, Value=date
+ undef @files;
+ foreach $filename (sort keys %filehash){
+ Vact(); print "--> moving $filename $filehash{$filename}\n";
+ move $filename,"$MailDir/$dirname/cur" ;
+ }
+ }
+
+
+ # loop through all matching directories
+ foreach my $logdir (@logdirs) {
+ #sa-learn here to avoid its loading for each files, when it can walk in directories on itself!
+ my $SpamDir = ($sadb->get_prop($mode, "LearnNew") eq "enabled")? $MailDir . "/" . $logdir : $MailDir . "/" . $logdir . "/cur/" ;
+ #taking action according to actual mode
+ my $counter = 0;
+ find( { wanted => sub { -f && $_ =~ m/($logdir\/cur|$logdir\/new|$logdir\/tmp)/ && $counter++;}, no_chdir => 1, follow_fast => 1 }, $SpamDir );
+ if ($mode eq "LearnAsSpam" and $counter>0) {
+ my $result = `/usr/bin/sa-learn --spam $SpamDir`;
+ chomp($result); Vact(); printf("+Learning Spam from %s: %s\n",$logdir,$result);
+ }
+ elsif ($mode eq "LearnAsHam" and $counter>0) {
+ my $result = `/usr/bin/sa-learn --ham $SpamDir`;
+ chomp($result); Vact(); printf("+Learning Ham from %s: %s\n",$logdir,$result);
+ }
+
+ @list= ($sadb->get_prop($mode, "LearnNew") eq "enabled") ? ('/cur/','/new/'):('/cur/');
+ foreach $a (@list){
+ my $SpamDir = $MailDir . "/" . $logdir . "$a";
+ # list and sort file in dir
+ opendir(SPAMDIR, $SpamDir);
+ my @spamfiles = sort grep { /$hostname/ } readdir(SPAMDIR);
+ closedir(SPAMDIR);
+
+ #
+ foreach my $spamfile (@spamfiles) {
+ my $filetolearn = $SpamDir . $spamfile;
+ my $filetolearnbash = $filetolearn;
+ $filetolearnbash =~ s/;/\\;/g;
+ $filetolearnbash =~ s/:/\\:/g;
+
+ #taking action according to actual mode
+ if ($mode eq "LearnInWL") {
+ Vact(); printf("+Learning in WhiteList: %s\n",$filetolearnbash);
+ `/usr/bin/LearnInWL.pl $filetolearnbash`;
+ }
+ # if we are in LearnAsSpam mode and DeleteAfterLearn is enabled delete message, else tagging a move message
+ if ($mode eq "LearnAsSpam" and $sadb->get_prop($mode, "DeleteAfterLearn") eq "enabled") {
+ `rm -f $filetolearn`;
+ }
+ else {
+ if (defined($sadb->get_prop($mode, "tag"))) {
+ # Opening, reading in one scalar and parsing mail
+ $/=undef;
+ open(MAILFILE,"<:encoding(UTF-8)",$filetolearn);
+ my $emailbrut = ;
+ close(MAILFILE);
+ $/="\n";
+ my $email = Email::Simple->new($emailbrut);
+
+ #changing subject (tagging), opening and writing new mail
+ my $Subject= $email->header("Subject");
+ if ( ($mode ne "LearnAsSpam") && ($sadb->get_prop($mode, "RemoveSPAMTag") eq "enabled") ) {
+ for my $test ( quotemeta $subjectTAG , quotemeta $tag) {
+ ($Subject= $Subject) =~ s/$test//;
+ }
+ # we also remove Spam tag or client will move the mail again in spam dir
+ $email->header_set("X-Spam-Flag",'NO');
+ $email->header_set("X-Spam-Level",'*');
+ $email->header_set("X-Spam-Status",'No, score=0.0 required=4.0 autolearn=disabled');
+ }
+ # encoding as MIME Q (not B) with UTF8 as default.
+ my $cleanTag= encode("MIME-Q",$sadb->get_prop($mode, "tag"));
+ $email->header_set("Subject",$cleanTag.$Subject);
+ open(MAILFILEWRITE,">","$filetolearn");
+ print(MAILFILEWRITE $email->as_string);
+ close(MAILFILEWRITE)
+ }
+ my $mvdir;
+ if ($mode eq "LearnAsSpam") {
+ $mvdir = $MailDir . "/.junkmail/cur/";
+ }
+ else {
+ $mvdir = $MailDir . "/cur/";
+ }
+ `mv $filetolearn $mvdir`;
+ }
+ }
+ }
+ }
+ }
+
+}
+
+open(SADB, "/home/e-smith/db/spamassassin");
+binmode(SADB);
+my $newmd5 = Digest::MD5->new->addfile(SADB)->b64digest;
+close(SADB);
+
+# fix for bad ownership of bayes files
+system('/bin/chown','spamd:spamd','/var/spool/spamd/.spamassassin/bayes_toks');
+
+if ($newmd5 ne $md5) {
+ `expand-template /etc/mail/spamassassin/local.cf`;
+ `/usr/bin/systemctl restart spamassassin.service`;
+ print "\n";
+}
+
+# restore STDOUT
+select STDOUT;
+# read log and print on STDOUT if mode active
+if ($verbose eq "active"){
+ seek $LOG, 0, 0;
+ while (<$LOG>) {
+ print $_;
+ }
+}
+close($LOG) unless ($verbose eq "enabled");
+unlink $outputfile unless ($verbose eq "disabled");
+
+# sub to print the user line if something happen on active verbose mode
+sub Vact() {
+return unless ($verbose eq "active");
+print $currentuser;
+$currentuser="";
+}
diff --git a/root/usr/bin/LearnInWL.pl b/root/usr/bin/LearnInWL.pl
new file mode 100755
index 0000000..7b88fa5
--- /dev/null
+++ b/root/usr/bin/LearnInWL.pl
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+
+#############################################################################
+#
+# This script has been developed
+# by Emmanuel Jooris at http://sme.firewall-services.com
+#
+# Revision History:
+#
+# June 06, 2008: : Initial dev version
+#############################################################################
+
+use Email::Simple;
+use esmith::ConfigDB;
+use warnings;
+
+$/=undef;
+open(MAIL,$ARGV[0]);
+$file = ;
+close(MAIL);
+$/="\n";
+my $email = Email::Simple->new($file);
+my $from_header = $email->header("From");
+my $sadb = esmith::ConfigDB->open("spamassassin")
+ or die "Could not open SpamAssasinDB ( reason : )".esmith::DB->error." )\n";
+
+if ($from_header =~ m/<.*?>/) {
+ $from_mail = $&;
+ $from_mail =~ s//;
+ $from_mail =~ s/>//;
+}
+elsif ($from_header =~ m/@/) {
+ $from_mail = $&;
+}
+$from_mail =~ tr/[A-Z]/[a-z]/;
+$sadb->set_prop("wbl.global",$from_mail,"White");
diff --git a/smeserver-learn.spec b/smeserver-learn.spec
new file mode 100644
index 0000000..45a5fe9
--- /dev/null
+++ b/smeserver-learn.spec
@@ -0,0 +1,131 @@
+# $Id: smeserver-learn.spec,v 1.4 2021/02/23 19:39:09 jpp Exp $
+# Authority: unnilennium
+# Name: Jean-Philippe Pialasse.
+%define name smeserver-learn
+%define version 1.0
+%define release 17
+
+Summary: SME Server Mails Learning script
+Name: %{name}
+Version: %{version}
+Release: %{release}%{?dist}
+License: GPL
+Group: Networking/Daemons
+Source: %{name}-%{version}.tar.xz
+
+Packager: JP Pialasse
+BuildRoot: /var/tmp/%{name}-%{version}-%{release}-buildroot
+BuildArchitectures: noarch
+Requires: perl-Email-Simple
+BuildRequires: e-smith-devtools >= 1.13.1-03
+
+%description
+Scripts which allows users to interact with spamassassin rules simply
+by droping mail in special folders of their mailbox (working only with imap)
+- Learn mail as spam
+- Learn mail as ham
+- Whitelist the sender so his mails won't be taged as spam again
+
+%changelog
+* Sat Sep 07 2024 cvs2git.sh aka Brian Read 1.0-17.sme
+- Roll up patches and move to git repo [SME: 12338]
+
+* Sat Sep 07 2024 BogusDateBot
+- Eliminated rpmbuild "bogus date" warnings due to inconsistent weekday,
+ by assuming the date is correct and changing the weekday.
+
+* Tue Feb 23 2021 Jean-Philipe Pialasse 1.0-16.sme
+- make use of systemd [SME: 11281]
+- create an update event to configure the contrib without reboot [SME: 11281]
+- untag ham to avoid client to move them back to spamdir [SME: 10732]
+- move existing spamdir before creating link to replace them [SME: 9524]
+
+* Thu Dec 31 2020 Brian Read 1.0-15.sme
+- Remove-deprecated-defined [SME: 11281]
+
+* Sun Dec 20 2020 Brian Read 1.0-14.sme
+- Initial Import in SME 10 [SME: 11281]
+
+* Fri Jul 29 2016 Jean-Philipe Pialasse 1.0-13.sme
+- fix permission problem on bayes_tok [SME: 9446]
+
+* Sat May 14 2016 Jean-Philipe Pialasse 1.0-12.sme
+- fix verbose disabled unlink /dev/null [SME: 9512]
+
+* Tue Mar 29 2016 Jean-Philipe Pialasse 1.0-11.sme
+- first import in SME9 [SME: 9414]
+
+* Wed Mar 16 2016 JP Pialasse 1.0-10.sme
+- fix encoding problem when removing or adding tags [SME: 9282]
+- fix moving file not working MOVE patch [SME: 9314]
+- NFR: setting for verbose (enabled,disabled, active) VERBOSE patch [SME: 9313]
+
+* Tue Mar 15 2016 JP Pialasse 1.0-8.sme
+- NFR: check admin Maildir [SME: 9279]
+- NFR: restrict utilization to some users [SME: 9280]
+
+* Wed Mar 09 2016 JP Pialasse 1.0-7.sme
+- fix find not restrictive enough when learning also new [SME: 6862]
+
+* Tue Mar 08 2016 JP Pialasse 1.0-6.sme
+- new version SME8 and SME9
+- fix wrong default db key DeleteAfterLearn [SME: 7083]
+- added inspection of temp and new subdir [SME: 6862]
+- moved default db keys as file outside of spec
+- new db properties: (LearnAsHam,LearnInWL)RemoveSPAMTag, (*)LeanNew,(*)Uniq,(LearnAsSpam)SpamLinks
+- new account db property for user: SpamLinks
+- import to buildsys [SME: 4423]
+- use sa-learn per dir and not per message [SME: 1701]
+- merge enhancements [SME: 4423] [SME: 1701]
+- NFR: create links
+- NFR: filter out SPAM tag from HAM and InWL mails
+- NFR: added auto create folders if multiple folder disabled
+- NFR: create db key for multiple folder to inspect
+- enhanced move to junkmail or inbox
+- moving job from crontab to /etc/cron.d/Learn
+- fixed typo in event email-update (r3)
+- fixed wrong file permission on /etc/cron.d/Lean [SME: 9283] (r4)
+- fixed messy output [SME: 9284] (r5)
+- fixed replaced cron file on update [SME: 9286](r6)
+
+* Wed Jun 25 2008 Emmmanuel JOORIS 0.0.1-3
+- add dependencies to spec file
+
+* Mon Jun 23 2008 Daniel B. 0.0.1-2
+- add spaces after the tags
+
+* Wed Jun 18 2008 Daniel B. 0.0.1-1
+- full path for Learn script in crontab templates
+- rename to smeserver-learn
+- Fix default tag for Ham mail to [HAM]
+- Lean.pl will be executed daily by default
+
+* Wed Jun 11 2008 Emmanuel JOORIS
+- 0.0.1-0
+- Original version
+
+%prep
+%setup
+
+%build
+perl createlinks
+
+%install
+rm -rf $RPM_BUILD_ROOT
+(cd root ; find . -depth -print | cpio -dump $RPM_BUILD_ROOT)
+rm -f %{name}-%{version}-filelist
+/sbin/e-smith/genfilelist $RPM_BUILD_ROOT |\
+sed -e "s@^$RPM_BUILD_ROOT@@g" \
+-e "s@^%attr(0644,root,root) /etc/cron.d/Learn@%attr(0644,root,root)%config(noreplace) /etc/cron.d/Learn@"\
+> %{name}-%{version}-filelist
+
+%clean
+#rm -rf $RPM_BUILD_ROOT
+
+%post
+
+%postun
+
+%files -f %{name}-%{version}-filelist
+%defattr(-,root,root)
+