#-*- perl -*- # # Copyright (C) 2004,2005,2006,2008 Ken'ichi Fukamachi # All rights reserved. This program is free software; you can # redistribute it and/or modify it under the same terms as Perl itself. # # $FML: State.pm,v 1.28 2008/07/03 20:49:57 fukachan Exp $ # package FML::Process::State; use strict; use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD); use Carp; my $debug = 0; =head1 NAME FML::Process::State - interface to handle states within current process. =head1 SYNOPSIS =head1 DESCRIPTION =head1 METHODS =head1 CURRENT MAILING LIST =cut # Descriptions: dummy. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: none sub current_process_init { my ($curproc) = @_; } # Descriptions: set ml_name to handle currently. # Arguments: OBJ($curproc) STR($ml_name) # Side Effects: update pcb. # Return Value: none sub current_process_set_ml_name { my ($curproc, $ml_name) = @_; my $pcb = $curproc->pcb(); return $pcb->get("current_process", "ml_name", $ml_name); } # Descriptions: get ml_name to handle currently. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: STR sub current_process_get_ml_name { my ($curproc) = @_; my $pcb = $curproc->pcb(); return $pcb->get("current_process", "ml_name"); } =head1 BASIC RESTRICTION STATES CAUTION: restriction_state_*() is reset each command. =cut # Descriptions: dummy. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: none sub restriction_state_init { my ($curproc) = @_; } # Descriptions: save reason on denial. # Arguments: OBJ($curproc) STR($reason) # Side Effects: none # Return Value: none sub restriction_state_set_deny_reason { my ($curproc, $reason) = @_; my $pcb = $curproc->pcb(); $pcb->set("check_restrictions", "deny_reason", $reason); $curproc->logdebug("restriction_state_set_deny_reason: $reason") if $debug; } # Descriptions: return the latest reason on denial. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: none sub restriction_state_get_deny_reason { my ($curproc) = @_; my $pcb = $curproc->pcb(); return $pcb->get("check_restrictions", "deny_reason"); } # Descriptions: save reason on ignore. # Arguments: OBJ($curproc) STR($reason) # Side Effects: none # Return Value: none sub restriction_state_set_ignore_reason { my ($curproc, $reason) = @_; my $pcb = $curproc->pcb(); $pcb->set("check_restrictions", "ignore_reason", $reason); if ($debug) { $curproc->logdebug("restriction_state_set_ignore_reason: $reason"); } } # Descriptions: return the latest reason on ignore. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: none sub restriction_state_get_ignore_reason { my ($curproc) = @_; my $pcb = $curproc->pcb(); return $pcb->get("check_restrictions", "ignore_reason"); } # Descriptions: save reason on hold. # Arguments: OBJ($curproc) STR($reason) # Side Effects: none # Return Value: none sub restriction_state_set_hold_reason { my ($curproc, $reason) = @_; my $pcb = $curproc->pcb(); $pcb->set("check_restrictions", "hold_reason", $reason); if ($debug) { $curproc->logdebug("restriction_state_set_hold_reason: $reason"); } } # Descriptions: return the latest reason on hold. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: none sub restriction_state_get_hold_reason { my ($curproc) = @_; my $pcb = $curproc->pcb(); return $pcb->get("check_restrictions", "hold_reason"); } # Descriptions: save reason on isolate. # Arguments: OBJ($curproc) STR($reason) # Side Effects: none # Return Value: none sub restriction_state_set_isolate_reason { my ($curproc, $reason) = @_; my $pcb = $curproc->pcb(); $pcb->set("check_restrictions", "isolate_reason", $reason); if ($debug) { $curproc->logdebug("restriction_state_set_isolate_reason: $reason"); } } # Descriptions: return the latest reason on isolate. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: none sub restriction_state_get_isolate_reason { my ($curproc) = @_; my $pcb = $curproc->pcb(); return $pcb->get("check_restrictions", "isolate_reason"); } # Descriptions: send message on the latest reason on denial. # Arguments: OBJ($curproc) STR($type) HASH_REF($msg_args) # Side Effects: none # Return Value: none sub restriction_state_reply_reason { my ($curproc, $type, $msg_args) = @_; my $rule = $curproc->restriction_state_get_deny_reason(); $curproc->logdebug("restriction_state_reply_reason: $rule") if $debug; if ($rule eq 'reject_system_special_accounts') { my $r = "deny request from a system account"; $curproc->reply_message_nl("error.system_special_accounts", $r, $msg_args); } elsif ($rule eq 'permit_member_maps') { my $r = "denied since you are not a member"; if ($type eq 'command_mail') { $curproc->reply_message_nl("command.deny", $r, $msg_args); } my $count = $curproc->error_message_get_count("error.not_member"); unless ($count) { $curproc->reply_message_nl("error.not_member", $r, $msg_args); $curproc->error_message_set_count("error.not_member"); } } elsif ($rule eq 'permit_user_command') { my $r = "you are not allowed to use this command."; $curproc->reply_message_nl("command.deny", $r, $msg_args); } elsif ($rule eq 'reject') { my $r = "deny your request"; if ($type eq 'article_post') { $curproc->reply_message_nl("error.reject_post", $r, $msg_args); } elsif ($type eq 'command_mail') { $curproc->reply_message_nl("error.reject_command", $r, $msg_args); } } else { my $r = "deny your request due to an unknown reason"; if ($type eq 'article_post') { $curproc->reply_message_nl("error.reject_post", $r, $msg_args); } elsif ($type eq 'command_mail') { $curproc->reply_message_nl("error.reject_command", $r, $msg_args); } } } =head1 ARTICLE STATES =cut # Descriptions: set the current article id on current process. # Arguments: OBJ($curproc) NUM($id) # Side Effects: update pcb. # Return Value: NUM sub article_set_id { my ($curproc, $id) = @_; my $pcb = $curproc->pcb(); $pcb->set("article_message", "id", $id); } # Descriptions: get the current article id on current process. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: NUM sub article_get_id { my ($curproc) = @_; my $pcb = $curproc->pcb(); return $pcb->get("article_message", "id"); } =head1 COMMAND PROCESSOER STATES command_context_init() called for each command, so restriction_state_*() is reset each time. But in the case of emulatation of listserv/majordomo emulation, we should pay attention for handlings of $command_mail_restrictions and $admin_command_mail_restrictions. The restrictions must differ among mailing lists. So, we do not cache the return value and always check it each command mail line. =cut # Descriptions: parse $orig_command and set up HASH_REF as # base information for command processing. # Arguments: OBJ($curproc) STR($orig_command) # Side Effects: none # Return Value: OBJ sub command_context_init { my ($curproc, $orig_command) = @_; # Example: if orig_command = "# help", comname = "help" use FML::Context::Command; my $context = new FML::Context::Command $curproc; $context->set_command($orig_command); # reset error reason $curproc->restriction_state_set_deny_reason(''); # declare current mailing list. my $ml_name = $curproc->ml_name(); $curproc->command_context_set_ml_name($ml_name); # check if command is valid. my $found = 0; my $name = $context->get_cooked_command() || ''; if ($name) { my $config = $curproc->config(); LIST: for my $list (qw(anonymous_command_mail_allowed_commands user_command_mail_allowed_commands)) { if ($config->has_attribute($list, $name)) { $found = 1; last LIST; } } } # if valid, return the current context (HASH_REF). if ($found) { return $context; } else { # return dummy object. use FML::Context::Command; my $context = new FML::Context::Command $curproc; return $context; } } # Descriptions: set the current $ml_name. # Arguments: OBJ($curproc) STR($ml_name) # Side Effects: update pcb. # Return Value: STR sub command_context_set_ml_name { my ($curproc, $ml_name) = @_; my $pcb = $curproc->pcb(); $pcb->set("process_command", "ml_name", $ml_name); } # Descriptions: return the current $ml_name. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: STR sub command_context_get_ml_name { my ($curproc) = @_; my $pcb = $curproc->pcb(); return( $pcb->get("process_command", "ml_name") || '' ); } # Descriptions: declare no more further command processing needed # due to critical error. # Arguments: OBJ($curproc) # Side Effects: update pcb. # Return Value: NUM sub command_context_set_stop_process { my ($curproc) = @_; my $pcb = $curproc->pcb(); $pcb->set("process_command", "stop_now", 1); } # Descriptions: we stop here or not ? # Arguments: OBJ($curproc) # Side Effects: none # Return Value: NUM sub command_context_get_stop_process { my ($curproc) = @_; my $pcb = $curproc->pcb(); return( $pcb->get("process_command", "stop_now") || 0 ); } # Descriptions: declare no more further command processing needed. # Arguments: OBJ($curproc) # Side Effects: update pcb. # Return Value: NUM sub command_context_set_normal_stop { my ($curproc) = @_; my $pcb = $curproc->pcb(); $pcb->set("process_command", "normal_stop", 1); } # Descriptions: we stop here or not ? # Arguments: OBJ($curproc) # Side Effects: none # Return Value: NUM sub command_context_get_normal_stop { my ($curproc) = @_; my $pcb = $curproc->pcb(); return( $pcb->get("process_command", "normal_stop") || 0 ); } # Descriptions: set "we need to send back confirmation". # usually, this flag means we send back the original message. # hence, this flag is universal over plural ML's. # Arguments: OBJ($curproc) # Side Effects: update pcb. # Return Value: NUM sub command_context_set_need_confirm { my ($curproc) = @_; my $pcb = $curproc->pcb(); # XXX-TODO: correct ? this flag is universal over plural ML's. $pcb->set("process_command", "need_confirm", 1); } # Descriptions: check if we need to send back confirmation ? # usually, this flag means we send back the original message. # hence, this flag is universal over plural ML's. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: NUM sub command_context_get_need_confirm { my ($curproc) = @_; my $pcb = $curproc->pcb(); # XXX-TODO: correct ? this flag is universal over plural ML's. return( $pcb->get("process_command", "need_confirm") || 0 ); } # Descriptions: remote administrator is authenticated. # state is ml specific. # Arguments: OBJ($curproc) # Side Effects: update pcb. # Return Value: NUM sub command_context_set_admin_auth { my ($curproc) = @_; my $pcb = $curproc->pcb(); my $cur_ml = $curproc->command_context_get_ml_name(); my $class = sprintf("admin_auth_ml_name=%s", $cur_ml); $pcb->set("process_command", $class, 1); } # Descriptions: check if remote administrator is authenticated. # state is ml specific. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: NUM sub command_context_get_admin_auth { my ($curproc) = @_; my $pcb = $curproc->pcb(); my $cur_ml = $curproc->command_context_get_ml_name(); my $class = sprintf("admin_auth_ml_name=%s", $cur_ml); return( $pcb->get("process_command", $class) || 0 ); } # Descriptions: store password on memory for later use. # Arguments: OBJ($curproc) STR($password) # Side Effects: update pcb. # Return Value: STR sub command_context_set_admin_password { my ($curproc, $password) = @_; my $pcb = $curproc->pcb(); my $cur_ml = $curproc->command_context_get_ml_name(); my $class = sprintf("admin_password_ml_name=%s", $cur_ml); $pcb->set("process_command", "admin_password", $password); } # Descriptions: retrive stored password on memory. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: STR sub command_context_get_admin_password { my ($curproc) = @_; my $pcb = $curproc->pcb(); my $cur_ml = $curproc->command_context_get_ml_name(); my $class = sprintf("admin_password_ml_name=%s", $cur_ml); return( $pcb->get("process_command", "admin_password") || '' ); } # Descriptions: remote administrator is authenticated. # state is ml specific. # Arguments: OBJ($curproc) NUM($req) # Side Effects: update pcb. # Return Value: NUM sub command_context_set_try_admin_auth_request { my ($curproc, $req) = @_; my $pcb = $curproc->pcb(); my $cur_ml = $curproc->command_context_get_ml_name(); my $class = sprintf("try_admin_auth_request_ml_name=%s", $cur_ml); $pcb->set("process_command", $class, 1); } # Descriptions: remote administrator is authenticated. # state is ml specific. # Arguments: OBJ($curproc) NUM($req) # Side Effects: update pcb. # Return Value: NUM sub command_context_reset_try_admin_auth_request { my ($curproc, $req) = @_; my $pcb = $curproc->pcb(); my $cur_ml = $curproc->command_context_get_ml_name(); my $class = sprintf("try_admin_auth_request_ml_name=%s", $cur_ml); $pcb->set("process_command", $class, 0); } # Descriptions: check if remote administrator is authenticated. # state is ml specific. # Arguments: OBJ($curproc) # Side Effects: none # Return Value: NUM sub command_context_get_try_admin_auth_request { my ($curproc) = @_; my $pcb = $curproc->pcb(); my $cur_ml = $curproc->command_context_get_ml_name(); my $class = sprintf("try_admin_auth_request_ml_name=%s", $cur_ml); return( $pcb->get("process_command", $class) || 0 ); } =head1 FILTER STATE =head2 filter_state_set_error($category, $code) save the filter error for later use. =head2 filter_state_get_error($category) get the filter error. =cut # Descriptions: save the filter error for later use. # Arguments: OBJ($curproc) STR($category) STR($code) # Side Effects: update pcb. # Return Value: none sub filter_state_set_error { my ($curproc, $category, $code) = @_; my $pcb = $curproc->pcb(); $pcb->set("filter_state", $category, $code || 0); } # Descriptions: get the filter error. # Arguments: OBJ($curproc) STR($category) # Side Effects: update pcb. # Return Value: none sub filter_state_get_error { my ($curproc, $category) = @_; my $pcb = $curproc->pcb(); return( $pcb->get("filter_state", $category) || 0 ); } # Descriptions: save the spam filter error for later use. # Arguments: OBJ($curproc) STR($code) # Side Effects: update pcb. # Return Value: none sub filter_state_spam_checker_set_error { my ($curproc, $code) = @_; my $category = "spam_checker"; my $pcb = $curproc->pcb(); $pcb->set("filter_state", $category, $code || 0); } # Descriptions: get the spam filter error. # Arguments: OBJ($curproc) # Side Effects: update pcb. # Return Value: none sub filter_state_spam_checker_get_error { my ($curproc) = @_; my $category = "spam_checker"; my $pcb = $curproc->pcb(); return( $pcb->get("filter_state", $category) || 0 ); } # Descriptions: save the virus filter error for later use. # Arguments: OBJ($curproc) STR($code) # Side Effects: update pcb. # Return Value: none sub filter_state_virus_checker_set_error { my ($curproc, $code) = @_; my $category = "virus_checker"; my $pcb = $curproc->pcb(); $pcb->set("filter_state", $category, $code || 0); } # Descriptions: get the virus filter error. # Arguments: OBJ($curproc) # Side Effects: update pcb. # Return Value: none sub filter_state_virus_checker_get_error { my ($curproc) = @_; my $category = "virus_checker"; my $pcb = $curproc->pcb(); return( $pcb->get("filter_state", $category) || 0 ); } # Descriptions: we need to exit as EX_TEMPFAIL. # Arguments: OBJ($curproc) # Side Effects: update pcb. # Return Value: none sub filter_state_set_tempfail_request { my ($curproc) = @_; my $category = "exit_tempfail"; my $pcb = $curproc->pcb(); $pcb->set("filter_state", $category, 1); } # Descriptions: check if we need to exit as EX_TEMPFAIL. # Arguments: OBJ($curproc) # Side Effects: none. # Return Value: NUM sub filter_state_get_tempfail_request { my ($curproc) = @_; my $category = "exit_tempfail"; my $pcb = $curproc->pcb(); return( $pcb->get("filter_state", $category) || 0 ); } =head1 SMTP STATE =head2 smtp_server_state_set_error($mta) set $mta as error for later hint. =head2 smtp_server_state_get_error() check if $mta as error for later hint. =cut # Descriptions: set $mta as error for later hint. # implies "all servers" unless $mta specified. # Arguments: OBJ($curproc) STR($mta) # Side Effects: update pcb. # Return Value: none sub smtp_server_state_set_error { my ($curproc, $mta) = @_; my $pcb = $curproc->pcb(); $pcb->set("smtp_transaction", $mta || "ALL", "error"); } # Descriptions: check if $mta as error for later hint. # implies "all servers" unless $mta specified. # Arguments: OBJ($curproc) STR($mta) # Side Effects: update pcb. # Return Value: NUM(1 or 0) sub smtp_server_state_get_error { my ($curproc, $mta) = @_; my $pcb = $curproc->pcb(); return( $pcb->get("smtp_transaction", $mta || "ALL") ? 1 : 0 ); } =head1 UTILITY =head2 error_message_set_count($class) increment error count on this class $class to avoid duplicated error messages. =head2 error_message_get_count($class) get error count on this class $class to avoid duplicated error messages. =cut # Descriptions: increment error count on this class $class # to avoid duplicated error messages. # hence, this flag is universal over plural ML's. # Arguments: OBJ($curproc) STR($class) # Side Effects: none # Return Value: none sub error_message_set_count { my ($curproc, $class) = @_; my $pcb = $curproc->pcb(); my $count = $pcb->get("reply_message_count", $class) || 0; $pcb->set("reply_message_count", $class, $count + 1); } # Descriptions: get error count on this class $class # to avoid duplicated error messages. # hence, this flag is universal over plural ML's. # Arguments: OBJ($curproc) STR($class) # Side Effects: none # Return Value: none sub error_message_get_count { my ($curproc, $class) = @_; my $pcb = $curproc->pcb(); return $pcb->get("reply_message_count", $class) || 0; } =head1 CODING STYLE See C on fml coding style guide. =head1 AUTHOR Ken'ichi Fukamachi =head1 COPYRIGHT Copyright (C) 2004,2005,2006,2008 Ken'ichi Fukamachi All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 HISTORY FML::Process::State appeared in fml8 mailing list driver package. See C for more details. =cut 1;