]> git.proxmox.com Git - pmg-api.git/blame - PMG/API2/Quarantine.pm
MailQueue.pm: create new TicketID when inserting data to CMSReceivers
[pmg-api.git] / PMG / API2 / Quarantine.pm
CommitLineData
b66faa68
DM
1package PMG::API2::Quarantine;
2
3use strict;
4use warnings;
5use Time::Local;
6use Time::Zone;
7use Data::Dumper;
34db0c3f 8use Encode;
b66faa68 9
34db0c3f 10use Mail::Header;
e325aa6f 11use Mail::SpamAssassin;
dae021a8 12
b66faa68 13use PVE::SafeSyslog;
6e8886d4 14use PVE::Exception qw(raise_param_exc raise_perm_exc);
b66faa68
DM
15use PVE::Tools qw(extract_param);
16use PVE::JSONSchema qw(get_standard_option);
17use PVE::RESTHandler;
18use PVE::INotify;
34db0c3f 19use PVE::APIServer::Formatter;
b66faa68 20
dae021a8 21use PMG::Utils;
b66faa68 22use PMG::AccessControl;
34db0c3f 23use PMG::Config;
b66faa68 24use PMG::DBTools;
34db0c3f 25use PMG::HTMLMail;
e84bf942 26use PMG::Quarantine;
b66faa68
DM
27
28use base qw(PVE::RESTHandler);
29
1284c016
DM
30my $spamdesc;
31
9efdabf0
DM
32my $extract_pmail = sub {
33 my ($authuser, $role) = @_;
34
35 if ($authuser =~ m/^(.+)\@quarantine$/) {
36 return $1;
37 }
38 raise_param_exc({ pmail => "got unexpected authuser '$authuser' with role '$role'"});
39};
40
157a946b
DM
41my $verify_optional_pmail = sub {
42 my ($authuser, $role, $pmail) = @_;
43
44 if ($role eq 'quser') {
5182cea0 45 raise_param_exc({ pmail => "parameter not allwed with role '$role'"})
91affd06 46 if defined($pmail) && ($pmail ne $authuser);
9efdabf0 47 $pmail = $extract_pmail->($authuser, $role);
157a946b 48 } else {
5182cea0 49 raise_param_exc({ pmail => "parameter required with role '$role'"})
157a946b
DM
50 if !defined($pmail);
51 }
52 return $pmail;
53};
54
1284c016
DM
55sub decode_spaminfo {
56 my ($info) = @_;
57
e325aa6f
DC
58 my $saversion = Mail::SpamAssassin->VERSION;
59
60 my $salocaldir = "/var/lib/spamassassin/$saversion/updates_spamassassin_org";
61
62 $spamdesc = PMG::Utils::load_sa_descriptions([$salocaldir]) if !$spamdesc;
1284c016
DM
63
64 my $res = [];
65
66 foreach my $test (split (',', $info)) {
67 my ($name, $score) = split (':', $test);
68
273c538f 69 my $info = { name => $name, score => $score + 0, desc => '-' };
1284c016
DM
70 if (my $si = $spamdesc->{$name}) {
71 $info->{desc} = $si->{desc};
72 $info->{url} = $si->{url} if defined($si->{url});
73 }
74 push @$res, $info;
75 }
76
77 return $res;
78}
b66faa68 79
157a946b
DM
80my $extract_email = sub {
81 my $data = shift;
82
83 return $data if !$data;
84
85 if ($data =~ m/^.*\s(\S+)\s*$/) {
86 $data = $1;
87 }
88
89 if ($data =~ m/^<([^<>\s]+)>$/) {
90 $data = $1;
91 }
92
93 if ($data !~ m/[\s><]/ && $data =~ m/^(.+\@[^\.]+\..*[^\.]+)$/) {
94 $data = $1;
95 } else {
96 $data = undef;
97 }
98
99 return $data;
100};
101
102my $get_real_sender = sub {
103 my ($ref) = @_;
104
105 my @lines = split('\n', $ref->{header});
106 my $head = Mail::Header->new(\@lines);
107
108 my @fromarray = split ('\s*,\s*', $head->get ('from') || $ref->{sender});
109 my $from = $extract_email->($fromarray[0]) || $ref->{sender};;
110 my $sender = $extract_email->($head->get ('sender'));
111
112 return $sender if $sender;
113
114 return $from;
115};
116
dae021a8
DM
117my $parse_header_info = sub {
118 my ($ref) = @_;
119
120 my $res = { subject => '', from => '' };
121
122 my @lines = split('\n', $ref->{header});
123 my $head = Mail::Header->new(\@lines);
124
125 $res->{subject} = PMG::Utils::decode_rfc1522(PVE::Tools::trim($head->get('subject'))) // '';
126
127 my @fromarray = split('\s*,\s*', $head->get('from') || $ref->{sender});
128
129 $res->{from} = PMG::Utils::decode_rfc1522(PVE::Tools::trim ($fromarray[0])) // '';
130
131 my $sender = PMG::Utils::decode_rfc1522(PVE::Tools::trim($head->get('sender')));
1284c016 132 $res->{sender} = $sender if $sender && ($sender ne $res->{from});
dae021a8
DM
133
134 $res->{envelope_sender} = $ref->{sender};
e84bf942 135 $res->{receiver} = $ref->{receiver} // $ref->{pmail};
6e8886d4 136 $res->{id} = 'C' . $ref->{cid} . 'R' . $ref->{rid};
dae021a8
DM
137 $res->{time} = $ref->{time};
138 $res->{bytes} = $ref->{bytes};
139
1284c016
DM
140 my $qtype = $ref->{qtype};
141
142 if ($qtype eq 'V') {
143 $res->{virusname} = $ref->{info};
2ad4def7 144 $res->{spamlevel} = 0;
1284c016
DM
145 } elsif ($qtype eq 'S') {
146 $res->{spamlevel} = $ref->{spamlevel} // 0;
1284c016
DM
147 }
148
dae021a8
DM
149 return $res;
150};
151
157a946b
DM
152my $pmail_param_type = {
153 description => "List entries for the user with this primary email address. Quarantine users cannot speficy this parameter, but it is required for all other roles.",
154 type => 'string', format => 'email',
155 optional => 1,
156};
6e8886d4 157
b66faa68
DM
158__PACKAGE__->register_method ({
159 name => 'index',
160 path => '',
161 method => 'GET',
162 permissions => { user => 'all' },
163 description => "Directory index.",
164 parameters => {
165 additionalProperties => 0,
166 properties => {},
167 },
168 returns => {
169 type => 'array',
170 items => {
171 type => "object",
172 properties => {},
173 },
174 links => [ { rel => 'child', href => "{name}" } ],
175 },
176 code => sub {
177 my ($param) = @_;
178
179 my $result = [
157a946b
DM
180 { name => 'whitelist' },
181 { name => 'blacklist' },
6e8886d4 182 { name => 'content' },
b66faa68 183 { name => 'spam' },
4f41cebc 184 { name => 'spamusers' },
ab02ba10 185 { name => 'spamstatus' },
b66faa68 186 { name => 'virus' },
c3246f47 187 { name => 'virusstatus' },
091d8086 188 { name => 'quarusers' },
b66faa68
DM
189 ];
190
191 return $result;
192 }});
193
157a946b 194
767657cb
DM
195my $read_or_modify_user_bw_list = sub {
196 my ($listname, $param, $addrs, $delete) = @_;
157a946b
DM
197
198 my $rpcenv = PMG::RESTEnvironment->get();
199 my $authuser = $rpcenv->get_user();
200 my $role = $rpcenv->get_role();
201
202 my $pmail = $verify_optional_pmail->($authuser, $role, $param->{pmail});
203
204 my $dbh = PMG::DBTools::open_ruledb();
205
767657cb
DM
206 my $list = PMG::Quarantine::add_to_blackwhite(
207 $dbh, $pmail, $listname, $addrs, $delete);
157a946b
DM
208
209 my $res = [];
210 foreach my $a (@$list) { push @$res, { address => $a }; }
211 return $res;
212};
213
214__PACKAGE__->register_method ({
215 name => 'whitelist',
216 path => 'whitelist',
217 method => 'GET',
218 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
219 description => "Show user whitelist.",
220 parameters => {
221 additionalProperties => 0,
222 properties => {
223 pmail => $pmail_param_type,
224 },
225 },
226 returns => {
227 type => 'array',
228 items => {
229 type => "object",
230 properties => {
231 address => {
232 type => "string",
233 },
234 },
235 },
236 },
237 code => sub {
238 my ($param) = @_;
239
767657cb
DM
240 return $read_or_modify_user_bw_list->('WL', $param);
241 }});
242
243__PACKAGE__->register_method ({
244 name => 'whitelist_add',
245 path => 'whitelist',
246 method => 'POST',
247 description => "Add user whitelist entries.",
248 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
249 protected => 1,
250 parameters => {
251 additionalProperties => 0,
252 properties => {
253 pmail => $pmail_param_type,
01929101 254 address => get_standard_option('pmg-email-address', {
767657cb 255 description => "The address you want to add.",
01929101 256 }),
767657cb
DM
257 },
258 },
259 returns => { type => 'null' },
260 code => sub {
261 my ($param) = @_;
262
263 $read_or_modify_user_bw_list->('WL', $param, [ $param->{address} ]);
264
265 return undef;
266 }});
267
268__PACKAGE__->register_method ({
269 name => 'whitelist_delete',
270 path => 'whitelist/{address}',
271 method => 'DELETE',
272 description => "Delete user whitelist entries.",
273 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
274 protected => 1,
275 parameters => {
276 additionalProperties => 0,
277 properties => {
278 pmail => $pmail_param_type,
01929101 279 address => get_standard_option('pmg-email-address', {
767657cb 280 description => "The address you want to remove.",
01929101 281 }),
767657cb
DM
282 },
283 },
284 returns => { type => 'null' },
285 code => sub {
286 my ($param) = @_;
287
288 $read_or_modify_user_bw_list->('WL', $param, [ $param->{address} ], 1);
289
290 return undef;
157a946b
DM
291 }});
292
293__PACKAGE__->register_method ({
294 name => 'blacklist',
295 path => 'blacklist',
296 method => 'GET',
297 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
298 description => "Show user blacklist.",
299 parameters => {
300 additionalProperties => 0,
301 properties => {
302 pmail => $pmail_param_type,
303 },
304 },
305 returns => {
306 type => 'array',
307 items => {
308 type => "object",
309 properties => {
310 address => {
311 type => "string",
312 },
313 },
314 },
315 },
316 code => sub {
317 my ($param) = @_;
318
767657cb
DM
319 return $read_or_modify_user_bw_list->('BL', $param);
320 }});
321
322__PACKAGE__->register_method ({
323 name => 'blacklist_add',
324 path => 'blacklist',
325 method => 'POST',
326 description => "Add user blacklist entries.",
327 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
328 protected => 1,
329 parameters => {
330 additionalProperties => 0,
331 properties => {
332 pmail => $pmail_param_type,
01929101 333 address => get_standard_option('pmg-email-address', {
767657cb 334 description => "The address you want to add.",
01929101 335 }),
767657cb
DM
336 },
337 },
338 returns => { type => 'null' },
339 code => sub {
340 my ($param) = @_;
341
342 $read_or_modify_user_bw_list->('BL', $param, [ $param->{address} ]);
343
344 return undef;
345 }});
346
347__PACKAGE__->register_method ({
348 name => 'blacklist_delete',
349 path => 'blacklist/{address}',
350 method => 'DELETE',
351 description => "Delete user blacklist entries.",
352 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
353 protected => 1,
354 parameters => {
355 additionalProperties => 0,
356 properties => {
357 pmail => $pmail_param_type,
01929101 358 address => get_standard_option('pmg-email-address', {
767657cb 359 description => "The address you want to remove.",
01929101 360 }),
767657cb
DM
361 },
362 },
363 returns => { type => 'null' },
364 code => sub {
365 my ($param) = @_;
366
367 $read_or_modify_user_bw_list->('BL', $param, [ $param->{address} ], 1);
368
369 return undef;
157a946b
DM
370 }});
371
4f41cebc
DC
372__PACKAGE__->register_method ({
373 name => 'spamusers',
374 path => 'spamusers',
375 method => 'GET',
24a0be04 376 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
4f41cebc
DC
377 description => "Get a list of receivers of spam in the given timespan (Default the last 24 hours).",
378 parameters => {
379 additionalProperties => 0,
380 properties => {
6214b5cf
DM
381 starttime => get_standard_option('pmg-starttime'),
382 endtime => get_standard_option('pmg-endtime'),
4f41cebc
DC
383 },
384 },
385 returns => {
386 type => 'array',
387 items => {
388 type => "object",
389 properties => {
390 mail => {
391 description => 'the receiving email',
392 type => 'string',
393 },
394 },
395 },
396 },
397 code => sub {
398 my ($param) = @_;
399
400 my $rpcenv = PMG::RESTEnvironment->get();
401 my $authuser = $rpcenv->get_user();
4f41cebc
DC
402
403 my $res = [];
404
405 my $dbh = PMG::DBTools::open_ruledb();
406
407 my $start = $param->{starttime} // (time - 86400);
408 my $end = $param->{endtime} // ($start + 86400);
409
410 my $sth = $dbh->prepare(
411 "SELECT DISTINCT pmail " .
412 "FROM CMailStore, CMSReceivers WHERE " .
413 "time >= $start AND time < $end AND " .
414 "QType = 'S' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
415 "AND Status = 'N' ORDER BY pmail");
416
417 $sth->execute();
418
419 while (my $ref = $sth->fetchrow_hashref()) {
420 push @$res, { mail => $ref->{pmail} };
421 }
422
423 return $res;
424 }});
425
ab02ba10
DM
426__PACKAGE__->register_method ({
427 name => 'spamstatus',
428 path => 'spamstatus',
429 method => 'GET',
430 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
431 description => "Get Spam Quarantine Status",
432 parameters => {
433 additionalProperties => 0,
434 properties => {},
435 },
436 returns => {
437 type => "object",
438 properties => {
439 count => {
440 description => 'Number of stored mails.',
441 type => 'integer',
442 },
443 mbytes => {
444 description => "Estimated disk space usage in MByte.",
445 type => 'number',
446 },
447 avgbytes => {
448 description => "Average size of stored mails in bytes.",
449 type => 'number',
450 },
451 avgspam => {
452 description => "Average spam level.",
453 type => 'number',
454 },
455 },
456 },
457 code => sub {
458 my ($param) = @_;
459
ab02ba10
DM
460 my $dbh = PMG::DBTools::open_ruledb();
461 my $ref = PMG::DBTools::get_quarantine_count($dbh, 'S');
462
463 return $ref;
464 }});
465
9867e2da
DM
466__PACKAGE__->register_method ({
467 name => 'quarusers',
468 path => 'quarusers',
469 method => 'GET',
470 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
471 description => "Get a list of users with whitelist/blacklist setttings.",
472 parameters => {
473 additionalProperties => 0,
474 properties => {},
475 },
476 returns => {
477 type => 'array',
478 items => {
479 type => "object",
480 properties => {
481 mail => {
482 description => 'the receiving email',
483 type => 'string',
484 },
485 },
486 },
487 },
488 code => sub {
489 my ($param) = @_;
490
491 my $rpcenv = PMG::RESTEnvironment->get();
492 my $authuser = $rpcenv->get_user();
9867e2da
DM
493
494 my $res = [];
495
496 my $dbh = PMG::DBTools::open_ruledb();
497
498 my $sth = $dbh->prepare(
499 "SELECT DISTINCT pmail FROM UserPrefs ORDER BY pmail");
500
501 $sth->execute();
502
503 while (my $ref = $sth->fetchrow_hashref()) {
504 push @$res, { mail => $ref->{pmail} };
505 }
506
507 return $res;
508 }});
509
ded33c7c 510__PACKAGE__->register_method ({
83ce499f
DC
511 name => 'spam',
512 path => 'spam',
ded33c7c
DM
513 method => 'GET',
514 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
83ce499f 515 description => "Get a list of quarantined spam mails in the given timeframe (default the last 24 hours) for the given user.",
ded33c7c
DM
516 parameters => {
517 additionalProperties => 0,
518 properties => {
6214b5cf
DM
519 starttime => get_standard_option('pmg-starttime'),
520 endtime => get_standard_option('pmg-endtime'),
157a946b 521 pmail => $pmail_param_type,
ded33c7c
DM
522 },
523 },
524 returns => {
525 type => 'array',
526 items => {
527 type => "object",
dae021a8
DM
528 properties => {
529 id => {
530 description => 'Unique ID',
531 type => 'string',
532 },
533 bytes => {
534 description => "Size of raw email.",
535 type => 'integer' ,
536 },
537 envelope_sender => {
538 description => "SMTP envelope sender.",
539 type => 'string',
540 },
541 from => {
542 description => "Header 'From' field.",
543 type => 'string',
544 },
545 sender => {
546 description => "Header 'Sender' field.",
547 type => 'string',
548 optional => 1,
549 },
550 receiver => {
551 description => "Receiver email address",
552 type => 'string',
553 },
554 subject => {
555 description => "Header 'Subject' field.",
556 type => 'string',
557 },
558 time => {
559 description => "Receive time stamp",
560 type => 'integer',
561 },
1284c016
DM
562 spamlevel => {
563 description => "Spam score.",
564 type => 'number',
565 },
dae021a8 566 },
ded33c7c
DM
567 },
568 },
569 code => sub {
570 my ($param) = @_;
571
572 my $rpcenv = PMG::RESTEnvironment->get();
573 my $authuser = $rpcenv->get_user();
574 my $role = $rpcenv->get_role();
575
157a946b 576 my $pmail = $verify_optional_pmail->($authuser, $role, $param->{pmail});
b66faa68 577
ded33c7c
DM
578 my $res = [];
579
580 my $dbh = PMG::DBTools::open_ruledb();
581
83ce499f 582 my $start = $param->{starttime} // (time - 86400);
ded33c7c
DM
583 my $end = $param->{endtime} // ($start + 86400);
584
585 my $sth = $dbh->prepare(
586 "SELECT * " .
587 "FROM CMailStore, CMSReceivers WHERE " .
588 "pmail = ? AND time >= $start AND time < $end AND " .
589 "QType = 'S' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
590 "AND Status = 'N' ORDER BY pmail, time, receiver");
591
592 $sth->execute($pmail);
593
b66faa68 594 while (my $ref = $sth->fetchrow_hashref()) {
dae021a8
DM
595 my $data = $parse_header_info->($ref);
596 push @$res, $data;
b66faa68
DM
597 }
598
599 return $res;
600 }});
601
bb01dcf8
DM
602__PACKAGE__->register_method ({
603 name => 'virus',
604 path => 'virus',
605 method => 'GET',
606 permissions => { check => [ 'admin', 'qmanager', 'audit' ] },
607 description => "Get a list of quarantined virus mails in the given timeframe (default the last 24 hours).",
608 parameters => {
609 additionalProperties => 0,
610 properties => {
6214b5cf
DM
611 starttime => get_standard_option('pmg-starttime'),
612 endtime => get_standard_option('pmg-endtime'),
bb01dcf8
DM
613 },
614 },
615 returns => {
616 type => 'array',
617 items => {
618 type => "object",
619 properties => {
620 id => {
621 description => 'Unique ID',
622 type => 'string',
623 },
624 bytes => {
625 description => "Size of raw email.",
626 type => 'integer' ,
627 },
628 envelope_sender => {
629 description => "SMTP envelope sender.",
630 type => 'string',
631 },
632 from => {
633 description => "Header 'From' field.",
634 type => 'string',
635 },
636 sender => {
637 description => "Header 'Sender' field.",
638 type => 'string',
639 optional => 1,
640 },
641 receiver => {
642 description => "Receiver email address",
643 type => 'string',
644 },
645 subject => {
646 description => "Header 'Subject' field.",
647 type => 'string',
648 },
649 time => {
650 description => "Receive time stamp",
651 type => 'integer',
652 },
653 virusname => {
654 description => "Virus name.",
655 type => 'string',
656 },
657 },
658 },
659 },
660 code => sub {
661 my ($param) = @_;
662
663 my $rpcenv = PMG::RESTEnvironment->get();
664 my $authuser = $rpcenv->get_user();
bb01dcf8
DM
665
666 my $res = [];
667
668 my $dbh = PMG::DBTools::open_ruledb();
669
670 my $start = $param->{starttime} // (time - 86400);
671 my $end = $param->{endtime} // ($start + 86400);
672
673 my $sth = $dbh->prepare(
674 "SELECT * " .
675 "FROM CMailStore, CMSReceivers WHERE " .
676 "time >= $start AND time < $end AND " .
677 "QType = 'V' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
678 "AND Status = 'N' ORDER BY time, receiver");
679
680 $sth->execute();
681
682 while (my $ref = $sth->fetchrow_hashref()) {
683 my $data = $parse_header_info->($ref);
684 push @$res, $data;
685 }
686
687 return $res;
688 }});
689
c3246f47
DM
690__PACKAGE__->register_method ({
691 name => 'virusstatus',
692 path => 'virusstatus',
693 method => 'GET',
694 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
695 description => "Get Virus Quarantine Status",
696 parameters => {
697 additionalProperties => 0,
698 properties => {},
699 },
700 returns => {
701 type => "object",
702 properties => {
703 count => {
704 description => 'Number of stored mails.',
705 type => 'integer',
706 },
707 mbytes => {
708 description => "Estimated disk space usage in MByte.",
709 type => 'number',
710 },
711 avgbytes => {
712 description => "Average size of stored mails in bytes.",
713 type => 'number',
714 },
715 },
716 },
717 code => sub {
718 my ($param) = @_;
719
720 my $dbh = PMG::DBTools::open_ruledb();
721 my $ref = PMG::DBTools::get_quarantine_count($dbh, 'V');
722
f7fa880f
DM
723 delete $ref->{avgspam};
724
c3246f47
DM
725 return $ref;
726 }});
727
6e8886d4
DM
728__PACKAGE__->register_method ({
729 name => 'content',
730 path => 'content',
731 method => 'GET',
732 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
34db0c3f 733 description => "Get email data. There is a special formatter called 'htmlmail' to get sanitized html view of the mail content (use the '/api2/htmlmail/quarantine/content' url).",
6e8886d4
DM
734 parameters => {
735 additionalProperties => 0,
736 properties => {
737 id => {
738 description => 'Unique ID',
739 type => 'string',
740 pattern => 'C\d+R\d+',
741 maxLength => 40,
742 },
34db0c3f
DM
743 raw => {
744 description => "Display 'raw' eml data. This is only used with the 'htmlmail' formatter.",
745 type => 'boolean',
746 optional => 1,
747 default => 0,
748 },
6e8886d4
DM
749 },
750 },
751 returns => {
752 type => "object",
cd31bb45
DM
753 properties => {
754 id => {
755 description => 'Unique ID',
756 type => 'string',
757 },
758 bytes => {
759 description => "Size of raw email.",
760 type => 'integer' ,
761 },
762 envelope_sender => {
763 description => "SMTP envelope sender.",
764 type => 'string',
765 },
766 from => {
767 description => "Header 'From' field.",
768 type => 'string',
769 },
770 sender => {
771 description => "Header 'Sender' field.",
772 type => 'string',
773 optional => 1,
774 },
775 receiver => {
776 description => "Receiver email address",
777 type => 'string',
778 },
779 subject => {
780 description => "Header 'Subject' field.",
781 type => 'string',
782 },
783 time => {
784 description => "Receive time stamp",
785 type => 'integer',
786 },
787 spamlevel => {
788 description => "Spam score.",
789 type => 'number',
790 },
791 spaminfo => {
792 description => "Information about matched spam tests (name, score, desc, url).",
793 type => 'array',
794 },
795 header => {
796 description => "Raw email header data.",
797 type => 'string',
798 },
799 content => {
800 description => "Raw email data (first 4096 bytes). Useful for preview. NOTE: The 'htmlmail' formatter displays the whole email.",
801 type => 'string',
6eac8473 802 },
cd31bb45 803 },
6e8886d4
DM
804 },
805 code => sub {
806 my ($param) = @_;
807
808 my $rpcenv = PMG::RESTEnvironment->get();
809 my $authuser = $rpcenv->get_user();
810 my $role = $rpcenv->get_role();
34db0c3f 811 my $format = $rpcenv->get_format();
6e8886d4
DM
812
813 my ($cid, $rid) = $param->{id} =~ m/^C(\d+)R(\d+)$/;
814 $cid = int($cid);
815 $rid = int($rid);
816
9efdabf0
DM
817 my $pmail = $role eq 'quser' ? $extract_pmail->($authuser, $role) : undef;
818
6e8886d4
DM
819 my $dbh = PMG::DBTools::open_ruledb();
820
9efdabf0 821 my $ref = PMG::DBTools::load_mail_data($dbh, $cid, $rid, $pmail);
6e8886d4
DM
822
823 if ($role eq 'quser') {
6060e345 824 my $quar_username = $ref->{pmail} . '@quarantine';
8f34e0b1 825 raise_perm_exc("mail does not belong to user '$authuser' ($ref->{pmail}, $pmail)")
6060e345 826 if $authuser ne $quar_username;
6e8886d4
DM
827 }
828
829 my $res = $parse_header_info->($ref);
830
6e8886d4
DM
831
832 my $filename = $ref->{file};
833 my $spooldir = $PMG::MailQueue::spooldir;
834
835 my $path = "$spooldir/$filename";
836
34db0c3f
DM
837 if ($format eq 'htmlmail') {
838
839 my $cfg = PMG::Config->new();
840 my $viewimages = $cfg->get('spamquar', 'viewimages');
841 my $allowhref = $cfg->get('spamquar', 'allowhrefs');
842
34db0c3f
DM
843 $res->{content} = PMG::HTMLMail::email_to_html($path, $param->{raw}, $viewimages, $allowhref);
844
157a946b
DM
845 # to make result verification happy
846 $res->{file} = '';
847 $res->{header} = '';
2ad4def7 848 $res->{spamlevel} = 0;
157a946b 849 $res->{spaminfo} = [];
34db0c3f 850 } else {
cd31bb45 851 # include additional details
34db0c3f 852
cd31bb45 853 my ($header, $content) = PMG::HTMLMail::read_raw_email($path, 4096);
157a946b 854
cd31bb45
DM
855 $res->{file} = $ref->{file};
856 $res->{spaminfo} = decode_spaminfo($ref->{info});
34db0c3f
DM
857 $res->{header} = $header;
858 $res->{content} = $content;
859 }
6e8886d4 860
6e8886d4
DM
861
862 return $res;
863
864 }});
865
34db0c3f
DM
866PVE::APIServer::Formatter::register_page_formatter(
867 'format' => 'htmlmail',
868 method => 'GET',
869 path => '/quarantine/content',
870 code => sub {
871 my ($res, $data, $param, $path, $auth, $config) = @_;
872
873 if(!HTTP::Status::is_success($res->{status})) {
874 return ("Error $res->{status}: $res->{message}", "text/plain");
875 }
876
877 my $ct = "text/html;charset=UTF-8";
878
879 my $raw = $data->{content};
880
881 return (encode('UTF-8', $raw), $ct, 1);
882});
883
157a946b
DM
884__PACKAGE__->register_method ({
885 name =>'action',
886 path => 'content',
887 method => 'POST',
888 description => "Execute quarantine actions.",
889 permissions => { check => [ 'admin', 'qmanager', 'quser'] },
890 protected => 1,
891 parameters => {
892 additionalProperties => 0,
893 properties => {
8f34e0b1 894 pmail => $pmail_param_type,
157a946b
DM
895 id => {
896 description => 'Unique ID',
897 type => 'string',
898 pattern => 'C\d+R\d+',
899 maxLength => 40,
900 },
901 action => {
902 description => 'Action - specify what you want to do with the mail.',
903 type => 'string',
904 enum => ['whitelist', 'blacklist', 'deliver', 'delete'],
905 },
906 },
907 },
908 returns => { type => "null" },
909 code => sub {
910 my ($param) = @_;
911
912 my $rpcenv = PMG::RESTEnvironment->get();
913 my $authuser = $rpcenv->get_user();
914 my $role = $rpcenv->get_role();
915 my $action = $param->{action};
916
917 my ($cid, $rid) = $param->{id} =~ m/^C(\d+)R(\d+)$/;
918 $cid = int($cid);
919 $rid = int($rid);
920
8f34e0b1
DM
921 my $pmail = $verify_optional_pmail->($authuser, $role, $param->{pmail});
922
157a946b
DM
923 my $dbh = PMG::DBTools::open_ruledb();
924
8f34e0b1 925 my $ref = PMG::DBTools::load_mail_data($dbh, $cid, $rid, $pmail);
157a946b
DM
926
927 if ($role eq 'quser') {
6060e345 928 my $quar_username = $ref->{pmail} . '@quarantine';
8f34e0b1 929 raise_perm_exc("mail does not belong to user '$authuser' ($ref->{pmail}, $pmail)")
6060e345 930 if $authuser ne $quar_username;
157a946b
DM
931 }
932
933 my $sender = $get_real_sender->($ref);
157a946b
DM
934
935 if ($action eq 'whitelist') {
c7e20f13 936 PMG::Quarantine::add_to_blackwhite($dbh, $pmail, 'WL', [ $sender ]);
157a946b 937 } elsif ($action eq 'blacklist') {
c7e20f13 938 PMG::Quarantine::add_to_blackwhite($dbh, $pmail, 'BL', [ $sender ]);
157a946b 939 } elsif ($action eq 'deliver') {
c7e20f13 940 PMG::Quarantine::deliver_quarantined_mail($dbh, $ref, $ref->{receiver} // $pmail);
157a946b 941 } elsif ($action eq 'delete') {
c7e20f13 942 PMG::Quarantine::delete_quarantined_mail($dbh, $ref, $ref->{receiver} // $pmail);
e84bf942
DM
943 } else {
944 die "internal error"; # should not be reached
157a946b
DM
945 }
946
947 return undef;
948 }});
e84bf942 949
b66faa68 9501;