]> git.proxmox.com Git - pmg-api.git/blame - PMG/API2/Quarantine.pm
PMG/API2/Quarantine.pm - add virusstatus API
[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'"})
157a946b
DM
36 if defined($pmail);
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
469 my $rpcenv = PMG::RESTEnvironment->get();
470 my $authuser = $rpcenv->get_user();
471
472 my $dbh = PMG::DBTools::open_ruledb();
473 my $ref = PMG::DBTools::get_quarantine_count($dbh, 'S');
474
475 return $ref;
476 }});
477
9867e2da
DM
478__PACKAGE__->register_method ({
479 name => 'quarusers',
480 path => 'quarusers',
481 method => 'GET',
482 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
483 description => "Get a list of users with whitelist/blacklist setttings.",
484 parameters => {
485 additionalProperties => 0,
486 properties => {},
487 },
488 returns => {
489 type => 'array',
490 items => {
491 type => "object",
492 properties => {
493 mail => {
494 description => 'the receiving email',
495 type => 'string',
496 },
497 },
498 },
499 },
500 code => sub {
501 my ($param) = @_;
502
503 my $rpcenv = PMG::RESTEnvironment->get();
504 my $authuser = $rpcenv->get_user();
9867e2da
DM
505
506 my $res = [];
507
508 my $dbh = PMG::DBTools::open_ruledb();
509
510 my $sth = $dbh->prepare(
511 "SELECT DISTINCT pmail FROM UserPrefs ORDER BY pmail");
512
513 $sth->execute();
514
515 while (my $ref = $sth->fetchrow_hashref()) {
516 push @$res, { mail => $ref->{pmail} };
517 }
518
519 return $res;
520 }});
521
ded33c7c 522__PACKAGE__->register_method ({
83ce499f
DC
523 name => 'spam',
524 path => 'spam',
ded33c7c
DM
525 method => 'GET',
526 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
83ce499f 527 description => "Get a list of quarantined spam mails in the given timeframe (default the last 24 hours) for the given user.",
ded33c7c
DM
528 parameters => {
529 additionalProperties => 0,
530 properties => {
531 starttime => {
83ce499f 532 description => "Only consider entries newer than 'starttime' (unix epoch). This is set to 'now - 1day' by default.",
ded33c7c
DM
533 type => 'integer',
534 minimum => 0,
83ce499f 535 optional => 1,
ded33c7c
DM
536 },
537 endtime => {
538 description => "Only consider entries older than 'endtime' (unix epoch). This is set to '<start> + 1day' by default.",
539 type => 'integer',
540 minimum => 1,
541 optional => 1,
542 },
157a946b 543 pmail => $pmail_param_type,
ded33c7c
DM
544 },
545 },
546 returns => {
547 type => 'array',
548 items => {
549 type => "object",
dae021a8
DM
550 properties => {
551 id => {
552 description => 'Unique ID',
553 type => 'string',
554 },
555 bytes => {
556 description => "Size of raw email.",
557 type => 'integer' ,
558 },
559 envelope_sender => {
560 description => "SMTP envelope sender.",
561 type => 'string',
562 },
563 from => {
564 description => "Header 'From' field.",
565 type => 'string',
566 },
567 sender => {
568 description => "Header 'Sender' field.",
569 type => 'string',
570 optional => 1,
571 },
572 receiver => {
573 description => "Receiver email address",
574 type => 'string',
575 },
576 subject => {
577 description => "Header 'Subject' field.",
578 type => 'string',
579 },
580 time => {
581 description => "Receive time stamp",
582 type => 'integer',
583 },
1284c016
DM
584 spamlevel => {
585 description => "Spam score.",
586 type => 'number',
587 },
dae021a8 588 },
ded33c7c
DM
589 },
590 },
591 code => sub {
592 my ($param) = @_;
593
594 my $rpcenv = PMG::RESTEnvironment->get();
595 my $authuser = $rpcenv->get_user();
596 my $role = $rpcenv->get_role();
597
157a946b 598 my $pmail = $verify_optional_pmail->($authuser, $role, $param->{pmail});
b66faa68 599
ded33c7c
DM
600 my $res = [];
601
602 my $dbh = PMG::DBTools::open_ruledb();
603
83ce499f 604 my $start = $param->{starttime} // (time - 86400);
ded33c7c
DM
605 my $end = $param->{endtime} // ($start + 86400);
606
607 my $sth = $dbh->prepare(
608 "SELECT * " .
609 "FROM CMailStore, CMSReceivers WHERE " .
610 "pmail = ? AND time >= $start AND time < $end AND " .
611 "QType = 'S' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
612 "AND Status = 'N' ORDER BY pmail, time, receiver");
613
614 $sth->execute($pmail);
615
b66faa68 616 while (my $ref = $sth->fetchrow_hashref()) {
dae021a8
DM
617 my $data = $parse_header_info->($ref);
618 push @$res, $data;
b66faa68
DM
619 }
620
621 return $res;
622 }});
623
bb01dcf8
DM
624__PACKAGE__->register_method ({
625 name => 'virus',
626 path => 'virus',
627 method => 'GET',
628 permissions => { check => [ 'admin', 'qmanager', 'audit' ] },
629 description => "Get a list of quarantined virus mails in the given timeframe (default the last 24 hours).",
630 parameters => {
631 additionalProperties => 0,
632 properties => {
633 starttime => {
634 description => "Only consider entries newer than 'starttime' (unix epoch). This is set to 'now - 1day' by default.",
635 type => 'integer',
636 minimum => 0,
637 optional => 1,
638 },
639 endtime => {
640 description => "Only consider entries older than 'endtime' (unix epoch). This is set to '<start> + 1day' by default.",
641 type => 'integer',
642 minimum => 1,
643 optional => 1,
644 },
645 },
646 },
647 returns => {
648 type => 'array',
649 items => {
650 type => "object",
651 properties => {
652 id => {
653 description => 'Unique ID',
654 type => 'string',
655 },
656 bytes => {
657 description => "Size of raw email.",
658 type => 'integer' ,
659 },
660 envelope_sender => {
661 description => "SMTP envelope sender.",
662 type => 'string',
663 },
664 from => {
665 description => "Header 'From' field.",
666 type => 'string',
667 },
668 sender => {
669 description => "Header 'Sender' field.",
670 type => 'string',
671 optional => 1,
672 },
673 receiver => {
674 description => "Receiver email address",
675 type => 'string',
676 },
677 subject => {
678 description => "Header 'Subject' field.",
679 type => 'string',
680 },
681 time => {
682 description => "Receive time stamp",
683 type => 'integer',
684 },
685 virusname => {
686 description => "Virus name.",
687 type => 'string',
688 },
689 },
690 },
691 },
692 code => sub {
693 my ($param) = @_;
694
695 my $rpcenv = PMG::RESTEnvironment->get();
696 my $authuser = $rpcenv->get_user();
bb01dcf8
DM
697
698 my $res = [];
699
700 my $dbh = PMG::DBTools::open_ruledb();
701
702 my $start = $param->{starttime} // (time - 86400);
703 my $end = $param->{endtime} // ($start + 86400);
704
705 my $sth = $dbh->prepare(
706 "SELECT * " .
707 "FROM CMailStore, CMSReceivers WHERE " .
708 "time >= $start AND time < $end AND " .
709 "QType = 'V' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
710 "AND Status = 'N' ORDER BY time, receiver");
711
712 $sth->execute();
713
714 while (my $ref = $sth->fetchrow_hashref()) {
715 my $data = $parse_header_info->($ref);
716 push @$res, $data;
717 }
718
719 return $res;
720 }});
721
c3246f47
DM
722__PACKAGE__->register_method ({
723 name => 'virusstatus',
724 path => 'virusstatus',
725 method => 'GET',
726 permissions => { check => [ 'admin', 'qmanager', 'audit'] },
727 description => "Get Virus Quarantine Status",
728 parameters => {
729 additionalProperties => 0,
730 properties => {},
731 },
732 returns => {
733 type => "object",
734 properties => {
735 count => {
736 description => 'Number of stored mails.',
737 type => 'integer',
738 },
739 mbytes => {
740 description => "Estimated disk space usage in MByte.",
741 type => 'number',
742 },
743 avgbytes => {
744 description => "Average size of stored mails in bytes.",
745 type => 'number',
746 },
747 },
748 },
749 code => sub {
750 my ($param) = @_;
751
752 my $dbh = PMG::DBTools::open_ruledb();
753 my $ref = PMG::DBTools::get_quarantine_count($dbh, 'V');
754
755 return $ref;
756 }});
757
6e8886d4
DM
758__PACKAGE__->register_method ({
759 name => 'content',
760 path => 'content',
761 method => 'GET',
762 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
34db0c3f 763 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
764 parameters => {
765 additionalProperties => 0,
766 properties => {
767 id => {
768 description => 'Unique ID',
769 type => 'string',
770 pattern => 'C\d+R\d+',
771 maxLength => 40,
772 },
34db0c3f
DM
773 raw => {
774 description => "Display 'raw' eml data. This is only used with the 'htmlmail' formatter.",
775 type => 'boolean',
776 optional => 1,
777 default => 0,
778 },
6e8886d4
DM
779 },
780 },
781 returns => {
782 type => "object",
cd31bb45
DM
783 properties => {
784 id => {
785 description => 'Unique ID',
786 type => 'string',
787 },
788 bytes => {
789 description => "Size of raw email.",
790 type => 'integer' ,
791 },
792 envelope_sender => {
793 description => "SMTP envelope sender.",
794 type => 'string',
795 },
796 from => {
797 description => "Header 'From' field.",
798 type => 'string',
799 },
800 sender => {
801 description => "Header 'Sender' field.",
802 type => 'string',
803 optional => 1,
804 },
805 receiver => {
806 description => "Receiver email address",
807 type => 'string',
808 },
809 subject => {
810 description => "Header 'Subject' field.",
811 type => 'string',
812 },
813 time => {
814 description => "Receive time stamp",
815 type => 'integer',
816 },
817 spamlevel => {
818 description => "Spam score.",
819 type => 'number',
820 },
821 spaminfo => {
822 description => "Information about matched spam tests (name, score, desc, url).",
823 type => 'array',
824 },
825 header => {
826 description => "Raw email header data.",
827 type => 'string',
828 },
829 content => {
830 description => "Raw email data (first 4096 bytes). Useful for preview. NOTE: The 'htmlmail' formatter displays the whole email.",
831 type => 'string',
6eac8473 832 },
cd31bb45 833 },
6e8886d4
DM
834 },
835 code => sub {
836 my ($param) = @_;
837
838 my $rpcenv = PMG::RESTEnvironment->get();
839 my $authuser = $rpcenv->get_user();
840 my $role = $rpcenv->get_role();
34db0c3f 841 my $format = $rpcenv->get_format();
6e8886d4
DM
842
843 my ($cid, $rid) = $param->{id} =~ m/^C(\d+)R(\d+)$/;
844 $cid = int($cid);
845 $rid = int($rid);
846
847 my $dbh = PMG::DBTools::open_ruledb();
848
849 my $ref = PMG::DBTools::load_mail_data($dbh, $cid, $rid);
850
851 if ($role eq 'quser') {
852 raise_perm_exc("mail does not belong to user '$authuser'")
853 if $authuser ne $ref->{pmail};
854 }
855
856 my $res = $parse_header_info->($ref);
857
6e8886d4
DM
858
859 my $filename = $ref->{file};
860 my $spooldir = $PMG::MailQueue::spooldir;
861
862 my $path = "$spooldir/$filename";
863
34db0c3f
DM
864 if ($format eq 'htmlmail') {
865
866 my $cfg = PMG::Config->new();
867 my $viewimages = $cfg->get('spamquar', 'viewimages');
868 my $allowhref = $cfg->get('spamquar', 'allowhrefs');
869
34db0c3f
DM
870 $res->{content} = PMG::HTMLMail::email_to_html($path, $param->{raw}, $viewimages, $allowhref);
871
157a946b
DM
872 # to make result verification happy
873 $res->{file} = '';
874 $res->{header} = '';
875 $res->{spaminfo} = [];
34db0c3f 876 } else {
cd31bb45 877 # include additional details
34db0c3f 878
cd31bb45 879 my ($header, $content) = PMG::HTMLMail::read_raw_email($path, 4096);
157a946b 880
cd31bb45
DM
881 $res->{file} = $ref->{file};
882 $res->{spaminfo} = decode_spaminfo($ref->{info});
34db0c3f
DM
883 $res->{header} = $header;
884 $res->{content} = $content;
885 }
6e8886d4 886
6e8886d4
DM
887
888 return $res;
889
890 }});
891
34db0c3f
DM
892PVE::APIServer::Formatter::register_page_formatter(
893 'format' => 'htmlmail',
894 method => 'GET',
895 path => '/quarantine/content',
896 code => sub {
897 my ($res, $data, $param, $path, $auth, $config) = @_;
898
899 if(!HTTP::Status::is_success($res->{status})) {
900 return ("Error $res->{status}: $res->{message}", "text/plain");
901 }
902
903 my $ct = "text/html;charset=UTF-8";
904
905 my $raw = $data->{content};
906
907 return (encode('UTF-8', $raw), $ct, 1);
908});
909
157a946b
DM
910__PACKAGE__->register_method ({
911 name =>'action',
912 path => 'content',
913 method => 'POST',
914 description => "Execute quarantine actions.",
915 permissions => { check => [ 'admin', 'qmanager', 'quser'] },
916 protected => 1,
917 parameters => {
918 additionalProperties => 0,
919 properties => {
920 id => {
921 description => 'Unique ID',
922 type => 'string',
923 pattern => 'C\d+R\d+',
924 maxLength => 40,
925 },
926 action => {
927 description => 'Action - specify what you want to do with the mail.',
928 type => 'string',
929 enum => ['whitelist', 'blacklist', 'deliver', 'delete'],
930 },
931 },
932 },
933 returns => { type => "null" },
934 code => sub {
935 my ($param) = @_;
936
937 my $rpcenv = PMG::RESTEnvironment->get();
938 my $authuser = $rpcenv->get_user();
939 my $role = $rpcenv->get_role();
940 my $action = $param->{action};
941
942 my ($cid, $rid) = $param->{id} =~ m/^C(\d+)R(\d+)$/;
943 $cid = int($cid);
944 $rid = int($rid);
945
946 my $dbh = PMG::DBTools::open_ruledb();
947
948 my $ref = PMG::DBTools::load_mail_data($dbh, $cid, $rid);
949
950 if ($role eq 'quser') {
951 raise_perm_exc("mail does not belong to user '$authuser'")
952 if $authuser ne $ref->{pmail};
953 }
954
955 my $sender = $get_real_sender->($ref);
956 my $username = $ref->{pmail};
957
958 if ($action eq 'whitelist') {
e84bf942 959 PMG::Quarantine::add_to_blackwhite($dbh, $username, 'WL', [ $sender ]);
157a946b 960 } elsif ($action eq 'blacklist') {
e84bf942 961 PMG::Quarantine::add_to_blackwhite($dbh, $username, 'BL', [ $sender ]);
157a946b 962 } elsif ($action eq 'deliver') {
e84bf942
DM
963 my $targets = [ $ref->{pmail} ];
964 PMG::Quarantine::deliver_quarantined_mail($dbh, $ref, $targets);
157a946b 965 } elsif ($action eq 'delete') {
e84bf942
DM
966 PMG::Quarantine::delete_quarantined_mail($dbh, $ref);
967 } else {
968 die "internal error"; # should not be reached
157a946b
DM
969 }
970
971 return undef;
972 }});
e84bf942 973
b66faa68 9741;