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