From 0a5805931ef3a705425eb52cb20afd4953285c01 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Thu, 2 Feb 2017 11:03:42 +0100 Subject: [PATCH] add some basic ruledb object --- Makefile | 12 + PMG/CLI/pmgdb.pm | 10 +- PMG/DBTools.pm | 415 +++++++++++++++++++++++++++++++++++ PMG/RuleDB/Domain.pm | 75 +++++++ PMG/RuleDB/EMail.pm | 67 ++++++ PMG/RuleDB/Group.pm | 79 +++++++ PMG/RuleDB/Object.pm | 200 +++++++++++++++++ PMG/RuleDB/Quarantine.pm | 138 ++++++++++++ PMG/RuleDB/Receiver.pm | 18 ++ PMG/RuleDB/ReceiverDomain.pm | 18 ++ PMG/RuleDB/ReceiverRegex.pm | 18 ++ PMG/RuleDB/Rule.pm | 77 +++++++ PMG/RuleDB/TimeFrame.pm | 181 +++++++++++++++ PMG/RuleDB/WhoRegex.pm | 132 +++++++++++ 14 files changed, 1435 insertions(+), 5 deletions(-) create mode 100644 PMG/RuleDB/Domain.pm create mode 100644 PMG/RuleDB/EMail.pm create mode 100644 PMG/RuleDB/Group.pm create mode 100644 PMG/RuleDB/Object.pm create mode 100644 PMG/RuleDB/Quarantine.pm create mode 100644 PMG/RuleDB/Receiver.pm create mode 100644 PMG/RuleDB/ReceiverDomain.pm create mode 100644 PMG/RuleDB/ReceiverRegex.pm create mode 100644 PMG/RuleDB/Rule.pm create mode 100644 PMG/RuleDB/TimeFrame.pm create mode 100644 PMG/RuleDB/WhoRegex.pm diff --git a/Makefile b/Makefile index dff86c2..7102c17 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,18 @@ LIBSOURCES = \ PMG/Ticket.pm \ PMG/AccessControl.pm \ PMG/DBTools.pm \ + PMG/RuleDB/Group.pm \ + PMG/RuleDB/Rule.pm \ + PMG/RuleDB/Object.pm \ + PMG/RuleDB/Quarantine.pm \ + PMG/RuleDB/WhoRegex.pm \ + PMG/RuleDB/ReceiverRegex.pm \ + PMG/RuleDB/EMail.pm \ + PMG/RuleDB/Receiver.pm \ + PMG/RuleDB/Domain.pm \ + PMG/RuleDB/ReceiverDomain.pm \ + PMG/RuleDB/TimeFrame.pm \ + PMG/RuleDB.pm \ PMG/CLI/pmgdb.pm \ ${CLI_CLASSES} \ PMG/API2/Network.pm \ diff --git a/PMG/CLI/pmgdb.pm b/PMG/CLI/pmgdb.pm index d6e35d6..f7760f4 100644 --- a/PMG/CLI/pmgdb.pm +++ b/PMG/CLI/pmgdb.pm @@ -98,19 +98,19 @@ __PACKAGE__->register_method ({ print "Initialize rule database\n"; my $dbh = PMG::DBTools::create_ruledb ($dbname); - #$ruledb = Proxmox::RuleDB->new ($dbh); - #Proxmox::Utils::init_ruledb ($ruledb); + $ruledb = PMG::RuleDB->new($dbh); + PMG::DBTools::init_ruledb($ruledb); $dbh->disconnect(); } else { my $dbh = PMG::DBTools::open_ruledb("Proxmox_ruledb"); - #$ruledb = Proxmox::RuleDB->new ($dbh); + $ruledb = Proxmox::RuleDB->new($dbh); - #print "Analyzing/Upgrading existing Databases..."; + print "Analyzing/Upgrading existing Databases..."; #Proxmox::Utils::upgradedb ($ruledb); - #print "done\n"; + print "done\n"; # reset and update statistic databases if ($param->{statistics}) { diff --git a/PMG/DBTools.pm b/PMG/DBTools.pm index 5f2c30b..33846c4 100644 --- a/PMG/DBTools.pm +++ b/PMG/DBTools.pm @@ -9,6 +9,8 @@ use DBI; use PVE::Tools; +use PMG::RuleDB; + sub open_ruledb { my ($database, $host, $port) = @_; @@ -298,6 +300,33 @@ my $userprefs_ctablecmd = <<__EOD; CREATE INDEX UserPrefs_MTime_Index ON UserPrefs (MTime); __EOD + +sub cond_create_dbtable { + my ($dbh, $name, $ctablecmd) = @_; + + eval { + $dbh->begin_work; + + my $cmd = "SELECT tablename FROM pg_tables " . + "WHERE tablename = lower ('$name')"; + + my $sth = $dbh->prepare ($cmd); + + $sth->execute(); + + if (!(my $ref = $sth->fetchrow_hashref())) { + $dbh->do ($ctablecmd); + } + + $sth->finish(); + + $dbh->commit; + }; + if (my $err = $@) { + $dbh->rollback; + croak $err; + } +} sub create_ruledb { my ($dbname) = @_; @@ -384,4 +413,390 @@ EOD return $dbh; } +sub cond_create_action_quarantine { + my ($ruledb) = @_; + + my $dbh = $ruledb->{dbh}; + + eval { + my $sth = $dbh->prepare( + "SELECT * FROM Objectgroup, Object " . + "WHERE Object.ObjectType = ? AND Objectgroup.Class = ? " . + "AND Object.objectgroup_id = Objectgroup.id"); + + my $otype = PMG::RuleDB::Quarantine::otype(); + if ($sth->execute($otype, 'action') <= 0) { + my $obj = PMG::RuleDB::Quarantine->new (); + my $txt = decode_entities(PMG::RuleDB::Quarantine->otype_text); + my $quarantine = $ruledb->create_group_with_obj + ($obj, $txt, PMG::RuleDB::Quarantine->oinfo); + } + }; +} + +sub cond_create_std_actions { + my ($ruledb) = @_; + + cond_create_action_quarantine($ruledb); + + #cond_create_action_report_spam($ruledb); +} + + +sub upgradedb { + my ($ruledb) = @_; + + my $dbh = $ruledb->{dbh}; + + $dbh->do ($dbfunction_minint); + + $dbh->do ($dbfunction_maxint); + + $dbh->do ($dbfunction_merge_greylist); + + # make sure we do not use slow sequential scans when upgraing + # database (before analyze can gather statistics) + $dbh->do("set enable_seqscan = false"); + + cond_create_dbtable ($dbh, 'DailyStat', $daily_stat_ctablecmd); + cond_create_dbtable ($dbh, 'DomainStat', $domain_stat_ctablecmd); + cond_create_dbtable ($dbh, 'StatInfo', $statinfo_ctablecmd); + cond_create_dbtable ($dbh, 'CMailStore', $cmailstore_ctablecmd); + cond_create_dbtable ($dbh, 'UserPrefs', $userprefs_ctablecmd); + cond_create_dbtable ($dbh, 'CGreylist', $cgreylist_ctablecmd); + cond_create_dbtable ($dbh, 'CStatistic', $cstatistic_ctablecmd); + cond_create_dbtable ($dbh, 'ClusterInfo', $clusterinfo_ctablecmd); + cond_create_dbtable ($dbh, 'VirusInfo', $virusinfo_stat_ctablecmd); + + cond_create_std_actions ($ruledb); + + upgrade_mailstore_db ($dbh); + + upgrade_statistic_db ($dbh); + + upgrade_userprefs_db ($dbh); + + upgrade_greylist_db ($dbh); + + upgrade_dailystat_db ($dbh); + + upgrade_domainstat_db ($dbh); + + # update obsolete content type names + eval { + $dbh->do ("UPDATE Object " . + "SET value = 'content-type:application/java-vm' ". + "WHERE objecttype = 3003 " . + "AND value = 'content-type:application/x-java-vm';"); + }; + + eval { + $dbh->do ("ANALYZE"); + }; +} + +sub init_ruledb { + my ($ruledb, $reset, $testmode) = @_; + + my $dbh = $ruledb->{dbh}; + + if (!$reset) { + # Greylist Objectgroup + my $greylistgroup = PMG::RuleDB::Group->new + ("GreyExclusion", "-", "greylist"); + $ruledb->save_group ($greylistgroup); + + } else { + # we do not touch greylist objects + my $glids = "SELECT object.ID FROM Object, Objectgroup WHERE " . + "objectgroup_id = objectgroup.id and class = 'greylist'"; + + $dbh->do ("DELETE FROM Rule; " . + "DELETE FROM RuleGroup; " . + "DELETE FROM Attribut WHERE Object_ID NOT IN ($glids); " . + "DELETE FROM Object WHERE ID NOT IN ($glids); " . + "DELETE FROM Objectgroup WHERE class != 'greylist';"); + } + + # WHO Objects + + # Blacklist + my $obj = PMG::RuleDB::EMail->new ('nomail@fromthisdomain.com'); + my $blacklist = $ruledb->create_group_with_obj( + $obj, 'Blacklist', 'Global blacklist'); + + # Whitelist + $obj = PMG::RuleDB::EMail->new('mail@fromthisdomain.com'); + my $whitelist = $ruledb->create_group_with_obj( + $obj, 'Whitelist', 'Global whitelist'); + + # WHEN Objects + + # Working hours + $obj = Proxmox::RuleDB::TimeFrame->new(8*60, 16*60); + my $working_hours =$ruledb->create_group_with_obj($obj, 'Office Hours' , + 'Usual office hours'); + + # WHAT Objects + + die "fixme"; + + # Images + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('image/.*'); + my $img_content = $ruledb->create_group_with_obj ($obj, 'Images', + 'All kinds of graphic files'); + # Multimedia + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('audio/.*'); + my $mm_content = $ruledb->create_group_with_obj ($obj, 'Multimedia', + 'Audio and Video'); + + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('video/.*'); + $ruledb->group_add_object ($mm_content, $obj); + + # Office Files + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/vnd\.ms-excel'); + my $office_content = $ruledb->create_group_with_obj ($obj, 'Office Files', + 'Common Office Files'); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/vnd\.ms-powerpoint'); + $ruledb->group_add_object ($office_content, $obj); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/msword'); + $ruledb->group_add_object ($office_content, $obj); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/vnd\.openxmlformats-officedocument\..*'); + $ruledb->group_add_object ($office_content, $obj); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/vnd\.oasis\.opendocument\..*'); + $ruledb->group_add_object ($office_content, $obj); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/vnd\.stardivision\..*'); + $ruledb->group_add_object ($office_content, $obj); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/vnd\.sun\.xml\..*'); + $ruledb->group_add_object ($office_content, $obj); + + + # Dangerous Content + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/x-ms-dos-executable'); + my $exe_content = $ruledb->create_group_with_obj ($obj, 'Dangerous Content', + 'executable files and partial messages'); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/x-java'); + $ruledb->group_add_object ($exe_content, $obj); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/javascript'); + $ruledb->group_add_object ($exe_content, $obj); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('application/x-executable'); + $ruledb->group_add_object ($exe_content, $obj); + $obj = Proxmox::RuleDB::ContentTypeFilter->new ('message/partial'); + $ruledb->group_add_object ($exe_content, $obj); + $obj = Proxmox::RuleDB::MatchFilename->new ('.*\.(vbs|pif|lnk|shs|shb)'); + $ruledb->group_add_object ($exe_content, $obj); + $obj = Proxmox::RuleDB::MatchFilename->new ('.*\.{.+}'); + $ruledb->group_add_object ($exe_content, $obj); + + # Virus + $obj = Proxmox::RuleDB::Virus->new (); + my $virus = $ruledb->create_group_with_obj ($obj, 'Virus', + 'Matches virus infected mail'); + # WHAT Objects + + # Spam + $obj = Proxmox::RuleDB::Spam->new (3); + my $spam3 = $ruledb->create_group_with_obj ($obj, 'Spam (Level 3)', + 'Matches possible spam mail'); + $obj = Proxmox::RuleDB::Spam->new (5); + my $spam5 = $ruledb->create_group_with_obj ($obj, 'Spam (Level 5)', + 'Matches possible spam mail'); + $obj = Proxmox::RuleDB::Spam->new (10); + my $spam10 = $ruledb->create_group_with_obj ($obj, 'Spam (Level 10)', + 'Matches possible spam mail'); + + +# $obj = Proxmox::RuleDB::MatchField->new ('content-type', 'application/pdf'); +# $ct_filter = $ruledb->create_group_with_obj ($obj, 'Content Type Filter', +# 'Content Type Filter'); + + + # ACTIONS + + # Mark Spam + $obj = Proxmox::RuleDB::ModField->new ('X-SPAM-LEVEL', '__SPAM_INFO__'); + my $mod_spam_level = $ruledb->create_group_with_obj ($obj, 'Modify Spam Level', + 'Mark mail as spam by adding a header tag.'); + + # Mark Spam + $obj = Proxmox::RuleDB::ModField->new ('subject', 'SPAM: __SUBJECT__'); + my $mod_spam_subject = $ruledb->create_group_with_obj ($obj, 'Modify Spam Subject', + 'Mark mail as spam by modifying the subject.'); + # Remove matching attachments + $obj = Proxmox::RuleDB::Remove->new (0); + my $remove = $ruledb->create_group_with_obj ($obj, 'Remove attachments', + 'Remove matching attachments'); + # Remove all attachments + $obj = Proxmox::RuleDB::Remove->new (1); + my $remove_all = $ruledb->create_group_with_obj ($obj, + 'Remove all attachments', + 'Remove all attachments'); + + # Accept + $obj = Proxmox::RuleDB::Accept->new (); + my $accept = $ruledb->create_group_with_obj ($obj, 'Accept', 'Accept mail for Delivery'); + + # Block + $obj = Proxmox::RuleDB::Block->new (); + my $block = $ruledb->create_group_with_obj ($obj, 'Block', 'Block mail'); + + # Quarantine + $obj = Proxmox::RuleDB::Quarantine->new (); + my $quarantine = $ruledb->create_group_with_obj ($obj, 'Quarantine', 'Move mail to quarantine'); + + # Spam Counter + #$obj = Proxmox::RuleDB::Counter->new (0); + #my $count_spam = $ruledb->create_group_with_obj ($obj, 'Count Spam', + # 'Count spam mails'); + # Virus Counter + #$obj = Proxmox::RuleDB::Counter->new (0); + #my $count_virus = $ruledb->create_group_with_obj ($obj, 'Count Viruses', + # 'Count virus mails'); + # BCC dietmar + #$obj = Proxmox::RuleDB::BCC->new ('dietmar@maurer-it.com'); + #$bcc = $ruledb->create_group_with_obj ($obj, 'BCC dietmar', 'send bcc'); + + # Store in quarantine + #$obj = Proxmox::RuleDB::Store->new ('quarantine', 'O'); + #$storeq = $ruledb->create_group_with_obj ($obj, 'Quarantine', ' Store in quarantine'); + + # Notify Admin + $obj = Proxmox::RuleDB::Notify->new ('__ADMIN__'); + my $notify_admin = $ruledb->create_group_with_obj ($obj, 'Notify Admin', + 'Send notification'); + + # Notify Sender + $obj = Proxmox::RuleDB::Notify->new ('__SENDER__'); + my $notify_sender = $ruledb->create_group_with_obj ($obj, 'Notify Sender', + 'Send notification'); + + # Add Disclaimer + $obj = Proxmox::RuleDB::Disclaimer->new (); + my $add_discl = $ruledb->create_group_with_obj ($obj, 'Disclaimer', + 'Add Disclaimer'); + + # Attach original mail + #$obj = Proxmox::RuleDB::Attach->new (); + #my $attach_orig = $ruledb->create_group_with_obj ($obj, 'Attach Original Mail', + # 'Attach Original Mail'); + + ####################### RULES ################################## + + ## Block Dangerous Files + my $rule = Proxmox::RuleDB::Rule->new ('Block Dangerous Files', 93, 1, 0); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $exe_content); + $ruledb->rule_add_action ($rule, $remove); + + ## Block Viruses + $rule = Proxmox::RuleDB::Rule->new ('Block Viruses', 96, 1, 0); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $virus); + $ruledb->rule_add_action ($rule, $notify_admin); + + if ($testmode) { + $ruledb->rule_add_action ($rule, $block); + } else { + $ruledb->rule_add_action ($rule, $quarantine); + } + + ## Virus Alert + $rule = Proxmox::RuleDB::Rule->new ('Virus Alert', 96, 1, 1); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $virus); + $ruledb->rule_add_action ($rule, $notify_sender); + $ruledb->rule_add_action ($rule, $notify_admin); + $ruledb->rule_add_action ($rule, $block); + + ## Blacklist + $rule = Proxmox::RuleDB::Rule->new ('Blacklist', 98, 1, 0); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_from_group ($rule, $blacklist); + $ruledb->rule_add_action ($rule, $block); + + ## Modify header + if (!$testmode) { + $rule = Proxmox::RuleDB::Rule->new ('Modify Header', 90, 1, 0); + $ruledb->save_rule ($rule); + $ruledb->rule_add_action ($rule, $mod_spam_level); + } + + ## Whitelist + $rule = Proxmox::RuleDB::Rule->new ('Whitelist', 85, 1, 0); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_from_group ($rule, $whitelist); + $ruledb->rule_add_action ($rule, $accept); + + if ($testmode) { + $rule = Proxmox::RuleDB::Rule->new ('Mark Spam', 80, 1, 0); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $spam10); + $ruledb->rule_add_action ($rule, $mod_spam_level); + $ruledb->rule_add_action ($rule, $mod_spam_subject); + } else { + # Quarantine/Mark Spam (Level 3) + $rule = Proxmox::RuleDB::Rule->new ('Quarantine/Mark Spam (Level 3)', 80, 1, 0); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $spam3); + $ruledb->rule_add_action ($rule, $mod_spam_subject); + $ruledb->rule_add_action ($rule, $quarantine); + #$ruledb->rule_add_action ($rule, $count_spam); + } + + # Quarantine/Mark Spam (Level 5) + $rule = Proxmox::RuleDB::Rule->new ('Quarantine/Mark Spam (Level 5)', 79, 0, 0); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $spam5); + $ruledb->rule_add_action ($rule, $mod_spam_subject); + $ruledb->rule_add_action ($rule, $quarantine); + + ## Block Spam Level 10 + $rule = Proxmox::RuleDB::Rule->new ('Block Spam (Level 10)', 78, 0, 0); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $spam10); + $ruledb->rule_add_action ($rule, $block); + + ## Block Outgoing Spam + $rule = Proxmox::RuleDB::Rule->new ('Block outgoing Spam', 70, 0, 1); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $spam3); + $ruledb->rule_add_action ($rule, $notify_admin); + $ruledb->rule_add_action ($rule, $notify_sender); + $ruledb->rule_add_action ($rule, $block); + + ## Add disclaimer + $rule = Proxmox::RuleDB::Rule->new ('Add Disclaimer', 60, 0, 1); + $ruledb->save_rule ($rule); + $ruledb->rule_add_action ($rule, $add_discl); + + # Block Multimedia Files + $rule = Proxmox::RuleDB::Rule->new ('Block Multimedia Files', 87, 0, 2); + $ruledb->save_rule ($rule); + + $ruledb->rule_add_what_group ($rule, $mm_content); + $ruledb->rule_add_action ($rule, $remove); + + #$ruledb->rule_add_from_group ($rule, $anybody); + #$ruledb->rule_add_from_group ($rule, $trusted); + #$ruledb->rule_add_to_group ($rule, $anybody); + #$ruledb->rule_add_what_group ($rule, $ct_filter); + #$ruledb->rule_add_action ($rule, $add_discl); + #$ruledb->rule_add_action ($rule, $remove); + #$ruledb->rule_add_action ($rule, $bcc); + #$ruledb->rule_add_action ($rule, $storeq); + #$ruledb->rule_add_action ($rule, $accept); + + cond_create_std_actions ($ruledb); +} + 1; diff --git a/PMG/RuleDB/Domain.pm b/PMG/RuleDB/Domain.pm new file mode 100644 index 0000000..53bb56a --- /dev/null +++ b/PMG/RuleDB/Domain.pm @@ -0,0 +1,75 @@ +package PMG::RuleDB::Domain; + +use strict; +use warnings; +use Carp; +use DBI; + +use PMG::RuleDB::WhoRegex; + +use base qw(PMG::RuleDB::WhoRegex); + +sub otype { + return 1002; +} + +sub otype_text { + return 'Domain'; +} + +sub oicon { + return 'domain.gif'; +} + +sub oconfigsite { + # fixme: ??? + return 'item_domain.epl'; +} + +sub new { + my ($type, $address, $ogroup) = @_; + + my $class = ref($type) || $type; + + $address //= 'domain.tld'; + + my $self = $class->SUPER::new($address, $ogroup); + + return $self; +} + +sub who_match { + my ($self, $addr) = @_; + + $addr =~ m/^.+@(.+)$/; + + return (lc ($1) eq lc ($self->address)); +} + +sub short_desc { + my $self = shift; + + my $desc = $self->{address}; + + return $desc; +} + + + +1; +__END__ + +=head1 PMG::RuleDB::Domain + +A WHO object to check email domains. + +=head2 Attribues + +=head3 address + +An Email domain. We use case insensitive compares. + +=head2 Examples + + $obj = PMG::RuleDB::Domain->new ('yourdomain.com'); + diff --git a/PMG/RuleDB/EMail.pm b/PMG/RuleDB/EMail.pm new file mode 100644 index 0000000..d10800a --- /dev/null +++ b/PMG/RuleDB/EMail.pm @@ -0,0 +1,67 @@ +package PMG::RuleDB::EMail; + +use strict; +use warnings; +use Carp; +use DBI; + +use PMG::RuleDB::WhoRegex; + +use base qw(PMG::RuleDB::WhoRegex); + +sub otype { + return 1001; +} + +sub otype_text { + return 'Mail address'; +} + +sub oicon { + return 'mail.gif'; +} + +sub new { + my ($type, $address, $ogroup) = @_; + my $class = ref($type) || $type; + + $address //= 'unknown@domain.tld'; + + my $self = $class->SUPER::new($address, $ogroup); + + return $self; +} + +sub who_match { + my ($self, $addr) = @_; + + return (lc ($addr) eq lc ($self->address)); +} + + +sub short_desc { + my $self = shift; + + my $desc = $self->{address}; + + return $desc; +} + +1; + +__END__ + +=head1 PMG::RuleDB::EMail + +A WHO object to check email addresses. + +=head2 Attribues + +=head3 address + +An Email address. We use case insensitive compares. + +=head2 Examples + + $obj = PMG::RuleDB::Email->new ('you@yourdomain.com'); + diff --git a/PMG/RuleDB/Group.pm b/PMG/RuleDB/Group.pm new file mode 100644 index 0000000..75ad3f5 --- /dev/null +++ b/PMG/RuleDB/Group.pm @@ -0,0 +1,79 @@ +package PMG::RuleDB::Group; + +use strict; +use warnings; +use Carp; +use DBI; + +use PMG::RuleDB; + +# FIXME: log failures ? + +sub new { + my ($type, $name, $info, $class) = @_; + + my $self = { + name => $name, + info => $info, + class => $class, + }; + + bless $self, $type; + + return $self; +} + +sub gtype { + my ($self, $str) = @_; + + if ($str eq "from") { return 0; } + if ($str eq "to") { return 1; } + if ($str eq "when") { return 2; } + if ($str eq "what") { return 3; } + if ($str eq "action") { return 4; } + if ($str eq "greylist") { return 5; } + + return -1; +} + +sub name { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{name} = $v; + } + + $self->{name}; +} + +sub info { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{info} = $v; + } + + $self->{info}; +} + +sub class { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{class} = $v; + } + + $self->{class}; +} + +sub id { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{id}=$v; + } + + $self->{id}; +} + +1; diff --git a/PMG/RuleDB/Object.pm b/PMG/RuleDB/Object.pm new file mode 100644 index 0000000..13728eb --- /dev/null +++ b/PMG/RuleDB/Object.pm @@ -0,0 +1,200 @@ +package PMG::RuleDB::Object; + +use strict; +use warnings; +use Carp; +use DBI; + +sub new { + my ($type, $otype, $ogroup) = @_; + + $otype //= 0; + + my $self = { + otype => $otype, + ogroup => $ogroup, + }; + + bless $self, $type; + + return $self; +} + +sub save { + croak "never call this method: ERROR"; +} + +sub load_attr { + croak "never call this method: ERROR"; +} + +sub load { + my ($ruledb, $objid) = @_; + + return $ruledb->load_object($objid); +} + +sub who_match { + croak "never call this method: ERROR"; +} + +sub when_match { + croak "never call this method: ERROR"; +} + +sub what_match { + croak "never call this method: ERROR"; +} + +sub execute { + croak "never call this method: ERROR"; +} + +sub final { + return undef; +} + +sub priority { + return 0; +} + +sub oisedit { + return 1; +} + +sub ogroup { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{ogroup} = $v; + } + + $self->{ogroup}; +} + +sub otype { + my $self = shift; + + $self->{otype}; +} + +sub otype_text { + my $self = shift; + + return "object"; +} + +sub oicon { + my $self = shift; + + return "def_icon.gif"; +} + +sub oconfigsite { + return undef; +} + +sub oinfo { + return 'object'; +} + +# some who object only matches 'receivers' +sub receivertest { + return 0; +} + +sub oclass { + croak "never call this method: ERROR"; +} + +sub id { + my $self = shift; + + $self->{id}; +} + +sub short_desc { + return "basic object"; +} + +1; + +__END__ + +=head1 PMG::RuleDB::Object + +The Proxmox Rules consists of Objects. There are several classes of Objects. Ech such class has a method to check if the object 'matches'. + +=head2 WHO Objects ($obj->oclass() eq 'who') + +Who sent the mail, who is the receiver? + +=head3 $obj->who_match ($addr) + +Returns true if $addr belongs to this objects. $addr is a text string representing the email address you want to check. + +=over + +=item * + +EMail: the only attribute is a regex to test email addresses + +=back + +=head2 WHEN Objects ($obj->oclass() eq 'when') + +Used to test for a certain daytime + +=head3 $obj->when_match ($time) + +Return true if $time matches the when object constraints. $time is an integer like returned by the time() system call (or generated with POSIX::mktime()). + +=over + +=item * + +TimeFrame: specifies a start and a end time + +=back + +=head2 WHAT Objects ($obj->oclass() eq 'what') + +mail content tests + +=head2 ACTION Objects ($obj->oclass() eq 'action') + +actions which can be executed + +=head3 $obj->execute ($mod_group, $queue, $ruledb, $mod_group, $targets, $msginfo, $vars, $marks) + +Execute the action code. $target is a array reference containing all +matching targets. + +=head2 Common Methods + +=head3 $obj->oclass() + +Returns 'who', 'when' 'what' or 'action'; + +=head3 $obj->short_desc() + +Returns a short text describing the contents of the object. This is used +for debugging purposes. + +=head3 $obj->otype + +Returns an integer representing the Type of the objects. This integer +is used in the database to uniquely identify object types. + +=head3 $obj->id + +Returns the unique database ID of the object. undef means the object is not jet stored in the databse. + +=head3 $obj->final() + +Return true if the object is an action and the action is final, i.e. the action stops further rule processing for all matching targets. + +=head3 $obj->priority() + +Return a priority between 0 and 100. This is currently used to sort action objects by priority. + diff --git a/PMG/RuleDB/Quarantine.pm b/PMG/RuleDB/Quarantine.pm new file mode 100644 index 0000000..1b7fe90 --- /dev/null +++ b/PMG/RuleDB/Quarantine.pm @@ -0,0 +1,138 @@ +package PMG::RuleDB::Quarantine; + +use strict; +use warnings; +use Carp; +use DBI; +use Digest::SHA; + +use PVE::SafeSyslog; +use PMG::RuleDB; +use PMG::RuleDB::Object; + +use base qw(PMG::RuleDB::Object); + +sub otype { + return 4006; +} + +sub oclass { + return 'action'; +} + +sub otype_text { + return 'Quarantine'; +} + +sub oinfo { + return 'Move to quarantine.'; +} + +sub oicon { + # fixme: + return 'accept.gif'; +} + +sub oisedit { + return 0; +} + +sub final { + return 1; +} + +sub priority { + return 90; +} + +sub new { + my ($type, $ogroup) = @_; + + my $class = ref($type) || $type; + + my $self = $class->SUPER::new(otype(), $ogroup); + + return $self; +} + +sub load_attr { + my ($type, $ruledb, $id, $ogroup, $value) = @_; + + my $class = ref($type) || $type; + + my $obj = $class->new($ogroup); + $obj->{id} = $id; + + $obj->{digest} = Digest::SHA::sha1_hex($id, $ogroup); + + return $obj; +} + +sub save { + my ($self, $ruledb) = @_; + + defined($self->{ogroup}) || return undef; + + if (defined ($self->{id})) { + # update + + # nothing to update + + } else { + # insert + my $sth = $ruledb->{dbh}->prepare ( + "INSERT INTO Object (Objectgroup_ID, ObjectType) VALUES (?, ?);"); + + $sth->execute($self->ogroup, $self->otype); + + $self->{id} = PMG::RuleDB::lastid($ruledb->{dbh}, 'object_id_seq'); + } + + return $self->{id}; +} + +sub execute { + my ($self, $queue, $ruledb, $mod_group, $targets, + $msginfo, $vars, $marks, $ldap) = @_; + + die "fixme"; + + my $subgroups = $mod_group->subgroups($targets, 1); + + foreach my $ta (@$subgroups) { + my ($tg, $entity) = (@$ta[0], @$ta[1]); + + Proxmox::Utils::remove_marks ($entity); + + if ($queue->{vinfo}) { + if (my $qid = $queue->quarantine_mail($ruledb, 'V', $entity, $tg, $msginfo, $vars, $ldap)) { + + foreach (@$tg) { + syslog ('info', "$queue->{logid}: moved mail for <%s> to virus quarantine - $qid", $_); + } + + $queue->set_status ($tg, 'delivered'); + } + + } else { + if (my $qid = $queue->quarantine_mail($ruledb, 'S', $entity, $tg, $msginfo, $vars, $ldap)) { + + foreach (@$tg) { + syslog ('info', "$queue->{logid}: moved mail for <%s> to spam quarantine - $qid", $_); + } + + $queue->set_status($tg, 'delivered'); + } + } + } + + # warn if no subgroups +} + +sub short_desc { + my $self = shift; + + return oinfo(); +} + +1; diff --git a/PMG/RuleDB/Receiver.pm b/PMG/RuleDB/Receiver.pm new file mode 100644 index 0000000..1044216 --- /dev/null +++ b/PMG/RuleDB/Receiver.pm @@ -0,0 +1,18 @@ +package Proxmox::RuleDB::Receiver; + +use strict; +use warnings; + +use PMG::RuleDB::EMail; + +use base qw(PMG::RuleDB::EMail); + +sub otype { + return 1007; +} + +sub receivertest { + return 1; +} + +1; diff --git a/PMG/RuleDB/ReceiverDomain.pm b/PMG/RuleDB/ReceiverDomain.pm new file mode 100644 index 0000000..f6663e9 --- /dev/null +++ b/PMG/RuleDB/ReceiverDomain.pm @@ -0,0 +1,18 @@ +package PMG::RuleDB::ReceiverDomain; + +use strict; +use warnings; + +use PMG::RuleDB::Domain; + +use base qw(PMG::RuleDB::Domain); + +sub otype { + return 1008; +} + +sub receivertest { + return 1; +} + +1; diff --git a/PMG/RuleDB/ReceiverRegex.pm b/PMG/RuleDB/ReceiverRegex.pm new file mode 100644 index 0000000..3ae5ced --- /dev/null +++ b/PMG/RuleDB/ReceiverRegex.pm @@ -0,0 +1,18 @@ +package PMG::RuleDB::ReceiverRegex; + +use strict; +use warnings; + +use PMG::RuleDB::WhoRegex; + +use base qw(PMG::RuleDB::WhoRegex); + +sub otype { + return 1009; +} + +sub receivertest { + return 1; +} + +1; diff --git a/PMG/RuleDB/Rule.pm b/PMG/RuleDB/Rule.pm new file mode 100644 index 0000000..8ce1d3d --- /dev/null +++ b/PMG/RuleDB/Rule.pm @@ -0,0 +1,77 @@ +package PMG::RuleDB::Rule; + +use strict; +use warnings; +use DBI; + +use PMG::RuleDB; + +# FIXME: log failures ? + +sub new { + my ($type, $name, $priority, $active, $direction) = @_; + + my $self = { + name => $name, + priority => $priority, + active => $active, + }; + + if (!defined($direction)) { + $self->{direction} = 2; + } else { + $self->{direction} = $direction; + } + + bless $self, $type; + + return $self; +} + +sub name { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{name} = $v; + } + + $self->{name}; +} + +sub priority { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{priority} = $v; + } + + $self->{priority}; +} + +sub direction { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{direction} = $v; + } + + $self->{direction}; +} + +sub active { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{active} = $v; + } + + $self->{active}; +} + +sub id { + my $self = shift; + + $self->{id}; +} + +1; diff --git a/PMG/RuleDB/TimeFrame.pm b/PMG/RuleDB/TimeFrame.pm new file mode 100644 index 0000000..175f64f --- /dev/null +++ b/PMG/RuleDB/TimeFrame.pm @@ -0,0 +1,181 @@ +package PMG::RuleDB::TimeFrame; + +use strict; +use warnings; +use Carp; +use DBI; +use Digest::SHA; + +use PMG::RuleDB::Object; + +use base qw(Proxmox::RuleDB::Object); + +sub otype { + return 2000; +} + +sub oclass { + return 'when'; +} + +sub otype_text { + return 'TimeFrame'; +} + +sub oicon { + return 'timeframe.gif'; +} + +sub oconfigsite { + # fixme: + return 'item_timeframe.epl'; +} + +sub oinfo { + # fixme: + return 'e.g. 12:00 - 13:00'; +} + +sub new { + my ($type, $start, $end, $ogroup) = @_; + + my $class = ref($type) || $type; + + my $self = $class->SUPER::new(otype(), $ogroup); + + $start //= "00:00"; + $end //= "24:00"; + + if ($start =~ m/:/) { + my @tmp = split(/:/, $start); + $start = $tmp[0]*60+$tmp[1]; + } + + if ($end =~ m/:/) { + my @tmp = split(/:/, $end); + $end = $tmp[0]*60+$tmp[1]; + } + + $self->{start} = $start; + $self->{end} = $end; + + return $self; +} + +sub load_attr { + my ($type, $ruledb, $id, $ogroup, $value) = @_; + + my $class = ref($type) || $type; + + defined($value) || return undef; + + my ($sh, $sm, $eh, $em) = $value =~ m/(\d+):(\d+)-(\d+):(\d+)/; + + my $start = $sh*60+$sm; + my $end = $eh*60+$em; + + my $obj = $class->new($start, $end, $ogroup); + $obj->{id} = $id; + + $obj->{digest} = Digest::SHA::sha1_hex ($id, $start, $end, $ogroup); + + return $obj; +} + +sub save { + my ($self, $ruledb) = @_; + + defined($self->{ogroup}) || return undef; + defined($self->{start}) || return undef; + defined($self->{end}) || return undef; + + my $v = sprintf ("%d:%d-%d:%d", int ($self->{start} / 60), int ($self->{start} % 60), + int ($self->{end} / 60), int ($self->{end} % 60)); + + if (defined ($self->{id})) { + # update + + $ruledb->{dbh}->do( + "UPDATE Object SET Value = ? WHERE ID = ?", undef, $v, $self->{id}); + + } else { + # insert + + my $sth = $ruledb->{dbh}->prepare( + "INSERT INTO Object " . + "(Objectgroup_ID, ObjectType, Value) " . + "VALUES (?, ?, ?);"); + + $sth->execute($self->ogroup, $self->otype, $v); + + $self->{id} = PMG::RuleDB::lastid($ruledb->{dbh}, 'object_id_seq'); + } + + return $self->{id}; +} + +sub when_match { + my ($self, $t) = @_; + + my ($sec,$min,$hour) = localtime($t); + + my $amin = $hour*60 + $min; + + return $amin >= $self->{start} && $amin <= $self->{end} +} + +sub start { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{start} = $v; + } + + $self->{start}; +} + +sub end { + my ($self, $v) = @_; + + if (defined ($v)) { + $self->{end} = $v; + } + + $self->{end}; +} + +sub short_desc { + my $self = shift; + + my $v = sprintf ("%d:%02d-%d:%02d", + int ($self->{start} / 60), + int ($self->{start} % 60 ), + int ($self->{end} / 60), + int ($self->{end} % 60)); + return "$v"; +} + + +1; + +__END__ + +=head1 PMG::RuleDB::TimeFrame + +A WHEN object to check for a specific daytime. + +=head2 Attribues + +=head3 start + +Start time im minutes since 00:00. + +=head3 end + +End time im minutes since 00:00. + +=head2 Examples + + $obj = PMG::RuleDB::TimeFrame->new(8*60+15, 16*60+30); + +Represent: 8:15 to 16:30 diff --git a/PMG/RuleDB/WhoRegex.pm b/PMG/RuleDB/WhoRegex.pm new file mode 100644 index 0000000..a1562a1 --- /dev/null +++ b/PMG/RuleDB/WhoRegex.pm @@ -0,0 +1,132 @@ +package PMG::RuleDB::WhoRegex; + +use strict; +use warnings; +use Carp; +use DBI; +use Digest::SHA; + +use PMG::RuleDB::Object; + +use base qw(PMG::RuleDB::Object); + +sub otype { + return 1000; +} + +sub oclass { + return 'who'; +} + +sub otype_text { + return 'Regular Expression'; +} + +sub oicon { + return 'regexp.gif'; +} + +sub new { + my ($type, $address, $ogroup) = @_; + + my $class = ref($type) || $type; + + my $self = $class->SUPER::new(otype(), $ogroup); + + $address //= '.*@domain\.tld'; + + $self->{address} = $address; + + return $self; +} + +sub load_attr { + my ($type, $ruledb, $id, $ogroup, $value) = @_; + + my $class = ref($type) || $type; + + defined($value) || croak "undefined value: ERROR"; + + my $obj = $class->new ($value, $ogroup); + $obj->{id} = $id; + + $obj->{digest} = Digest::SHA::sha1_hex($id, $value, $ogroup); + + return $obj; +} + +sub save { + my ($self, $ruledb) = @_; + + defined($self->{ogroup}) || croak "undefined ogroup: ERROR"; + defined($self->{address}) || croak "undefined address: ERROR"; + + my $adr = $self->{address}; + $adr =~ s/\\/\\\\/g; + + if (defined ($self->{id})) { + # update + + $ruledb->{dbh}->do ( + "UPDATE Object SET Value = ? WHERE ID = ?", + undef, $adr, $self->{id}); + + } else { + # insert + + my $sth = $ruledb->{dbh}->prepare ( + "INSERT INTO Object (Objectgroup_ID, ObjectType, Value) " . + "VALUES (?, ?, ?);"); + + $sth->execute($self->{ogroup}, $self->otype, $adr); + + $self->{id} = PMG::RuleDB::lastid($ruledb->{dbh}, 'object_id_seq'); + } + + return $self->{id}; +} + +sub who_match { + my ($self, $addr) = @_; + + my $t = $self->address; + + return $addr =~ m/^$t$/i; +} + +sub address { + my ($self, $addr) = @_; + + if (defined ($addr)) { + $self->{address} = $addr; + } + + $self->{address}; +} + +sub short_desc { + my $self = shift; + + my $desc = $self->{address}; + + return $desc; +} + +1; + +__END__ + +=head1 PMG::RuleDB::WhoRegex + +A WHO object to check email addresses with regular expresssions. + +=head2 Attribues + +=head3 address + +A Perl regular expression used to compare email addresses (ignore case). + +=head2 Examples + + $obj = PMG::RuleDB::WhoRegex->new ('.*@yourdomain.com'); + -- 2.39.2