use strict;
use warnings;
+
use Time::Local;
use Time::Zone;
use Data::Dumper;
use Encode;
use File::Path;
use IO::File;
+use MIME::Entity;
+use URI::Escape qw(uri_escape);
+use File::stat ();
use Mail::Header;
use Mail::SpamAssassin;
$res->{subject} = PMG::Utils::decode_rfc1522(PVE::Tools::trim($head->get('subject'))) // '';
- my @fromarray = split('\s*,\s*', $head->get('from') || $ref->{sender});
-
- $res->{from} = PMG::Utils::decode_rfc1522(PVE::Tools::trim ($fromarray[0])) // '';
+ $res->{from} = PMG::Utils::decode_rfc1522(PVE::Tools::trim($head->get('from') || $ref->{sender})) // '';
my $sender = PMG::Utils::decode_rfc1522(PVE::Tools::trim($head->get('sender')));
$res->{sender} = $sender if $sender && ($sender ne $res->{from});
{ name => 'attachment' },
{ name => 'listattachments' },
{ name => 'download' },
+ { name => 'sendlink' },
];
return $result;
return undef;
}});
+__PACKAGE__->register_method ({
+ name => 'whitelist_delete_base',
+ path => 'whitelist',
+ method => 'DELETE',
+ description => "Delete user whitelist entries.",
+ permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ pmail => $pmail_param_type,
+ address => get_standard_option('pmg-whiteblacklist-entry-list', {
+ pattern => '',
+ description => "The address, or comma-separated list of addresses, you want to remove.",
+ }),
+ },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $addresses = [split(',', $param->{address})];
+ $read_or_modify_user_bw_list->('WL', $param, $addresses, 1);
+
+ return undef;
+ }});
+
+# FIXME: remove for PMG 7.0 - addresses can contain stuff like '/' which breaks
+# API path resolution, thus we replaced it by above "un-templated" call
__PACKAGE__->register_method ({
name => 'whitelist_delete',
path => 'whitelist/{address}',
return undef;
}});
+__PACKAGE__->register_method ({
+ name => 'blacklist_delete_base',
+ path => 'blacklist',
+ method => 'DELETE',
+ description => "Delete user blacklist entries.",
+ permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ pmail => $pmail_param_type,
+ address => get_standard_option('pmg-whiteblacklist-entry-list', {
+ pattern => '',
+ description => "The address, or comma-separated list of addresses, you want to remove.",
+ }),
+ },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $addresses = [split(',', $param->{address})];
+ $read_or_modify_user_bw_list->('BL', $param, $addresses, 1);
+
+ return undef;
+ }});
+
+# FIXME: remove for PMG 7.0 - addresses can contain stuff like '/' which breaks
+# API path resolution, thus we replaced it by above "un-templated" call
__PACKAGE__->register_method ({
name => 'blacklist_delete',
path => 'blacklist/{address}',
return undef;
}});
+my $link_map_fn = "/run/pmgproxy/quarantinelink.map";
+my $per_user_limit = 60*60; # 1 hour
+
+my sub send_link_mail {
+ my ($cfg, $receiver) = @_;
+
+ my $hostname = PVE::INotify::nodename();
+ my $fqdn = $cfg->get('spamquar', 'hostname') //
+ PVE::Tools::get_fqdn($hostname);
+
+ my $port = $cfg->get('spamquar', 'port') // 8006;
+
+ my $protocol = $cfg->get('spamquar', 'protocol') // 'https';
+
+ my $protocol_fqdn_port = "$protocol://$fqdn";
+ if (($protocol eq 'https' && $port != 443) ||
+ ($protocol eq 'http' && $port != 80)) {
+ $protocol_fqdn_port .= ":$port";
+ }
+
+ my $mailfrom = $cfg->get ('spamquar', 'mailfrom') //
+ "Proxmox Mail Gateway <postmaster>";
+
+ my $ticket = PMG::Ticket::assemble_quarantine_ticket($receiver);
+ my $esc_ticket = uri_escape($ticket);
+ my $link = "$protocol_fqdn_port/quarantine?ticket=${esc_ticket}";
+
+ my $text = "Here is your Link for the Spam Quarantine on $fqdn:\n\n$link\n";
+
+ my $mail = MIME::Entity->build(
+ Type => "text/plain",
+ To => $receiver,
+ From => $mailfrom,
+ Subject => "Proxmox Mail Gateway - Quarantine Link",
+ Data => $text,
+ );
+
+ # we use an empty envelope sender (we dont want to receive NDRs)
+ PMG::Utils::reinject_mail ($mail, '', [$receiver], undef, $fqdn);
+}
+
+__PACKAGE__->register_method ({
+ name =>'sendlink',
+ path => 'sendlink',
+ method => 'POST',
+ description => "Send Quarantine link to given e-mail.",
+ permissions => { user => 'world' },
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ mail => get_standard_option('pmg-email-address'),
+ },
+ },
+ returns => { type => "null" },
+ code => sub {
+ my ($param) = @_;
+
+ my $starttime = time();
+
+ my $cfg = PMG::Config->new();
+ my $is_enabled = $cfg->get('spamquar', 'quarantinelink');
+ if (!$is_enabled) {
+ die "This feature is not enabled\n";
+ }
+
+ my $stat = File::stat::stat($link_map_fn);
+
+ if (defined($stat) && ($stat->mtime + 5) > $starttime) {
+ sleep(3);
+ die "Too many requests. Please try again later\n";
+ }
+ warn "mtime: ". $stat->mtime ."\n";
+
+ my $domains = PVE::INotify::read_file('domains');
+ my $domainregex = PMG::Utils::domain_regex([keys %$domains]);
+
+ my $receiver = $param->{mail};
+
+ if ($receiver !~ $domainregex) {
+ sleep(3);
+ return undef; # silently ignore invalid mails
+ }
+
+ PVE::Tools::lock_file_full("${link_map_fn}.lck", 10, 1, sub {
+ return if !-f $link_map_fn;
+ # check if user is allowed to request mail
+ my $data = PVE::Tools::file_get_contents($link_map_fn);
+ for my $line (split("\n", $data)) {
+ next if $line !~ m/^\Q$receiver\E (\d+)$/;
+ if (($1 + $per_user_limit) > $starttime) {
+ sleep(3);
+ die "Too many requests for '$receiver', only one request per"
+ ."hour is permitted. Please try again later\n";
+ } else {
+ last;
+ }
+ }
+ });
+ die $@ if $@;
+
+ # we are allowed to send mail, lock and update file and send
+ PVE::Tools::lock_file("${link_map_fn}.lck", 10, sub {
+ my $newdata = "$receiver $starttime\n";
+
+ if (-f $link_map_fn) {
+ my $data = PVE::Tools::file_get_contents($link_map_fn);
+ for my $line (split("\n", $data)) {
+ if ($line =~ m/^(?:.*) (\d+)$/) {
+ if (($1 + $per_user_limit) > $starttime) {
+ $newdata .= $line . "\n";
+ }
+ }
+ }
+ }
+ PVE::Tools::file_set_contents($link_map_fn, $newdata);
+ });
+ die $@ if $@;
+
+ send_link_mail($cfg, $receiver);
+ sleep(1); # always delay for a bit
+
+ return undef;
+ }});
+
1;