]> git.proxmox.com Git - pmg-api.git/commitdiff
implement quarantine delete/deliver
authorDietmar Maurer <dietmar@proxmox.com>
Wed, 16 Aug 2017 10:11:26 +0000 (12:11 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Wed, 16 Aug 2017 11:10:08 +0000 (13:10 +0200)
moved code to new PMG::Quarantine class.

Makefile
PMG/API2/Quarantine.pm
PMG/DBTools.pm
PMG/Quarantine.pm [new file with mode: 0644]

index a2f01f0f0cec20c8702d98d74fa49b62f665b83d..2b3de672a0c25140edab839938489ac663e89bfd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -78,6 +78,7 @@ LIBSOURCES =                          \
        PMG/LDAPSet.pm                  \
        PMG/LDAPCache.pm                \
        PMG/DBTools.pm                  \
+       PMG/Quarantine.pm               \
        PMG/RuleDB/Group.pm             \
        PMG/RuleDB/Rule.pm              \
        PMG/RuleDB/Object.pm            \
index e7f159a463d53b73f2bcb6ae62a097c5bd98dd71..bf70836d7324145de8fbfa0de65fa5c5d978b543 100644 (file)
@@ -22,6 +22,7 @@ use PMG::AccessControl;
 use PMG::Config;
 use PMG::DBTools;
 use PMG::HTMLMail;
+use PMG::Quarantine;
 
 use base qw(PVE::RESTHandler);
 
@@ -117,7 +118,7 @@ my $parse_header_info = sub {
     $res->{sender} = $sender if $sender && ($sender ne $res->{from});
 
     $res->{envelope_sender} = $ref->{sender};
-    $res->{receiver} = $ref->{receiver};
+    $res->{receiver} = $ref->{receiver} // $ref->{pmail};
     $res->{id} = 'C' . $ref->{cid} . 'R' . $ref->{rid};
     $res->{time} = $ref->{time};
     $res->{bytes} = $ref->{bytes};
@@ -183,7 +184,7 @@ my $read_user_bw_list = sub {
 
     my $dbh = PMG::DBTools::open_ruledb();
 
-    my $list = PMG::DBTools::add_to_blackwhite($dbh, $pmail, $listname);
+    my $list = PMG::Quarantine::add_to_blackwhite($dbh, $pmail, $listname);
 
     my $res = [];
     foreach my $a (@$list) { push @$res, { address => $a }; }
@@ -635,13 +636,19 @@ __PACKAGE__->register_method ({
        my $username = $ref->{pmail};
 
        if ($action eq 'whitelist') {
-           PMG::DBTools::add_to_blackwhite($dbh, $username, 'WL', [ $sender ]);
+           PMG::Quarantine::add_to_blackwhite($dbh, $username, 'WL', [ $sender ]);
        } elsif ($action eq 'blacklist') {
-           PMG::DBTools::add_to_blackwhite($dbh, $username, 'BL', [ $sender ]);
+           PMG::Quarantine::add_to_blackwhite($dbh, $username, 'BL', [ $sender ]);
        } elsif ($action eq 'deliver') {
+           my $targets = [ $ref->{pmail} ];
+           PMG::Quarantine::deliver_quarantined_mail($dbh, $ref, $targets);
        } elsif ($action eq 'delete') {
+           PMG::Quarantine::delete_quarantined_mail($dbh, $ref);
+       } else {
+           die "internal error"; # should not be reached
        }
 
        return undef;
     }});
+
 1;
index e24623c9ad7823240a3c58cc0d3f86ad2f1a6ac5..7ea760b802e2b9914984f56277d02c7eacfb436c 100644 (file)
@@ -1156,75 +1156,4 @@ sub load_mail_data {
     return $res;
 }
 
-sub add_to_blackwhite {
-    my ($dbh, $username, $listname, $addrs, $delete) = @_;
-
-    my $name = $listname eq 'BL' ? 'BL' : 'WL';
-    my $oname = $listname eq 'BL' ? 'WL' : 'BL';
-    my $qu = $dbh->quote ($username);
-
-    my $sth = $dbh->prepare(
-       "SELECT * FROM UserPrefs WHERE pmail = $qu AND (Name = 'BL' OR Name = 'WL')");
-    $sth->execute();
-
-    my $list = { 'WL' => {}, 'BL' => {} };
-
-    while (my $ref = $sth->fetchrow_hashref()) {
-       my $data = $ref->{data};
-       $data =~ s/[,;]/ /g;
-       my @alist = split('\s+', $data);
-
-       my $tmp = {};
-       foreach my $a (@alist) {
-           if ($a =~ m/^[[:ascii:]]+$/) {
-               $tmp->{$a} = 1;
-           }
-       }
-
-       $list->{$ref->{name}} = $tmp;
-    }
-
-    $sth->finish;
-
-    if ($addrs) {
-
-       foreach my $v (@$addrs) {
-           die "email address '$v' is too long (> 512 characters)\n"
-               if length($v) > 512;
-
-           if ($delete) {
-               delete($list->{$name}->{$v});
-           } else {
-               if ($v =~ m/[[:^ascii:]]/) {
-                   die "email address '$v' contains invalid characters\n";
-               }
-               $list->{$name}->{$v} = 1;
-               delete ($list->{$oname}->{$v});
-           }
-       }
-
-       my $wlist = $dbh->quote(join (',', keys %{$list->{WL}}) || '');
-       my $blist = $dbh->quote(join (',', keys %{$list->{BL}}) || '');
-
-       if (!$delete) {
-           my $maxlen = 200000;
-           die "whitelist size exceeds limit (> $maxlen bytes)\n"
-               if length($wlist) > $maxlen;
-           die "blacklist size exceeds limit (> $maxlen bytes)\n"
-               if length($blist) > $maxlen;
-       }
-
-       $dbh->do(
-           "DELETE FROM UserPrefs WHERE pmail = $qu AND (Name = 'WL' OR Name = 'BL');" .
-           "INSERT INTO UserPrefs (PMail, Name, Data, MTime) " .
-           "VALUES ($qu, 'BL', $blist, EXTRACT (EPOCH FROM now()));" .
-           "INSERT INTO UserPrefs (PMail, Name, Data, MTime) " .
-           "VALUES ($qu, 'WL', $wlist, EXTRACT (EPOCH FROM now()));");
-    }
-
-    my $values =  [ keys %{$list->{$name}} ];
-
-    return $values;
-}
-
 1;
diff --git a/PMG/Quarantine.pm b/PMG/Quarantine.pm
new file mode 100644 (file)
index 0000000..c3426b9
--- /dev/null
@@ -0,0 +1,194 @@
+package PMG::Quarantine;
+
+use strict;
+use warnings;
+use Net::SMTP;
+
+use PVE::SafeSyslog;
+use PVE::Tools;
+
+use PMG::Utils;
+use PMG::RuleDB;
+use PMG::MailQueue;
+
+sub add_to_blackwhite {
+    my ($dbh, $username, $listname, $addrs, $delete) = @_;
+
+    my $name = $listname eq 'BL' ? 'BL' : 'WL';
+    my $oname = $listname eq 'BL' ? 'WL' : 'BL';
+    my $qu = $dbh->quote ($username);
+
+    my $sth = $dbh->prepare(
+       "SELECT * FROM UserPrefs WHERE pmail = $qu AND (Name = 'BL' OR Name = 'WL')");
+    $sth->execute();
+
+    my $list = { 'WL' => {}, 'BL' => {} };
+
+    while (my $ref = $sth->fetchrow_hashref()) {
+       my $data = $ref->{data};
+       $data =~ s/[,;]/ /g;
+       my @alist = split('\s+', $data);
+
+       my $tmp = {};
+       foreach my $a (@alist) {
+           if ($a =~ m/^[[:ascii:]]+$/) {
+               $tmp->{$a} = 1;
+           }
+       }
+
+       $list->{$ref->{name}} = $tmp;
+    }
+
+    $sth->finish;
+
+    if ($addrs) {
+
+       foreach my $v (@$addrs) {
+           die "email address '$v' is too long (> 512 characters)\n"
+               if length($v) > 512;
+
+           if ($delete) {
+               delete($list->{$name}->{$v});
+           } else {
+               if ($v =~ m/[[:^ascii:]]/) {
+                   die "email address '$v' contains invalid characters\n";
+               }
+               $list->{$name}->{$v} = 1;
+               delete ($list->{$oname}->{$v});
+           }
+       }
+
+       my $wlist = $dbh->quote(join (',', keys %{$list->{WL}}) || '');
+       my $blist = $dbh->quote(join (',', keys %{$list->{BL}}) || '');
+
+       if (!$delete) {
+           my $maxlen = 200000;
+           die "whitelist size exceeds limit (> $maxlen bytes)\n"
+               if length($wlist) > $maxlen;
+           die "blacklist size exceeds limit (> $maxlen bytes)\n"
+               if length($blist) > $maxlen;
+       }
+
+       $dbh->do(
+           "DELETE FROM UserPrefs WHERE pmail = $qu AND (Name = 'WL' OR Name = 'BL');" .
+           "INSERT INTO UserPrefs (PMail, Name, Data, MTime) " .
+           "VALUES ($qu, 'BL', $blist, EXTRACT (EPOCH FROM now()));" .
+           "INSERT INTO UserPrefs (PMail, Name, Data, MTime) " .
+           "VALUES ($qu, 'WL', $wlist, EXTRACT (EPOCH FROM now()));");
+    }
+
+    my $values =  [ keys %{$list->{$name}} ];
+
+    return $values;
+}
+
+sub deliver_quarantined_mail {
+    my ($dbh, $ref, $targets) = @_;
+
+    my $filename = $ref->{file};
+    my $spooldir = $PMG::MailQueue::spooldir;
+    my $path = "$spooldir/$filename";
+
+    my $id = 'C' . $ref->{cid} . 'R' . $ref->{rid};
+
+    my $sender = 'postmaster'; # notify postmaster if something fails
+
+    my $smtp;
+
+    eval {
+       my $smtp = Net::SMTP->new ('127.0.0.1', Port => 10025, Hello => 'quarantine') ||
+           die "unable to connect to localhost at port 10025\n";
+
+       my $resid;
+
+       if (!$smtp->mail($sender)) {
+           die sprintf("smtp from error - got: %s %s\n", $smtp->code, $smtp->message);
+       }
+
+       if (!$smtp->to (@$targets)) {
+           die sprintf("smtp to error - got: %s %s\n", $smtp->code, $smtp->message);
+       }
+
+       $smtp->data();
+
+       my $header = 1;
+
+       open(my $fh, '<', $path) || die "unable to open file '$path' - $!\n";
+
+       while (defined(my $line = <$fh>)) {
+           chomp $line;
+           if ($header && ($line =~ m/^\s*$/)) {
+               $header = 0;
+           }
+
+           # skip Delivered-To and Return-Path (avoid problem with postfix
+           # forwarding loop detection (man local))
+           next if ($header && (($line =~ m/^Delivered-To:/i) || ($line =~ m/^Return-Path:/i)));
+
+           # rfc821 requires this
+           $line =~ s/^\./\.\./mg;
+           $smtp->datasend("$line\n");
+       }
+       close($fh);
+
+       if ($smtp->dataend()) {
+           my (@msgs) = $smtp->message;
+           my ($last_msg) = $msgs[$#msgs];
+           ($resid) = $last_msg =~ m/Ok: queued as ([0-9A-Z]+)/;
+           if (!$resid) {
+               die sprintf("smtp error - got: %s %s\n", $smtp->code, $smtp->message);
+           }
+       } else {
+           die sprintf("sending data failed - got: %s %s\n", $smtp->code, $smtp->message);
+       }
+
+       my $sth = $dbh->prepare(
+           "UPDATE CMSReceivers SET Status='D', MTime = ? " .
+           "WHERE CMailStore_CID = ? AND CMailStore_RID = ? AND PMail = ?");
+       $sth->execute(time(), $ref->{cid}, $ref->{rid}, $ref->{pmail});
+       $sth->finish;
+    };
+    my $err = $@;
+
+    $smtp->quit if $smtp;
+
+    if ($err) {
+       my $msg = "deliver quarantined mail '$id' ($path) failed: $err";
+       syslog('err', $msg);
+       die "$msg\n";
+    }
+
+    syslog('info', "delivered quarantined mail '$id' ($path)");
+
+    return 1;
+}
+
+sub delete_quarantined_mail {
+    my ($dbh, $ref) = @_;
+
+    my $filename = $ref->{file};
+    my $spooldir = $PMG::MailQueue::spooldir;
+    my $path = "$spooldir/$filename";
+
+    my $id = 'C' . $ref->{cid} . 'R' . $ref->{rid};
+
+    eval {
+       my $sth = $dbh->prepare(
+           "UPDATE CMSReceivers SET Status='D', MTime = ? WHERE " .
+           "CMailStore_CID = ? AND CMailStore_RID = ? AND PMail = ?");
+       $sth->execute (time(), $ref->{cid}, $ref->{rid}, $ref->{pmail});
+       $sth->finish;
+    };
+    if (my $err = $@) {
+       my $msg = "delete quarantined mail '$id' ($path) failed: $err";
+       syslog ('err', $msg);
+       die "$msg\n";
+    }
+
+    syslog ('info', "marked quarantined mail '$id' as deleted ($path)");
+
+    return 1;
+}
+
+
+1;