]>
git.proxmox.com Git - pmg-api.git/blob - PMG/API2/Quarantine.pm
1 package PMG
:: API2
:: Quarantine
;
11 use Mail
:: SpamAssassin
;
14 use PVE
:: Exception
qw(raise_param_exc raise_perm_exc) ;
15 use PVE
:: Tools
qw(extract_param) ;
16 use PVE
:: JSONSchema
qw(get_standard_option) ;
19 use PVE
:: APIServer
:: Formatter
;
22 use PMG
:: AccessControl
;
28 use base
qw(PVE::RESTHandler) ;
32 my $extract_pmail = sub {
33 my ( $authuser, $role ) = @_ ;
35 if ( $authuser =~ m/^(.+)\@quarantine$/ ) {
38 raise_param_exc
({ pmail
=> "got unexpected authuser ' $authuser ' with role ' $role '" });
41 my $verify_optional_pmail = sub {
42 my ( $authuser, $role, $pmail ) = @_ ;
44 if ( $role eq 'quser' ) {
45 raise_param_exc
({ pmail
=> "parameter not allwed with role ' $role '" })
46 if defined ( $pmail ) && ( $pmail ne $authuser );
47 $pmail = $extract_pmail ->( $authuser, $role );
49 raise_param_exc
({ pmail
=> "parameter required with role ' $role '" })
58 my $saversion = Mail
:: SpamAssassin-
> VERSION ;
60 my $salocaldir = "/var/lib/spamassassin/ $saversion/updates_spamassassin_org " ;
62 $spamdesc = PMG
:: Utils
:: load_sa_descriptions
([ $salocaldir ]) if ! $spamdesc ;
66 foreach my $test ( split ( ',' , $info )) {
67 my ( $name, $score ) = split ( ':' , $test );
69 my $info = { name
=> $name, score
=> $score + 0 , desc
=> '-' };
70 if ( my $si = $spamdesc ->{ $name }) {
71 $info ->{ desc
} = $si ->{ desc
};
72 $info ->{ url
} = $si ->{ url
} if defined ( $si ->{ url
});
80 my $extract_email = sub {
83 return $data if ! $data ;
85 if ( $data =~ m/^.*\s(\S+)\s*$/ ) {
89 if ( $data =~ m/^<([^<>\s]+)>$/ ) {
93 if ( $data !~ m/[\s><]/ && $data =~ m/^(.+\@[^\.]+\..*[^\.]+)$/ ) {
102 my $get_real_sender = sub {
105 my @lines = split ( ' \n ' , $ref ->{ header
});
106 my $head = Mail
:: Header-
> new ( \
@lines );
108 my @fromarray = split ( '\s*,\s*' , $head -> get ( 'from' ) || $ref ->{ sender
});
109 my $from = $extract_email ->( $fromarray [ 0 ]) || $ref ->{ sender
};;
110 my $sender = $extract_email ->( $head -> get ( 'sender' ));
112 return $sender if $sender ;
117 my $parse_header_info = sub {
120 my $res = { subject
=> '' , from
=> '' };
122 my @lines = split ( ' \n ' , $ref ->{ header
});
123 my $head = Mail
:: Header-
> new ( \
@lines );
125 $res ->{ subject
} = PMG
:: Utils
:: decode_rfc1522
( PVE
:: Tools
:: trim
( $head -> get ( 'subject' ))) // '' ;
127 my @fromarray = split ( '\s*,\s*' , $head -> get ( 'from' ) || $ref ->{ sender
});
129 $res ->{ from
} = PMG
:: Utils
:: decode_rfc1522
( PVE
:: Tools
:: trim
( $fromarray [ 0 ])) // '' ;
131 my $sender = PMG
:: Utils
:: decode_rfc1522
( PVE
:: Tools
:: trim
( $head -> get ( 'sender' )));
132 $res ->{ sender
} = $sender if $sender && ( $sender ne $res ->{ from
});
134 $res ->{ envelope_sender
} = $ref ->{ sender
};
135 $res ->{ receiver
} = $ref ->{ receiver
} // $ref ->{ pmail
};
136 $res ->{ id
} = 'C' . $ref ->{ cid
} . 'R' . $ref ->{ rid
};
137 $res ->{ time } = $ref ->{ time };
138 $res ->{ bytes
} = $ref ->{ bytes
};
140 my $qtype = $ref ->{ qtype
};
143 $res ->{ virusname
} = $ref ->{ info
};
144 $res ->{ spamlevel
} = 0 ;
145 } elsif ( $qtype eq 'S' ) {
146 $res ->{ spamlevel
} = $ref ->{ spamlevel
} // 0 ;
152 my $pmail_param_type = {
153 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." ,
154 type
=> 'string' , format
=> 'email' ,
158 __PACKAGE__-
> register_method ({
162 permissions
=> { user
=> 'all' },
163 description
=> "Directory index." ,
165 additionalProperties
=> 0 ,
174 links
=> [ { rel
=> 'child' , href
=> "{name}" } ],
180 { name
=> 'whitelist' },
181 { name
=> 'blacklist' },
182 { name
=> 'content' },
184 { name
=> 'spamusers' },
185 { name
=> 'spamstatus' },
187 { name
=> 'virusstatus' },
188 { name
=> 'quarusers' },
195 my $read_or_modify_user_bw_list = sub {
196 my ( $listname, $param, $addrs, $delete ) = @_ ;
198 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
199 my $authuser = $rpcenv -> get_user ();
200 my $role = $rpcenv -> get_role ();
202 my $pmail = $verify_optional_pmail ->( $authuser, $role, $param ->{ pmail
});
204 my $dbh = PMG
:: DBTools
:: open_ruledb
();
206 my $list = PMG
:: Quarantine
:: add_to_blackwhite
(
207 $dbh, $pmail, $listname, $addrs, $delete );
210 foreach my $a ( @$list ) { push @$res, { address
=> $a }; }
214 __PACKAGE__-
> register_method ({
218 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
219 description
=> "Show user whitelist." ,
221 additionalProperties
=> 0 ,
223 pmail
=> $pmail_param_type,
240 return $read_or_modify_user_bw_list ->( 'WL' , $param );
243 __PACKAGE__-
> register_method ({
244 name
=> 'whitelist_add' ,
247 description
=> "Add user whitelist entries." ,
248 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
251 additionalProperties
=> 0 ,
253 pmail
=> $pmail_param_type,
254 address
=> get_standard_option
( 'pmg-email-address' , {
255 description
=> "The address you want to add." ,
259 returns
=> { type
=> 'null' },
263 $read_or_modify_user_bw_list ->( 'WL' , $param, [ $param ->{ address
} ]);
268 __PACKAGE__-
> register_method ({
269 name
=> 'whitelist_delete' ,
270 path
=> 'whitelist/{address}' ,
272 description
=> "Delete user whitelist entries." ,
273 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
276 additionalProperties
=> 0 ,
278 pmail
=> $pmail_param_type,
279 address
=> get_standard_option
( 'pmg-email-address' , {
280 description
=> "The address you want to remove." ,
284 returns
=> { type
=> 'null' },
288 $read_or_modify_user_bw_list ->( 'WL' , $param, [ $param ->{ address
} ], 1 );
293 __PACKAGE__-
> register_method ({
297 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
298 description
=> "Show user blacklist." ,
300 additionalProperties
=> 0 ,
302 pmail
=> $pmail_param_type,
319 return $read_or_modify_user_bw_list ->( 'BL' , $param );
322 __PACKAGE__-
> register_method ({
323 name
=> 'blacklist_add' ,
326 description
=> "Add user blacklist entries." ,
327 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
330 additionalProperties
=> 0 ,
332 pmail
=> $pmail_param_type,
333 address
=> get_standard_option
( 'pmg-email-address' , {
334 description
=> "The address you want to add." ,
338 returns
=> { type
=> 'null' },
342 $read_or_modify_user_bw_list ->( 'BL' , $param, [ $param ->{ address
} ]);
347 __PACKAGE__-
> register_method ({
348 name
=> 'blacklist_delete' ,
349 path
=> 'blacklist/{address}' ,
351 description
=> "Delete user blacklist entries." ,
352 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
355 additionalProperties
=> 0 ,
357 pmail
=> $pmail_param_type,
358 address
=> get_standard_option
( 'pmg-email-address' , {
359 description
=> "The address you want to remove." ,
363 returns
=> { type
=> 'null' },
367 $read_or_modify_user_bw_list ->( 'BL' , $param, [ $param ->{ address
} ], 1 );
372 __PACKAGE__-
> register_method ({
376 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
377 description
=> "Get a list of receivers of spam in the given timespan (Default the last 24 hours)." ,
379 additionalProperties
=> 0 ,
381 starttime
=> get_standard_option
( 'pmg-starttime' ),
382 endtime
=> get_standard_option
( 'pmg-endtime' ),
391 description
=> 'the receiving email' ,
400 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
401 my $authuser = $rpcenv -> get_user ();
405 my $dbh = PMG
:: DBTools
:: open_ruledb
();
407 my $start = $param ->{ starttime
} // ( time - 86400 );
408 my $end = $param ->{ endtime
} // ( $start + 86400 );
410 my $sth = $dbh -> prepare (
411 "SELECT DISTINCT pmail " .
412 "FROM CMailStore, CMSReceivers WHERE " .
413 "time >= $start AND time < $end AND " .
414 "QType = 'S' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
415 "AND Status = 'N' ORDER BY pmail" );
419 while ( my $ref = $sth -> fetchrow_hashref ()) {
420 push @$res, { mail
=> $ref ->{ pmail
} };
426 __PACKAGE__-
> register_method ({
427 name
=> 'spamstatus' ,
428 path
=> 'spamstatus' ,
430 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
431 description
=> "Get Spam Quarantine Status" ,
433 additionalProperties
=> 0 ,
440 description
=> 'Number of stored mails.' ,
444 description
=> "Estimated disk space usage in MByte." ,
448 description
=> "Average size of stored mails in bytes." ,
452 description
=> "Average spam level." ,
460 my $dbh = PMG
:: DBTools
:: open_ruledb
();
461 my $ref = PMG
:: DBTools
:: get_quarantine_count
( $dbh, 'S' );
466 __PACKAGE__-
> register_method ({
470 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
471 description
=> "Get a list of users with whitelist/blacklist setttings." ,
473 additionalProperties
=> 0 ,
482 description
=> 'the receiving email' ,
491 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
492 my $authuser = $rpcenv -> get_user ();
496 my $dbh = PMG
:: DBTools
:: open_ruledb
();
498 my $sth = $dbh -> prepare (
499 "SELECT DISTINCT pmail FROM UserPrefs ORDER BY pmail" );
503 while ( my $ref = $sth -> fetchrow_hashref ()) {
504 push @$res, { mail
=> $ref ->{ pmail
} };
510 __PACKAGE__-
> register_method ({
514 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
515 description
=> "Get a list of quarantined spam mails in the given timeframe (default the last 24 hours) for the given user." ,
517 additionalProperties
=> 0 ,
519 starttime
=> get_standard_option
( 'pmg-starttime' ),
520 endtime
=> get_standard_option
( 'pmg-endtime' ),
521 pmail
=> $pmail_param_type,
530 description
=> 'Unique ID' ,
534 description
=> "Size of raw email." ,
538 description
=> "SMTP envelope sender." ,
542 description
=> "Header 'From' field." ,
546 description
=> "Header 'Sender' field." ,
551 description
=> "Receiver email address" ,
555 description
=> "Header 'Subject' field." ,
559 description
=> "Receive time stamp" ,
563 description
=> "Spam score." ,
572 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
573 my $authuser = $rpcenv -> get_user ();
574 my $role = $rpcenv -> get_role ();
576 my $pmail = $verify_optional_pmail ->( $authuser, $role, $param ->{ pmail
});
580 my $dbh = PMG
:: DBTools
:: open_ruledb
();
582 my $start = $param ->{ starttime
} // ( time - 86400 );
583 my $end = $param ->{ endtime
} // ( $start + 86400 );
585 my $sth = $dbh -> prepare (
587 "FROM CMailStore, CMSReceivers WHERE " .
588 "pmail = ? AND time >= $start AND time < $end AND " .
589 "QType = 'S' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
590 "AND Status = 'N' ORDER BY pmail, time, receiver" );
592 $sth -> execute ( $pmail );
594 while ( my $ref = $sth -> fetchrow_hashref ()) {
595 my $data = $parse_header_info ->( $ref );
602 __PACKAGE__-
> register_method ({
606 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
607 description
=> "Get a list of quarantined virus mails in the given timeframe (default the last 24 hours)." ,
609 additionalProperties
=> 0 ,
611 starttime
=> get_standard_option
( 'pmg-starttime' ),
612 endtime
=> get_standard_option
( 'pmg-endtime' ),
621 description
=> 'Unique ID' ,
625 description
=> "Size of raw email." ,
629 description
=> "SMTP envelope sender." ,
633 description
=> "Header 'From' field." ,
637 description
=> "Header 'Sender' field." ,
642 description
=> "Receiver email address" ,
646 description
=> "Header 'Subject' field." ,
650 description
=> "Receive time stamp" ,
654 description
=> "Virus name." ,
663 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
664 my $authuser = $rpcenv -> get_user ();
668 my $dbh = PMG
:: DBTools
:: open_ruledb
();
670 my $start = $param ->{ starttime
} // ( time - 86400 );
671 my $end = $param ->{ endtime
} // ( $start + 86400 );
673 my $sth = $dbh -> prepare (
675 "FROM CMailStore, CMSReceivers WHERE " .
676 "time >= $start AND time < $end AND " .
677 "QType = 'V' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
678 "AND Status = 'N' ORDER BY time, receiver" );
682 while ( my $ref = $sth -> fetchrow_hashref ()) {
683 my $data = $parse_header_info ->( $ref );
690 __PACKAGE__-
> register_method ({
691 name
=> 'virusstatus' ,
692 path
=> 'virusstatus' ,
694 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' ] },
695 description
=> "Get Virus Quarantine Status" ,
697 additionalProperties
=> 0 ,
704 description
=> 'Number of stored mails.' ,
708 description
=> "Estimated disk space usage in MByte." ,
712 description
=> "Average size of stored mails in bytes." ,
720 my $dbh = PMG
:: DBTools
:: open_ruledb
();
721 my $ref = PMG
:: DBTools
:: get_quarantine_count
( $dbh, 'V' );
723 delete $ref ->{ avgspam
};
728 __PACKAGE__-
> register_method ({
732 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'audit' , 'quser' ] },
733 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)." ,
735 additionalProperties
=> 0 ,
738 description
=> 'Unique ID' ,
740 pattern
=> 'C\d+R\d+' ,
744 description
=> "Display 'raw' eml data. This is only used with the 'htmlmail' formatter." ,
755 description
=> 'Unique ID' ,
759 description
=> "Size of raw email." ,
763 description
=> "SMTP envelope sender." ,
767 description
=> "Header 'From' field." ,
771 description
=> "Header 'Sender' field." ,
776 description
=> "Receiver email address" ,
780 description
=> "Header 'Subject' field." ,
784 description
=> "Receive time stamp" ,
788 description
=> "Spam score." ,
792 description
=> "Information about matched spam tests (name, score, desc, url)." ,
796 description
=> "Raw email header data." ,
800 description
=> "Raw email data (first 4096 bytes). Useful for preview. NOTE: The 'htmlmail' formatter displays the whole email." ,
808 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
809 my $authuser = $rpcenv -> get_user ();
810 my $role = $rpcenv -> get_role ();
811 my $format = $rpcenv -> get_format ();
813 my ( $cid, $rid ) = $param ->{ id
} =~ m/^C(\d+)R(\d+)$/ ;
817 my $pmail = $role eq 'quser' ?
$extract_pmail ->( $authuser, $role ) : undef ;
819 my $dbh = PMG
:: DBTools
:: open_ruledb
();
821 my $ref = PMG
:: DBTools
:: load_mail_data
( $dbh, $cid, $rid, $pmail );
823 if ( $role eq 'quser' ) {
824 my $quar_username = $ref ->{ pmail
} . ' @quarantine ' ;
825 raise_perm_exc
( "mail does not belong to user ' $authuser ' ( $ref ->{pmail}, $pmail )" )
826 if $authuser ne $quar_username ;
829 my $res = $parse_header_info ->( $ref );
832 my $filename = $ref ->{ file
};
833 my $spooldir = $PMG :: MailQueue
:: spooldir
;
835 my $path = " $spooldir/$filename " ;
837 if ( $format eq 'htmlmail' ) {
839 my $cfg = PMG
:: Config-
> new ();
840 my $viewimages = $cfg -> get ( 'spamquar' , 'viewimages' );
841 my $allowhref = $cfg -> get ( 'spamquar' , 'allowhrefs' );
843 $res ->{ content
} = PMG
:: HTMLMail
:: email_to_html
( $path, $param ->{ raw
}, $viewimages, $allowhref );
845 # to make result verification happy
848 $res ->{ spamlevel
} = 0 ;
849 $res ->{ spaminfo
} = [];
851 # include additional details
853 my ( $header, $content ) = PMG
:: HTMLMail
:: read_raw_email
( $path, 4096 );
855 $res ->{ file
} = $ref ->{ file
};
856 $res ->{ spaminfo
} = decode_spaminfo
( $ref ->{ info
});
857 $res ->{ header
} = $header ;
858 $res ->{ content
} = $content ;
866 PVE
:: APIServer
:: Formatter
:: register_page_formatter
(
867 'format' => 'htmlmail' ,
869 path
=> '/quarantine/content' ,
871 my ( $res, $data, $param, $path, $auth, $config ) = @_ ;
873 if (! HTTP
:: Status
:: is_success
( $res ->{ status
})) {
874 return ( "Error $res ->{status}: $res ->{message}" , "text/plain" );
877 my $ct = "text/html;charset=UTF-8" ;
879 my $raw = $data ->{ content
};
881 return ( encode
( 'UTF-8' , $raw ), $ct, 1 );
884 __PACKAGE__-
> register_method ({
888 description
=> "Execute quarantine actions." ,
889 permissions
=> { check
=> [ 'admin' , 'qmanager' , 'quser' ] },
892 additionalProperties
=> 0 ,
894 pmail
=> $pmail_param_type,
896 description
=> 'Unique ID' ,
898 pattern
=> 'C\d+R\d+' ,
902 description
=> 'Action - specify what you want to do with the mail.' ,
904 enum
=> [ 'whitelist' , 'blacklist' , 'deliver' , 'delete' ],
908 returns
=> { type
=> "null" },
912 my $rpcenv = PMG
:: RESTEnvironment-
> get ();
913 my $authuser = $rpcenv -> get_user ();
914 my $role = $rpcenv -> get_role ();
915 my $action = $param ->{ action
};
917 my ( $cid, $rid ) = $param ->{ id
} =~ m/^C(\d+)R(\d+)$/ ;
921 my $pmail = $verify_optional_pmail ->( $authuser, $role, $param ->{ pmail
});
923 my $dbh = PMG
:: DBTools
:: open_ruledb
();
925 my $ref = PMG
:: DBTools
:: load_mail_data
( $dbh, $cid, $rid, $pmail );
927 if ( $role eq 'quser' ) {
928 my $quar_username = $ref ->{ pmail
} . ' @quarantine ' ;
929 raise_perm_exc
( "mail does not belong to user ' $authuser ' ( $ref ->{pmail}, $pmail )" )
930 if $authuser ne $quar_username ;
933 my $sender = $get_real_sender ->( $ref );
934 my $username = $ref ->{ pmail
};
936 if ( $action eq 'whitelist' ) {
937 PMG
:: Quarantine
:: add_to_blackwhite
( $dbh, $username, 'WL' , [ $sender ]);
938 } elsif ( $action eq 'blacklist' ) {
939 PMG
:: Quarantine
:: add_to_blackwhite
( $dbh, $username, 'BL' , [ $sender ]);
940 } elsif ( $action eq 'deliver' ) {
941 my $targets = [ $ref ->{ pmail
} ];
942 PMG
:: Quarantine
:: deliver_quarantined_mail
( $dbh, $ref, $targets );
943 } elsif ( $action eq 'delete' ) {
944 PMG
:: Quarantine
:: delete_quarantined_mail
( $dbh, $ref );
946 die "internal error" ; # should not be reached