]> git.proxmox.com Git - pmg-api.git/blob - PMG/API2/Quarantine.pm
verify_optional_pmail: do not raise error if $pmail ne $authuser
[pmg-api.git] / PMG / API2 / Quarantine.pm
1 package PMG::API2::Quarantine;
2
3 use strict;
4 use warnings;
5 use Time::Local;
6 use Time::Zone;
7 use Data::Dumper;
8 use Encode;
9
10 use Mail::Header;
11
12 use PVE::SafeSyslog;
13 use PVE::Exception qw(raise_param_exc raise_perm_exc);
14 use PVE::Tools qw(extract_param);
15 use PVE::JSONSchema qw(get_standard_option);
16 use PVE::RESTHandler;
17 use PVE::INotify;
18 use PVE::APIServer::Formatter;
19
20 use PMG::Utils;
21 use PMG::AccessControl;
22 use PMG::Config;
23 use PMG::DBTools;
24 use PMG::HTMLMail;
25 use PMG::Quarantine;
26
27 use base qw(PVE::RESTHandler);
28
29 my $spamdesc;
30
31 my $verify_optional_pmail = sub {
32 my ($authuser, $role, $pmail) = @_;
33
34 if ($role eq 'quser') {
35 raise_param_exc({ pmail => "parameter not allwed with role '$role'"})
36 if defined($pmail) && ($pmail ne $authuser);
37 $pmail = $authuser;
38 } else {
39 raise_param_exc({ pmail => "parameter required with role '$role'"})
40 if !defined($pmail);
41 }
42 return $pmail;
43 };
44
45 sub 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 }
65
66 my $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
88 my $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
103 my $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')));
118 $res->{sender} = $sender if $sender && ($sender ne $res->{from});
119
120 $res->{envelope_sender} = $ref->{sender};
121 $res->{receiver} = $ref->{receiver} // $ref->{pmail};
122 $res->{id} = 'C' . $ref->{cid} . 'R' . $ref->{rid};
123 $res->{time} = $ref->{time};
124 $res->{bytes} = $ref->{bytes};
125
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;
132 }
133
134 return $res;
135 };
136
137 my $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 };
142
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 = [
165 { name => 'whitelist' },
166 { name => 'blacklist' },
167 { name => 'content' },
168 { name => 'spam' },
169 { name => 'spamusers' },
170 { name => 'spamstatus' },
171 { name => 'virus' },
172 { name => 'virusstatus' },
173 { name => 'quarusers' },
174 ];
175
176 return $result;
177 }});
178
179
180 my $read_or_modify_user_bw_list = sub {
181 my ($listname, $param, $addrs, $delete) = @_;
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
191 my $list = PMG::Quarantine::add_to_blackwhite(
192 $dbh, $pmail, $listname, $addrs, $delete);
193
194 my $res = [];
195 foreach my $a (@$list) { push @$res, { address => $a }; }
196 return $res;
197 };
198
199 my $address_pattern = '[a-zA-Z0-9\+\-\_\*\.\@]+';
200
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
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;
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
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;
369 }});
370
371 __PACKAGE__->register_method ({
372 name => 'spamusers',
373 path => 'spamusers',
374 method => 'GET',
375 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
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();
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
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
469 my $dbh = PMG::DBTools::open_ruledb();
470 my $ref = PMG::DBTools::get_quarantine_count($dbh, 'S');
471
472 return $ref;
473 }});
474
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();
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
519 __PACKAGE__->register_method ({
520 name => 'spam',
521 path => 'spam',
522 method => 'GET',
523 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
524 description => "Get a list of quarantined spam mails in the given timeframe (default the last 24 hours) for the given user.",
525 parameters => {
526 additionalProperties => 0,
527 properties => {
528 starttime => {
529 description => "Only consider entries newer than 'starttime' (unix epoch). This is set to 'now - 1day' by default.",
530 type => 'integer',
531 minimum => 0,
532 optional => 1,
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 },
540 pmail => $pmail_param_type,
541 },
542 },
543 returns => {
544 type => 'array',
545 items => {
546 type => "object",
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 },
581 spamlevel => {
582 description => "Spam score.",
583 type => 'number',
584 },
585 },
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
595 my $pmail = $verify_optional_pmail->($authuser, $role, $param->{pmail});
596
597 my $res = [];
598
599 my $dbh = PMG::DBTools::open_ruledb();
600
601 my $start = $param->{starttime} // (time - 86400);
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
613 while (my $ref = $sth->fetchrow_hashref()) {
614 my $data = $parse_header_info->($ref);
615 push @$res, $data;
616 }
617
618 return $res;
619 }});
620
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();
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
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
752 delete $ref->{avgspam};
753
754 return $ref;
755 }});
756
757 __PACKAGE__->register_method ({
758 name => 'content',
759 path => 'content',
760 method => 'GET',
761 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
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).",
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 },
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 },
778 },
779 },
780 returns => {
781 type => "object",
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',
831 },
832 },
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();
840 my $format = $rpcenv->get_format();
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
857
858 my $filename = $ref->{file};
859 my $spooldir = $PMG::MailQueue::spooldir;
860
861 my $path = "$spooldir/$filename";
862
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
869 $res->{content} = PMG::HTMLMail::email_to_html($path, $param->{raw}, $viewimages, $allowhref);
870
871 # to make result verification happy
872 $res->{file} = '';
873 $res->{header} = '';
874 $res->{spaminfo} = [];
875 } else {
876 # include additional details
877
878 my ($header, $content) = PMG::HTMLMail::read_raw_email($path, 4096);
879
880 $res->{file} = $ref->{file};
881 $res->{spaminfo} = decode_spaminfo($ref->{info});
882 $res->{header} = $header;
883 $res->{content} = $content;
884 }
885
886
887 return $res;
888
889 }});
890
891 PVE::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
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') {
958 PMG::Quarantine::add_to_blackwhite($dbh, $username, 'WL', [ $sender ]);
959 } elsif ($action eq 'blacklist') {
960 PMG::Quarantine::add_to_blackwhite($dbh, $username, 'BL', [ $sender ]);
961 } elsif ($action eq 'deliver') {
962 my $targets = [ $ref->{pmail} ];
963 PMG::Quarantine::deliver_quarantined_mail($dbh, $ref, $targets);
964 } elsif ($action eq 'delete') {
965 PMG::Quarantine::delete_quarantined_mail($dbh, $ref);
966 } else {
967 die "internal error"; # should not be reached
968 }
969
970 return undef;
971 }});
972
973 1;