[pmg-devel] [PATCH pmg-api 09/10] API2/Quarantine: add attachment quarantine api paths
Dominik Csapak
d.csapak at proxmox.com
Thu Sep 26 12:28:13 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
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
src/PMG/API2/Quarantine.pm | 246 ++++++++++++++++++++++++++++++++++---
1 file changed, 232 insertions(+), 14 deletions(-)
diff --git a/src/PMG/API2/Quarantine.pm b/src/PMG/API2/Quarantine.pm
index 9d85370..d84093f 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 File::Copy qw(move);
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,27 @@ __PACKAGE__->register_method ({
return $ref;
}});
+my $get_and_check_mail = sub {
+ my ($id, $authuser, $role) = @_;
+
+ my ($cid, $rid, $tid) = $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;
+ }
+
+ return $ref;
+};
+
__PACKAGE__->register_method ({
name => 'content',
path => 'content',
@@ -823,20 +910,7 @@ __PACKAGE__->register_method ({
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}, $authuser, $role);
my $res = $parse_header_info->($ref);
@@ -872,6 +946,150 @@ __PACKAGE__->register_method ({
$res->{content} = $content;
}
+ return $res;
+
+ }});
+
+my $get_attachments = sub {
+ my ($mailid, $dumpdir, $with_path) = @_;
+
+ my $rpcenv = PMG::RESTEnvironment->get();
+ my $authuser = $rpcenv->get_user();
+ my $role = $rpcenv->get_role();
+
+ my $ref = $get_and_check_mail->($mailid, $authuser, $role);
+
+ 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/tmp/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 $dumpdir ="/var/tmp/pmg-$param->{mailid}-$$/";
+ my $attachments = $get_attachments->($param->{mailid}, $dumpdir, 1);
+
+ my $res = $attachments->[$param->{attachmentid}];
+ if (!$res) {
+ raise_param_exc({ attachmentid => "Invalid Attachment ID for Mail."});
+ }
+
+ my $tmpfile = "/var/tmp/pmg-$param->{mailid}-$$-$param->{attachmentid}";
+ move($res->{path}, $tmpfile);
+ $res->{path} = $tmpfile;
+ $res->{delete} = 1;
+
+ rmtree $dumpdir;
return $res;
--
2.20.1
More information about the pmg-devel
mailing list