[pmg-devel] [PATCH pmg-api v2 09/10] API2/Quarantine: add attachment quarantine api paths
Dominik Csapak
d.csapak at proxmox.com
Mon Sep 30 14:55:33 CEST 2019
this introduces 3 new api paths in /quarantine/
* GET attachment
lists all mails in the attachment quarantine, similar to the virus one
* GET listattachments
lists all attachments for a specific mail in the quarantine
* GET download
serves the attachment of a mail as a download to the user
also refactor the mail info gathering
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
changes from v1:
* refactor get_and_check_mail better (give rpcenv instead of authuser/role),
and use it one more time than before
* change dumpdir to '/var/run/pmgproxy/' (tmpfs)
* adapt to send_file changes and open the file for download instead of
moving to another dir
src/PMG/API2/Quarantine.pm | 267 ++++++++++++++++++++++++++++++++-----
1 file changed, 237 insertions(+), 30 deletions(-)
diff --git a/src/PMG/API2/Quarantine.pm b/src/PMG/API2/Quarantine.pm
index ec93e80..aa33350 100644
--- a/src/PMG/API2/Quarantine.pm
+++ b/src/PMG/API2/Quarantine.pm
@@ -6,6 +6,8 @@ use Time::Local;
use Time::Zone;
use Data::Dumper;
use Encode;
+use File::Path;
+use IO::File;
use Mail::Header;
use Mail::SpamAssassin;
@@ -24,6 +26,8 @@ use PMG::Config;
use PMG::DBTools;
use PMG::HTMLMail;
use PMG::Quarantine;
+use PMG::MailQueue;
+use PMG::MIMEUtils;
use base qw(PVE::RESTHandler);
@@ -188,6 +192,9 @@ __PACKAGE__->register_method ({
{ name => 'virus' },
{ name => 'virusstatus' },
{ name => 'quarusers' },
+ { name => 'attachment' },
+ { name => 'listattachments' },
+ { name => 'download' },
];
return $result;
@@ -698,6 +705,65 @@ __PACKAGE__->register_method ({
return $quarantine_api->($param, 'V');
}});
+__PACKAGE__->register_method ({
+ name => 'attachment',
+ path => 'attachment',
+ method => 'GET',
+ permissions => { check => [ 'admin', 'qmanager', 'audit' ] },
+ description => "Get a list of quarantined attachment mails in the given timeframe (default the last 24 hours).",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ starttime => get_standard_option('pmg-starttime'),
+ endtime => get_standard_option('pmg-endtime'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => {
+ id => {
+ description => 'Unique ID',
+ type => 'string',
+ },
+ bytes => {
+ description => "Size of raw email.",
+ type => 'integer' ,
+ },
+ envelope_sender => {
+ description => "SMTP envelope sender.",
+ type => 'string',
+ },
+ from => {
+ description => "Header 'From' field.",
+ type => 'string',
+ },
+ sender => {
+ description => "Header 'Sender' field.",
+ type => 'string',
+ optional => 1,
+ },
+ receiver => {
+ description => "Receiver email address",
+ type => 'string',
+ },
+ subject => {
+ description => "Header 'Subject' field.",
+ type => 'string',
+ },
+ time => {
+ description => "Receive time stamp",
+ type => 'integer',
+ },
+ },
+ },
+ },
+ code => sub {
+ my ($param) = @_;
+ return $quarantine_api->($param, 'A');
+ }});
+
__PACKAGE__->register_method ({
name => 'virusstatus',
path => 'virusstatus',
@@ -736,6 +802,32 @@ __PACKAGE__->register_method ({
return $ref;
}});
+my $get_and_check_mail = sub {
+ my ($id, $rpcenv, $dbh) = @_;
+
+ my ($cid, $rid, $tid) = $id =~ m/^C(\d+)R(\d+)T(\d+)$/;
+ $cid = int($cid);
+ $rid = int($rid);
+ $tid = int($tid);
+
+ if (!$dbh) {
+ $dbh = PMG::DBTools::open_ruledb();
+ }
+
+ my $ref = PMG::DBTools::load_mail_data($dbh, $cid, $rid, $tid);
+
+ my $authuser = $rpcenv->get_user();
+ my $role = $rpcenv->get_role();
+
+ if ($role eq 'quser') {
+ my $quar_username = $ref->{pmail} . '@quarantine';
+ raise_perm_exc("mail does not belong to user '$authuser' ($ref->{pmail})")
+ if $authuser ne $quar_username;
+ }
+
+ return $ref;
+};
+
__PACKAGE__->register_method ({
name => 'content',
path => 'content',
@@ -817,26 +909,11 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $rpcenv = PMG::RESTEnvironment->get();
- my $authuser = $rpcenv->get_user();
- my $role = $rpcenv->get_role();
my $format = $rpcenv->get_format();
my $raw = $param->{raw} // 0;
- my ($cid, $rid, $tid) = $param->{id} =~ m/^C(\d+)R(\d+)T(\d+)$/;
- $cid = int($cid);
- $rid = int($rid);
- $tid = int($tid);
-
- my $dbh = PMG::DBTools::open_ruledb();
-
- my $ref = PMG::DBTools::load_mail_data($dbh, $cid, $rid, $tid);
-
- if ($role eq 'quser') {
- my $quar_username = $ref->{pmail} . '@quarantine';
- raise_perm_exc("mail does not belong to user '$authuser' ($ref->{pmail})")
- if $authuser ne $quar_username;
- }
+ my $ref = $get_and_check_mail->($param->{id}, $rpcenv);
my $res = $parse_header_info->($ref);
@@ -872,6 +949,149 @@ __PACKAGE__->register_method ({
$res->{content} = $content;
}
+ return $res;
+
+ }});
+
+my $get_attachments = sub {
+ my ($mailid, $dumpdir, $with_path) = @_;
+
+ my $rpcenv = PMG::RESTEnvironment->get();
+
+ my $ref = $get_and_check_mail->($mailid, $rpcenv);
+
+ my $filename = $ref->{file};
+ my $spooldir = $PMG::MailQueue::spooldir;
+
+ my $parser = PMG::MIMEUtils::new_mime_parser({
+ nested => 1,
+ decode_bodies => 0,
+ extract_uuencode => 0,
+ dumpdir => $dumpdir,
+ });
+
+ my $entity = $parser->parse_open("$spooldir/$filename");
+ PMG::MIMEUtils::fixup_multipart($entity);
+ PMG::MailQueue::decode_entities($parser, 'attachmentquarantine', $entity);
+
+ my $res = [];
+ my $id = 0;
+
+ PMG::MIMEUtils::traverse_mime_parts($entity, sub {
+ my ($part) = @_;
+ my $name = PMG::Utils::extract_filename($part->head) || "part-$id";
+ my $attachment_path = $part->{PMX_decoded_path};
+ return if !$attachment_path || ! -f $attachment_path;
+ my $size = -s $attachment_path // 0;
+ my $entry = {
+ id => $id,
+ name => $name,
+ size => $size,
+ 'content-type' => $part->head->mime_attr('content-type'),
+ };
+ $entry->{path} = $attachment_path if $with_path;
+ push @$res, $entry;
+ $id++;
+ });
+
+ return $res;
+};
+
+__PACKAGE__->register_method ({
+ name => 'listattachments',
+ path => 'listattachments',
+ method => 'GET',
+ permissions => { check => [ 'admin', 'qmanager', 'audit'] },
+ description => "Get Attachments for E-Mail in Quarantine.",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ id => {
+ description => 'Unique ID',
+ type => 'string',
+ pattern => 'C\d+R\d+T\d+',
+ maxLength => 60,
+ },
+ },
+ },
+ returns => {
+ type => "array",
+ items => {
+ type => "object",
+ properties => {
+ id => {
+ description => 'Attachment ID',
+ type => 'integer',
+ },
+ size => {
+ description => "Size of raw attachment in bytes.",
+ type => 'integer' ,
+ },
+ name => {
+ description => "Raw email header data.",
+ type => 'string',
+ },
+ 'content-type' => {
+ description => "Raw email header data.",
+ type => 'string',
+ },
+ },
+ },
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $dumpdir = "/var/run/pmgproxy/pmg-$param->{id}-$$";
+ my $res = $get_attachments->($param->{id}, $dumpdir);
+ rmtree $dumpdir;
+
+ return $res;
+
+ }});
+
+__PACKAGE__->register_method ({
+ name => 'download',
+ path => 'download',
+ method => 'GET',
+ permissions => { check => [ 'admin', 'qmanager', 'audit'] },
+ description => "Download Attachment for E-Mail in Quarantine.",
+ download => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ mailid => {
+ description => 'Unique ID',
+ type => 'string',
+ pattern => 'C\d+R\d+T\d+',
+ maxLength => 60,
+ },
+ attachmentid => {
+ description => "The Attachment ID for the mail.",
+ type => 'integer',
+ },
+ },
+ },
+ returns => {
+ type => "object",
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $mailid = $param->{mailid};
+ my $attachmentid = $param->{attachmentid};
+
+ my $dumpdir = "/var/run/pmgproxy/pmg-$mailid-$$/";
+ my $attachments = $get_attachments->($mailid, $dumpdir, 1);
+
+ my $res = $attachments->[$attachmentid];
+ if (!$res) {
+ raise_param_exc({ attachmentid => "Invalid Attachment ID for Mail."});
+ }
+
+ $res->{fh} = IO::File->new($res->{path}, '<') ||
+ die "unable to open file '$res->{path}' - $!\n";
+
+ rmtree $dumpdir;
return $res;
@@ -922,27 +1142,14 @@ __PACKAGE__->register_method ({
my ($param) = @_;
my $rpcenv = PMG::RESTEnvironment->get();
- my $authuser = $rpcenv->get_user();
- my $role = $rpcenv->get_role();
my $action = $param->{action};
my @idlist = split(';', $param->{id});
my $dbh = PMG::DBTools::open_ruledb();
for my $id (@idlist) {
- my ($cid, $rid, $tid) = $id =~ m/^C(\d+)R(\d+)T(\d+)$/;
- $cid = int($cid);
- $rid = int($rid);
- $tid = int($tid);
-
- my $ref = PMG::DBTools::load_mail_data($dbh, $cid, $rid, $tid);
-
- if ($role eq 'quser') {
- my $quar_username = $ref->{pmail} . '@quarantine';
- raise_perm_exc("mail does not belong to user '$authuser' ($ref->{pmail})")
- if $authuser ne $quar_username;
- }
+ my $ref = $get_and_check_mail->($id, $rpcenv, $dbh);
my $sender = $get_real_sender->($ref);
if ($action eq 'whitelist') {
--
2.20.1
More information about the pmg-devel
mailing list