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