]>
git.proxmox.com Git - pmg-api.git/blob - PMG/API2/Quarantine.pm
1 package PMG
:: API2
:: Quarantine
;
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) ;
18 use PVE
:: APIServer
:: Formatter
;
21 use PMG
:: AccessControl
;
27 use base
qw(PVE::RESTHandler) ;
31 my $verify_optional_pmail = sub {
32 my ( $authuser, $role, $pmail ) = @_ ;
34 if ( $role eq 'quser' ) {
35 raise_param_exc
({ pmail
=> "parameter not allwed with role ' $role '" })
36 if defined ( $pmail ) && ( $pmail ne $authuser );
39 raise_param_exc
({ pmail
=> "parameter required with role ' $role '" })
48 $spamdesc = PMG
:: Utils
:: load_sa_descriptions
() if ! $spamdesc ;
52 foreach my $test ( split ( ',' , $info )) {
53 my ( $name, $score ) = split ( ':' , $test );
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
});
66 my $extract_email = sub {
69 return $data if ! $data ;
71 if ( $data =~ m/^.*\s(\S+)\s*$/ ) {
75 if ( $data =~ m/^<([^<>\s]+)>$/ ) {
79 if ( $data !~ m/[\s><]/ && $data =~ m/^(.+\@[^\.]+\..*[^\.]+)$/ ) {
88 my $get_real_sender = sub {
91 my @lines = split ( ' \n ' , $ref ->{ header
});
92 my $head = Mail
:: Header-
> new ( \
@lines );
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' ));
98 return $sender if $sender ;
103 my $parse_header_info = sub {
106 my $res = { subject
=> '' , from
=> '' };
108 my @lines = split ( ' \n ' , $ref ->{ header
});
109 my $head = Mail
:: Header-
> new ( \
@lines );
111 $res ->{ subject
} = PMG
:: Utils
:: decode_rfc1522
( PVE
:: Tools
:: trim
( $head -> get ( 'subject' ))) // '' ;
113 my @fromarray = split ( '\s*,\s*' , $head -> get ( 'from' ) || $ref ->{ sender
});
115 $res ->{ from
} = PMG
:: Utils
:: decode_rfc1522
( PVE
:: Tools
:: trim
( $fromarray [ 0 ])) // '' ;
117 my $sender = PMG
:: Utils
:: decode_rfc1522
( PVE
:: Tools
:: trim
( $head -> get ( 'sender' )));
118 $res ->{ sender
} = $sender if $sender && ( $sender ne $res ->{ from
});
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
};
126 my $qtype = $ref ->{ qtype
};
129 $res ->{ virusname
} = $ref ->{ info
};
130 } elsif ( $qtype eq 'S' ) {
131 $res ->{ spamlevel
} = $ref ->{ spamlevel
} // 0 ;
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' ,
143 __PACKAGE__-
> register_method ({
147 permissions
=> { user
=> 'all' },
148 description
=> "Directory index." ,
150 additionalProperties
=> 0 ,
159 links
=> [ { rel
=> 'child' , href
=> "{name}" } ],
165 { name
=> 'whitelist' },
166 { name
=> 'blacklist' },
167 { name
=> 'content' },
169 { name
=> 'spamusers' },
170 { name
=> 'spamstatus' },
172 { name
=> 'virusstatus' },
173 { name
=> 'quarusers' },
180 my $read_or_modify_user_bw_list = sub {
181 my ( $listname, $param, $addrs, $delete ) = @_ ;
183 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
184 my $authuser = $rpcenv -> get_user ();
185 my $role = $rpcenv -> get_role ();
187 my $pmail = $verify_optional_pmail ->( $authuser, $role, $param ->{ pmail
});
189 my $dbh = PMG
:: DBTools
:: open_ruledb
();
191 my $list = PMG
:: Quarantine
:: add_to_blackwhite
(
192 $dbh, $pmail, $listname, $addrs, $delete );
195 foreach my $a ( @$list ) { push @$res, { address
=> $a }; }
199 my $address_pattern = '[a-zA-Z0-9\+\-\_\*\.\@]+' ;
201 __PACKAGE__-
> register_method ({
205 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
206 description
=> "Show user whitelist." ,
208 additionalProperties
=> 0 ,
210 pmail
=> $pmail_param_type,
227 return $read_or_modify_user_bw_list ->( 'WL' , $param );
230 __PACKAGE__-
> register_method ({
231 name
=> 'whitelist_add' ,
234 description
=> "Add user whitelist entries." ,
235 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
238 additionalProperties
=> 0 ,
240 pmail
=> $pmail_param_type,
242 description
=> "The address you want to add." ,
244 pattern
=> $address_pattern,
249 returns
=> { type
=> 'null' },
253 $read_or_modify_user_bw_list ->( 'WL' , $param, [ $param ->{ address
} ]);
258 __PACKAGE__-
> register_method ({
259 name
=> 'whitelist_delete' ,
260 path
=> 'whitelist/{address}' ,
262 description
=> "Delete user whitelist entries." ,
263 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
266 additionalProperties
=> 0 ,
268 pmail
=> $pmail_param_type,
270 description
=> "The address you want to remove." ,
272 pattern
=> $address_pattern,
277 returns
=> { type
=> 'null' },
281 $read_or_modify_user_bw_list ->( 'WL' , $param, [ $param ->{ address
} ], 1 );
286 __PACKAGE__-
> register_method ({
290 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
291 description
=> "Show user blacklist." ,
293 additionalProperties
=> 0 ,
295 pmail
=> $pmail_param_type,
312 return $read_or_modify_user_bw_list ->( 'BL' , $param );
315 __PACKAGE__-
> register_method ({
316 name
=> 'blacklist_add' ,
319 description
=> "Add user blacklist entries." ,
320 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
323 additionalProperties
=> 0 ,
325 pmail
=> $pmail_param_type,
327 description
=> "The address you want to add." ,
329 pattern
=> $address_pattern,
334 returns
=> { type
=> 'null' },
338 $read_or_modify_user_bw_list ->( 'BL' , $param, [ $param ->{ address
} ]);
343 __PACKAGE__-
> register_method ({
344 name
=> 'blacklist_delete' ,
345 path
=> 'blacklist/{address}' ,
347 description
=> "Delete user blacklist entries." ,
348 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
351 additionalProperties
=> 0 ,
353 pmail
=> $pmail_param_type,
355 description
=> "The address you want to remove." ,
357 pattern
=> $address_pattern,
362 returns
=> { type
=> 'null' },
366 $read_or_modify_user_bw_list ->( 'BL' , $param, [ $param ->{ address
} ], 1 );
371 __PACKAGE__-
> register_method ({
375 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
376 description
=> "Get a list of receivers of spam in the given timespan (Default the last 24 hours)." ,
378 additionalProperties
=> 0 ,
381 description
=> "Only consider entries newer than 'starttime' (unix epoch). Default is 'now - 1day'." ,
387 description
=> "Only consider entries older than 'endtime' (unix epoch). This is set to '<start> + 1day' by default." ,
400 description
=> 'the receiving email' ,
409 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
410 my $authuser = $rpcenv -> get_user ();
414 my $dbh = PMG
:: DBTools
:: open_ruledb
();
416 my $start = $param ->{ starttime
} // ( time - 86400 );
417 my $end = $param ->{ endtime
} // ( $start + 86400 );
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" );
428 while ( my $ref = $sth -> fetchrow_hashref ()) {
429 push @$res, { mail
=> $ref ->{ pmail
} };
435 __PACKAGE__-
> register_method ({
436 name
=> 'spamstatus' ,
437 path
=> 'spamstatus' ,
439 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
440 description
=> "Get Spam Quarantine Status" ,
442 additionalProperties
=> 0 ,
449 description
=> 'Number of stored mails.' ,
453 description
=> "Estimated disk space usage in MByte." ,
457 description
=> "Average size of stored mails in bytes." ,
461 description
=> "Average spam level." ,
469 my $dbh = PMG
:: DBTools
:: open_ruledb
();
470 my $ref = PMG
:: DBTools
:: get_quarantine_count
( $dbh, 'S' );
475 __PACKAGE__-
> register_method ({
479 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
480 description
=> "Get a list of users with whitelist/blacklist setttings." ,
482 additionalProperties
=> 0 ,
491 description
=> 'the receiving email' ,
500 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
501 my $authuser = $rpcenv -> get_user ();
505 my $dbh = PMG
:: DBTools
:: open_ruledb
();
507 my $sth = $dbh -> prepare (
508 "SELECT DISTINCT pmail FROM UserPrefs ORDER BY pmail" );
512 while ( my $ref = $sth -> fetchrow_hashref ()) {
513 push @$res, { mail
=> $ref ->{ pmail
} };
519 __PACKAGE__-
> register_method ({
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." ,
526 additionalProperties
=> 0 ,
529 description
=> "Only consider entries newer than 'starttime' (unix epoch). This is set to 'now - 1day' by default." ,
535 description
=> "Only consider entries older than 'endtime' (unix epoch). This is set to '<start> + 1day' by default." ,
540 pmail
=> $pmail_param_type,
549 description
=> 'Unique ID' ,
553 description
=> "Size of raw email." ,
557 description
=> "SMTP envelope sender." ,
561 description
=> "Header 'From' field." ,
565 description
=> "Header 'Sender' field." ,
570 description
=> "Receiver email address" ,
574 description
=> "Header 'Subject' field." ,
578 description
=> "Receive time stamp" ,
582 description
=> "Spam score." ,
591 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
592 my $authuser = $rpcenv -> get_user ();
593 my $role = $rpcenv -> get_role ();
595 my $pmail = $verify_optional_pmail ->( $authuser, $role, $param ->{ pmail
});
599 my $dbh = PMG
:: DBTools
:: open_ruledb
();
601 my $start = $param ->{ starttime
} // ( time - 86400 );
602 my $end = $param ->{ endtime
} // ( $start + 86400 );
604 my $sth = $dbh -> prepare (
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" );
611 $sth -> execute ( $pmail );
613 while ( my $ref = $sth -> fetchrow_hashref ()) {
614 my $data = $parse_header_info ->( $ref );
621 __PACKAGE__-
> register_method ({
625 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
626 description
=> "Get a list of quarantined virus mails in the given timeframe (default the last 24 hours)." ,
628 additionalProperties
=> 0 ,
631 description
=> "Only consider entries newer than 'starttime' (unix epoch). This is set to 'now - 1day' by default." ,
637 description
=> "Only consider entries older than 'endtime' (unix epoch). This is set to '<start> + 1day' by default." ,
650 description
=> 'Unique ID' ,
654 description
=> "Size of raw email." ,
658 description
=> "SMTP envelope sender." ,
662 description
=> "Header 'From' field." ,
666 description
=> "Header 'Sender' field." ,
671 description
=> "Receiver email address" ,
675 description
=> "Header 'Subject' field." ,
679 description
=> "Receive time stamp" ,
683 description
=> "Virus name." ,
692 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
693 my $authuser = $rpcenv -> get_user ();
697 my $dbh = PMG
:: DBTools
:: open_ruledb
();
699 my $start = $param ->{ starttime
} // ( time - 86400 );
700 my $end = $param ->{ endtime
} // ( $start + 86400 );
702 my $sth = $dbh -> prepare (
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" );
711 while ( my $ref = $sth -> fetchrow_hashref ()) {
712 my $data = $parse_header_info ->( $ref );
719 __PACKAGE__-
> register_method ({
720 name
=> 'virusstatus' ,
721 path
=> 'virusstatus' ,
723 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
724 description
=> "Get Virus Quarantine Status" ,
726 additionalProperties
=> 0 ,
733 description
=> 'Number of stored mails.' ,
737 description
=> "Estimated disk space usage in MByte." ,
741 description
=> "Average size of stored mails in bytes." ,
749 my $dbh = PMG
:: DBTools
:: open_ruledb
();
750 my $ref = PMG
:: DBTools
:: get_quarantine_count
( $dbh, 'V' );
752 delete $ref ->{ avgspam
};
757 __PACKAGE__-
> register_method ({
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)." ,
764 additionalProperties
=> 0 ,
767 description
=> 'Unique ID' ,
769 pattern
=> 'C\d+R\d+' ,
773 description
=> "Display 'raw' eml data. This is only used with the 'htmlmail' formatter." ,
784 description
=> 'Unique ID' ,
788 description
=> "Size of raw email." ,
792 description
=> "SMTP envelope sender." ,
796 description
=> "Header 'From' field." ,
800 description
=> "Header 'Sender' field." ,
805 description
=> "Receiver email address" ,
809 description
=> "Header 'Subject' field." ,
813 description
=> "Receive time stamp" ,
817 description
=> "Spam score." ,
821 description
=> "Information about matched spam tests (name, score, desc, url)." ,
825 description
=> "Raw email header data." ,
829 description
=> "Raw email data (first 4096 bytes). Useful for preview. NOTE: The 'htmlmail' formatter displays the whole email." ,
837 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
838 my $authuser = $rpcenv -> get_user ();
839 my $role = $rpcenv -> get_role ();
840 my $format = $rpcenv -> get_format ();
842 my ( $cid, $rid ) = $param ->{ id
} =~ m/^C(\d+)R(\d+)$/ ;
846 my $dbh = PMG
:: DBTools
:: open_ruledb
();
848 my $ref = PMG
:: DBTools
:: load_mail_data
( $dbh, $cid, $rid );
850 if ( $role eq 'quser' ) {
851 raise_perm_exc
( "mail does not belong to user ' $authuser '" )
852 if $authuser ne $ref ->{ pmail
};
855 my $res = $parse_header_info ->( $ref );
858 my $filename = $ref ->{ file
};
859 my $spooldir = $PMG :: MailQueue
:: spooldir
;
861 my $path = " $spooldir/$filename " ;
863 if ( $format eq 'htmlmail' ) {
865 my $cfg = PMG
:: Config-
> new ();
866 my $viewimages = $cfg -> get ( 'spamquar' , 'viewimages' );
867 my $allowhref = $cfg -> get ( 'spamquar' , 'allowhrefs' );
869 $res ->{ content
} = PMG
:: HTMLMail
:: email_to_html
( $path, $param ->{ raw
}, $viewimages, $allowhref );
871 # to make result verification happy
874 $res ->{ spaminfo
} = [];
876 # include additional details
878 my ( $header, $content ) = PMG
:: HTMLMail
:: read_raw_email
( $path, 4096 );
880 $res ->{ file
} = $ref ->{ file
};
881 $res ->{ spaminfo
} = decode_spaminfo
( $ref ->{ info
});
882 $res ->{ header
} = $header ;
883 $res ->{ content
} = $content ;
891 PVE
:: APIServer
:: Formatter
:: register_page_formatter
(
892 'format' => 'htmlmail' ,
894 path
=> '/quarantine/content' ,
896 my ( $res, $data, $param, $path, $auth, $config ) = @_ ;
898 if (! HTTP
:: Status
:: is_success
( $res ->{ status
})) {
899 return ( "Error $res ->{status}: $res ->{message}" , "text/plain" );
902 my $ct = "text/html;charset=UTF-8" ;
904 my $raw = $data ->{ content
};
906 return ( encode
( 'UTF-8' , $raw ), $ct, 1 );
909 __PACKAGE__-
> register_method ({
913 description
=> "Execute quarantine actions." ,
914 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'quser' ] },
917 additionalProperties
=> 0 ,
920 description
=> 'Unique ID' ,
922 pattern
=> 'C\d+R\d+' ,
926 description
=> 'Action - specify what you want to do with the mail.' ,
928 enum
=> [ 'whitelist' , 'blacklist' , 'deliver' , 'delete' ],
932 returns
=> { type
=> "null" },
936 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
937 my $authuser = $rpcenv -> get_user ();
938 my $role = $rpcenv -> get_role ();
939 my $action = $param ->{ action
};
941 my ( $cid, $rid ) = $param ->{ id
} =~ m/^C(\d+)R(\d+)$/ ;
945 my $dbh = PMG
:: DBTools
:: open_ruledb
();
947 my $ref = PMG
:: DBTools
:: load_mail_data
( $dbh, $cid, $rid );
949 if ( $role eq 'quser' ) {
950 raise_perm_exc
( "mail does not belong to user ' $authuser '" )
951 if $authuser ne $ref ->{ pmail
};
954 my $sender = $get_real_sender ->( $ref );
955 my $username = $ref ->{ pmail
};
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 );
967 die "internal error" ; # should not be reached