#!/usr/local/bin/perl -w #-*- perl -*- # # Copyright (C) 2000,2001 Ken'ichi Fukamachi # All rights reserved. # # $FML: Command.pm,v 1.19 2001/10/14 00:33:41 fukachan Exp $ # package FML::Process::Command; use vars qw($debug @ISA @EXPORT @EXPORT_OK); use strict; use Carp; use FML::Process::Kernel; use FML::Log qw(Log LogWarn LogError); use FML::Config; @ISA = qw(FML::Process::Kernel); =head1 NAME FML::Process::Command -- fml5 command dispacher. =head1 SYNOPSIS use FML::Process::Command; ... See L for details of fml process flow. =head1 DESCRIPTION C is a command wrapper and top level dispatcher for commands. It kicks off corresponding C->C<$command($curproc,$args)> for the given C<$command>. =head1 METHODS =head2 C make fml process object. my $curproc = new FML::Process::Kernel $args; =head2 C forward the request to SUPER CLASS. =cut sub new { my ($self, $args) = @_; my $type = ref($self) || $self; my $curproc = new FML::Process::Kernel $args; return bless $curproc, $type; } sub prepare { my ($self, $args) = @_; $self->SUPER::prepare($args); } =head2 C verify the sender is a valid member or not. =cut sub verify_request { my ($curproc, $args) = @_; $curproc->verify_sender_credential(); } =head2 C dispatcher to run correspondig C for C. lock execute FML::Command::command unlock =cut sub run { my ($curproc, $args) = @_; $curproc->_evaluate_command($args); } =head2 C $curproc->inform_reply_messages(); =cut sub finish { my ($curproc, $args) = @_; $curproc->inform_reply_messages(); $curproc->queue_flush(); } sub _pre_scan { my ($curproc, $ra_body) = @_; my $config = $curproc->{ config }; my $keyword = $config->{ confirm_keyword }; my $found = 0; for (@$ra_body) { if (/$keyword\s+\w\s+([\w\d]+)/) { $found = $1; } } return $found; } sub _can_accpet_command { my ($curproc, $args, $opts) = @_; my $config = $curproc->{ config }; my $cred = $curproc->{ credential }; # user credential my $prompt = $config->{ command_prompt } || '>>>'; my $comname = $opts->{ comname }; my $command = $opts->{ command }; # 1. simple command syntax check use FML::Filter::Utils; unless ( FML::Filter::Utils::is_secure_command_string( $command ) ) { LogError("insecure command: $command"); $curproc->reply_message("\n$prompt $command"); $curproc->reply_message_nl('command.insecure', "insecure, so ignored."); return 0; } # 2. use of this command is allowed in FML::Config or not ? unless ($config->has_attribute("available_commands", $comname)) { $curproc->reply_message("\n$prompt $command"); $curproc->reply_message_nl('command.not_command', "not command, ignored."); return 0; } # 3. Even new comer need to use commands [ guide, subscirbe, confirm ]. unless ($cred->is_member($curproc, $args)) { unless ($config->has_attribute("available_commands_for_stranger", $comname)) { $curproc->reply_message("\n$prompt $command"); $curproc->reply_message_nl('command.deny', "not allowed to use this command."); return 0; } else { Log("permit command $comname for stranger"); } } return 1; # o.k. accpet this command. } # Descriptions: parse command buffer to make # argument vector after command name # Arguments: ($string_to_parse, $string_command_name) # Side Effects: none # Return Value: ARRAY REFERENCE sub _parse_command_options { my ($command, $comname) = @_; my $found = 0; my (@options) = (); for (split(/\s+/, $command)) { push(@options, $_) if $found; $found = 1 if $_ eq $comname; } return \@options; } sub _get_command_name { my ($command) = @_; my $comname = (split(/\s+/, $command))[0]; return $comname; } # dynamic loading of command definition. # It resolves your customized command easily. sub _evaluate_command { my ($curproc, $args) = @_; my $config = $curproc->{ config }; my $ml_name = $config->{ ml_name }; my $argv = $config->{ main_cf }->{ ARGV }; my $keyword = $config->{ confirm_keyword }; my $prompt = $config->{ command_prompt } || '>>>'; my $body = $curproc->{ incoming_message }->{ body }->data_in_body_part; my @body = split(/\n/, $body); my $id = $curproc->_pre_scan( \@body ); $curproc->reply_message("result for your command requests follows:"); COMMAND: for my $command (@body) { next if $command =~ /^\s*$/; # ignore empty lines # command = line itsetlf, it contains superflous strings # comname = command name # for example, command = "# help", comname = "help" my $comname = _get_command_name($command); # validate general command except for confirmation unless ($command =~ /$keyword/ && defined($id)) { # we can accpet this command ? my $opts = { comname => $comname, command => $command }; unless ($curproc->_can_accpet_command($args, $opts)) { # no, we do not accept this command. Log("invalid command = $command"); next COMMAND; } } # "confirmation" is exceptional. else { $comname = $keyword; # comname = confirm Log("try $comname <$command>"); $command =~ s/^.*$comname/$comname/; } # o.k. here we go to execute command use FML::Command; my $obj = new FML::Command; if (defined $obj) { $curproc->reply_message("\n$prompt $command"); # arguments to pass off to each method my $command_args = { command_mode => 'user', comname => $comname, command => $command, ml_name => $ml_name, options => _parse_command_options($command, $comname), argv => $argv, args => $args, }; # execute command ($comname method) under eval(). eval q{ $obj->$comname($curproc, $command_args); }; unless ($@) { $curproc->reply_message_nl('command.ok', "ok."); } else { # error trap $curproc->reply_message_nl('command.fail', "fail."); LogError("command ${comname} fail"); if ($@ =~ /^(.*)\s+at\s+/) { my $reason = $1; Log($reason); # pick up reason } } } } # END OF FOR LOOP: for my $command (@body) { ... } } =head1 AUTHOR Ken'ichi Fukamachi =head1 COPYRIGHT Copyright (C) 2001 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::Command appeared in fml5 mailing list driver package. See C for more details. =cut 1;