diff options
| author | fukachan <fukachan> | 2006-06-10 01:05:10 +0000 |
|---|---|---|
| committer | fukachan <fukachan> | 2006-06-10 01:05:10 +0000 |
| commit | 896c1b1dcfb3ec2d24591852f51b51f5a040f7e7 (patch) | |
| tree | 487f6735a7b25e017a84e06971c47551453fc747 /regress/tinymta | |
| parent | 6b5a07fdbff0b60981764f1e8a35d1d8d6fe6d84 (diff) | |
| download | fml8-896c1b1dcfb3ec2d24591852f51b51f5a040f7e7.tar.gz fml8-896c1b1dcfb3ec2d24591852f51b51f5a040f7e7.tar.bz2 fml8-896c1b1dcfb3ec2d24591852f51b51f5a040f7e7.zip | |
Initial revision
Diffstat (limited to 'regress/tinymta')
| -rw-r--r-- | regress/tinymta/.cvsignore | 2 | ||||
| -rw-r--r-- | regress/tinymta/00_README | 25 | ||||
| -rw-r--r-- | regress/tinymta/Config.pm | 35 | ||||
| -rw-r--r-- | regress/tinymta/Drop.pm | 172 | ||||
| -rw-r--r-- | regress/tinymta/Log.pm | 36 | ||||
| -rw-r--r-- | regress/tinymta/Makefile.in | 32 | ||||
| -rw-r--r-- | regress/tinymta/SMTP.pm | 317 | ||||
| -rwxr-xr-x | regress/tinymta/configure | 25 | ||||
| -rwxr-xr-x | regress/tinymta/loader.in | 299 | ||||
| -rw-r--r-- | regress/tinymta/tinymta.cf.in | 11 |
10 files changed, 954 insertions, 0 deletions
diff --git a/regress/tinymta/.cvsignore b/regress/tinymta/.cvsignore new file mode 100644 index 00000000..317e354d --- /dev/null +++ b/regress/tinymta/.cvsignore @@ -0,0 +1,2 @@ +@template +@template.pm diff --git a/regress/tinymta/00_README b/regress/tinymta/00_README new file mode 100644 index 00000000..b9c737aa --- /dev/null +++ b/regress/tinymta/00_README @@ -0,0 +1,25 @@ + + tinymta + +1. 概要 + +fml8 のライブラリを使って作るシンプルな MTA です。 + +半分実益をかねていますが、半分はライブラリのテストみたいなものと考えて +ください。 + +2. 構成 + + /usr/local/libexec/fml/tmdrop + キューへいれる。 + 入力はメール(RFC822)そのもの + + /usr/local/libexec/fml/tmsmtp + キューからメールを一つ選択し SMTP 配送を行なう + + /usr/local/etc/fml/tinymta.cf + tinymta 固有の設定ファイル + + /usr/local/etc/fml/main.cf + fml8 と共通の設定ファイル + diff --git a/regress/tinymta/Config.pm b/regress/tinymta/Config.pm new file mode 100644 index 00000000..823678db --- /dev/null +++ b/regress/tinymta/Config.pm @@ -0,0 +1,35 @@ +#-*- perl -*- +# +# Copyright (C) 2006 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$ +# + +package TinyMTA::Config; +use strict; +use Carp; + +# Descriptions: load configuration from $config_cf_file. +# Arguments: STR($config_cf_file) OBJ($main_cf) +# Side Effects: none +# Return Value: OBJ +sub load_file +{ + my ($config_cf_file, $main_cf) = @_; + + my $opts = {}; + for my $k (keys %$main_cf) { + my $key = sprintf("fml_%s", $k); + $opts->{ $key } = $main_cf->{ $k }; + } + + use FML::Config; + my $config = new FML::Config $opts; + $config->load_file($config_cf_file); + return $config; +} + + +1; diff --git a/regress/tinymta/Drop.pm b/regress/tinymta/Drop.pm new file mode 100644 index 00000000..5e87945d --- /dev/null +++ b/regress/tinymta/Drop.pm @@ -0,0 +1,172 @@ +#-*- perl -*- +# +# Copyright (C) 2006 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: @template.pm,v 1.10 2006/01/07 13:16:41 fukachan Exp $ +# + +package TinyMTA::Drop; +use strict; +use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD $global_counter); +use Carp; + +=head1 NAME + +TinyMTA::Drop - mail drop wrapper + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +=head1 METHODS + +=head2 new() + +constructor. + +=cut + + +# Descriptions: constructor. +# Arguments: OBJ($self) OBJ($config) +# Side Effects: +# Return Value: none +sub new +{ + my ($self, $config) = @_; + my ($type) = ref($self) || $self; + my $me = { _config => $config }; + return bless $me, $type; +} + + +# Descriptions: main routine. +# Arguments: OBJ($self) +# Side Effects: none +# Return Value: none +sub run +{ + my ($self) = @_; + my $config = $self->{ _config }; + + # 1. prepare queue directory. + my $queue_dir = $config->{ queue_dir }; + unless (-d $queue_dir) { + mkdir $queue_dir, 0720; + if (-d $queue_dir) { + $self->log("$queue_dir created"); + } + } + + # 2. drop the message taken from STDIN into a new queue file. + my ($qid, $qf, $qtmp) = $self->queue_filename(); + my $wh = new FileHandle "> $qtmp"; + if (defined $wh) { + my $buf; + + $wh->autoflush(1); + LINE: + while ($buf = <STDIN>) { + print $wh $buf; + } + $wh->close(); + } + + if (-s $qtmp) { + if (rename($qtmp, $qf)) { + $self->log("queue-in: $qid"); + } + else { + $self->logerror("cannot create $qf"); + croak("cannot create $qf\n"); + } + } +} + + +# Descriptions: retrun a new queue file name. +# Arguments: OBJ($self) +# Side Effects: none +# Return Value: ARRAY(STR, STR, STR) +sub queue_filename +{ + my ($self) = @_; + my $config = $self->{ _config }; + my $queue_dir = $config->{ queue_dir }; + + use FileHandle; + my $qid = sprintf("%d.%d.%d", time, $$, ++$global_counter); + my $new = File::Spec->catfile($queue_dir, $qid); + my $tmp = File::Spec->catfile($queue_dir, ",$qid"); + + return($qid, $new, $tmp); +} + + +# Descriptions: log as normal level. +# Arguments: OBJ($self) STR($msg) +# Side Effects: none +# Return Value: none +sub log +{ + my ($self, $msg) = @_; + &TinyMTA::Log::log($msg); +} + + +# Descriptions: log as error level. +# Arguments: OBJ($self) STR($msg) +# Side Effects: none +# Return Value: none +sub logerror +{ + my ($self, $msg) = @_; + &TinyMTA::Log::log("error: $msg"); +} + + +###################################################################### +# +# dispatcher +# + +# Descriptions: main dispatcher. +# Arguments: OBJ($main_cf) STR($config_cf_file) +# Side Effects: none +# Return Value: none +sub main::dispatch +{ + my ($main_cf, $config_cf_file) = @_; + + my $config = TinyMTA::Config::load_file($config_cf_file, $main_cf); + my $obj = new TinyMTA::Drop $config; + $obj->run(); +} + + +=head1 CODING STYLE + +See C<http://www.fml.org/software/FNF/> on fml coding style guide. + +=head1 AUTHOR + +Ken'ichi Fukamachi + +=head1 COPYRIGHT + +Copyright (C) 2006 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 + +TinyMTA::Drop appeared in fml8 mailing list driver package. +See C<http://www.fml.org/> for more details. + +=cut + + +1; diff --git a/regress/tinymta/Log.pm b/regress/tinymta/Log.pm new file mode 100644 index 00000000..d3d49404 --- /dev/null +++ b/regress/tinymta/Log.pm @@ -0,0 +1,36 @@ +#-*- perl -*- +# +# Copyright (C) 2006 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$ +# + +package TinyMTA::Log; +use strict; +use Carp; + +# Descriptions: send log message by syslog(3). +# Arguments: STR($msg) +# Side Effects: none +# Return Value: OBJ +sub log +{ + my ($msg) = @_; + + use File::Basename; + my $myname = basename($0); + my $ident = "tinymta/$myname"; + my $logopt = "pid"; + my $facility = "local0"; + my $priority = "info"; + + use Sys::Syslog; + openlog($ident, $logopt, $facility); + syslog($priority, $msg); + closelog(); +} + + +1; diff --git a/regress/tinymta/Makefile.in b/regress/tinymta/Makefile.in new file mode 100644 index 00000000..137b33b5 --- /dev/null +++ b/regress/tinymta/Makefile.in @@ -0,0 +1,32 @@ +# +# $FML$ +# + +LIBS = @LIBS@ + +CC = @CC@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ + +INSTALLCMD = @INSTALL@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +mandir = @mandir@ +config_dir = @fmlconfdir@ +libexec_dir = @libexecdir@/fml + + +install: + test -f $(config_dir)/tinymta.cf ||\ + $(INSTALLCMD) -c -m 0644 tinymta.cf $(config_dir) + $(INSTALLCMD) -c -m 0755 tmdrop $(libexec_dir) + $(INSTALLCMD) -c -m 0755 tmsmtp $(libexec_dir) + +clean: + rm -f *~ + rm -f Makefile + rm -f loader tmdrop tmsmtp + rm -f tinymta.cf + diff --git a/regress/tinymta/SMTP.pm b/regress/tinymta/SMTP.pm new file mode 100644 index 00000000..2abf2470 --- /dev/null +++ b/regress/tinymta/SMTP.pm @@ -0,0 +1,317 @@ +#-*- perl -*- +# +# Copyright (C) 2006 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: @template.pm,v 1.10 2006/01/07 13:16:41 fukachan Exp $ +# + +package TinyMTA::SMTP; +use strict; +use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD); +use Carp; + +=head1 NAME + +TinyMTA::SMTP - smtp + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +=head1 METHODS + +=head2 new() + +constructor. + +=cut + + +# Descriptions: constructor. +# Arguments: OBJ($self) OBJ($config) +# Side Effects: +# Return Value: none +sub new +{ + my ($self, $config) = @_; + my ($type) = ref($self) || $self; + my $me = { _config => $config }; + return bless $me, $type; +} + + +# Descriptions: main routine. +# Arguments: OBJ($self) +# Side Effects: none +# Return Value: none +sub run +{ + my ($self) = @_; + my $config = $self->{ _config }; + + my $q_list = $self->pickup_queue(); + for my $q (@$q_list) { + $self->log("try to send: $q"); + $self->send($q); + } +} + + +# Descriptions: pick up queue (id's) and return it as ARRAY_REF. +# Arguments: OBJ($self) +# Side Effects: none +# Return Value: ARRAY_REF +sub pickup_queue +{ + my ($self) = @_; + my $config = $self->{ _config }; + + my (@queue) = (); + + use DirHandle; + my $queue_dir = $config->{ queue_dir }; + my $dh = new DirHandle $queue_dir; + if (defined $dh) { + my $entry; + + ENTRY: + while ($entry = $dh->read()) { + next ENTRY if $entry =~ /^\./o; + next ENTRY if $entry =~ /^\,/o; + next ENTRY if $entry =~ /^\_/o; + next ENTRY if $entry !~ /^\d/o; + + push(@queue, $entry); + } + + $dh->close(); + } + else { + $self->logerror("cannot open $queue_dir"); + croak("cannot open $queue_dir"); + } + + return \@queue; +} + + +# Descriptions: send queue. +# Arguments: OBJ($self) +# Side Effects: queue is removed if succeeded. +# Return Value: none +sub send +{ + my ($self, $q) = @_; + my $qf_candidate = $self->queue_file_path($q); + my $qf_locked = $self->queue_file_path("_$q"); + + if (rename($qf_candidate, $qf_locked)) { + $self->_send_file($qf_locked); + } + else { + $self->logerror("cannot lock queue: $q"); + } +} + + +# Descriptions: return full path for queue id. +# Arguments: OBJ($self) STR($qid) +# Side Effects: none +# Return Value: STR +sub queue_file_path +{ + my ($self, $qid) = @_; + my $config = $self->{ _config }; + my $queue_dir = $config->{ queue_dir }; + + use File::Spec; + return File::Spec->catfile($queue_dir, $qid); +} + + +# Descriptions: send $queue_file by Mail::Delivery. +# Arguments: OBJ($self) HASH_REF($args) +# Side Effects: queue removed if suceeded. +# Return Value: none +sub _send_file +{ + my ($self, $queue_file) = @_; + my $config = $self->{ _config }; + my $queue_dir = $config->{ queue_dir }; + + use Mail::Message; + my $message = Mail::Message->parse( { file => $queue_file } ); + + my ($sender, $rcpt_maps) = $self->_analyze_message($message); + + use Mail::Delivery::Queue; + my $queue = new Mail::Delivery::Queue { directory => $queue_dir }; + + my $validater = sub { + my ($address) = @_; + use FML::Restriction::Base; + my $restriction = new FML::Restriction::Base; + return $restriction->regexp_match( 'address', $address ); + }; + + use Mail::Delivery; + my $logfp_normal = sub { $self->log(@_); }; + my $logfp_error = sub { $self->log(@_); }; + my $service = new Mail::Delivery { + log_info_function => $logfp_normal, + log_error_function => $logfp_error, + log_debug_function => undef, + smtp_log_function => undef, + smtp_log_handle => undef, + address_validate_function => $validater, + }; + if ($service->error) { + # log($service->error); + croak("cannot initialize Mail::Delivery object"); + } + + $service->deliver({ + 'smtp_servers' => $config->{'smtp_servers'}, + + 'smtp_sender' => $sender, + 'recipient_array' => $rcpt_maps, + 'recipient_limit' => $config->{smtp_recipient_limit}, + + 'message' => $message, + + queue => $queue, + + # XXX do not need fallback here ? + use_queue_dir => 1, + queue_dir => $queue_dir, + }); + if ($service->error) { + $self->logerror($service->error); + croak($service->error); + } + + # delivery not completes. + if ($service->get_not_done()) { + $self->logerror("delivery not done"); + croak("delivery not done"); + } + + # done. + unlink $queue_file; + unless (-f $queue_file) { + use File::Basename; + my $qid = basename($queue_file); + $qid =~ s/^_//; + $self->log("$qid removed"); + } +} + + +# Descriptions: analyze message and return sender and recipients info. +# Arguments: OBJ($self) STR($msg) +# Side Effects: none +# Return Value: ARRAY(STR, ARRAY_REF) +sub _analyze_message +{ + my ($self, $msg) = @_; + my $header = $msg->whole_message_header(); + + # results + my ($sender) = ''; + my ($rcpt_maps) = []; + + { + my $from = $header->get('from'); + use Mail::Address; + my (@addrlist) = Mail::Address->parse($from); + if (defined $addrlist[0]) { + $sender = $addrlist[0]->address; + } + } + + { + my $to = $header->get('to') || ''; + my $cc = $header->get('cc') || ''; + my $bcc = $header->get('bcc') || ''; + use Mail::Address; + my (@addrlist) = Mail::Address->parse("$to, $cc, $bcc"); + if (defined $addrlist[0]) { + for my $a (@addrlist) { + if ($a->address) { + push(@$rcpt_maps, $a->address); + } + } + } + } + + return($sender, $rcpt_maps); +} + + +# Descriptions: log as normal level. +# Arguments: OBJ($self) STR($msg) +# Side Effects: none +# Return Value: none +sub log +{ + my ($self, $msg) = @_; + &TinyMTA::Log::log($msg); +} + + +# Descriptions: log as error level. +# Arguments: OBJ($self) STR($msg) +# Side Effects: none +# Return Value: none +sub logerror +{ + my ($self, $msg) = @_; + &TinyMTA::Log::log("error: $msg"); +} + + +###################################################################### +# +# dispatcher +# + +# Descriptions: main dispatcher. +# Arguments: OBJ($main_cf) STR($config_cf_file) +# Side Effects: none +# Return Value: none +sub main::dispatch +{ + my ($main_cf, $config_cf_file) = @_; + + my $config = TinyMTA::Config::load_file($config_cf_file, $main_cf); + my $obj = new TinyMTA::SMTP $config; + $obj->run(); +} + + +=head1 CODING STYLE + +See C<http://www.fml.org/software/FNF/> on fml coding style guide. + +=head1 AUTHOR + +Ken'ichi Fukamachi + +=head1 COPYRIGHT + +Copyright (C) 2006 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 + +TinyMTA::SMTP appeared in fml8 mailing list driver package. +See C<http://www.fml.org/> for more details. + +=cut + + +1; diff --git a/regress/tinymta/configure b/regress/tinymta/configure new file mode 100755 index 00000000..78470e1e --- /dev/null +++ b/regress/tinymta/configure @@ -0,0 +1,25 @@ +#!/bin/sh +# +# $FML$ +# + + +# assert +if [ ! -x ../../config.status ];then + echo "error: run configure at the top level directory (../..)" + echo " before running configure here" + exit 1 +fi + +env CONFIG_FILES='Makefile loader tinymta.cf' sh ../../config.status +chmod 755 loader + +echo creating tmdrop +cat loader Config.pm Log.pm Drop.pm > tmdrop +chmod 755 tmdrop + +echo creating tmsmtp +cat loader Config.pm Log.pm SMTP.pm > tmsmtp +chmod 755 tmsmtp + +exit 0; diff --git a/regress/tinymta/loader.in b/regress/tinymta/loader.in new file mode 100755 index 00000000..a2c829f8 --- /dev/null +++ b/regress/tinymta/loader.in @@ -0,0 +1,299 @@ +#! @PERL@ -w +#-*- perl -*- +# +# Copyright (C) 2000-2002,2004,2006 Ken'ichi Fukamachi +# All rights reserved. +# +# $FML: loader.in,v 1.13 2004/10/06 13:33:27 fukachan Exp $ +# + +eval 'exec @PERL@ -S $0 ${1+"$@"}' + if $running_under_some_shell; + +use vars qw($running_under_some_shell $hints $ERROR_EXIT_CODE); +use strict; +use IO::File; + +# reset PATH in the early stage +$ENV{'PATH'} = '/bin:/usr/bin'; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# XXX irregular global variable permitted to handle emergency cases. +# default exit code in error. +$ERROR_EXIT_CODE = 1; + +=head1 NAME + +loader -- top level wrapper to load and start a real fml program. + +=head1 SYNOPSIS + +loader C<[-c main.cf]> [program specific options] + +=head1 DESCRIPTION + +Perl modules C<fml> uses are dependent on fml version. +C<loader> resolves fml version dependence by +@sysconfdir@/main.cf, +set up proper @INC and load C<FML::Process::Switch>. + +See C<FML::Process::Switch> for boot strap phase 2 of fml process. + +=head1 COMMAND LINE OPTIONS + +C<-c main.cf> + main.cf alternative + +=head1 METHOD + +=head2 Bootstrap( main_cf ) + +top level loader. + +=cut + + +# Descriptions: top lebel bootstrap program +# which load a dispather program (process_switch) +# for process switch. The flow of execution follows: +# libexec/loader -> +# libexec/process_switch -> +# FML::Process::Something +# Arguments: none +# XXX this program sees $0 +# (program name, == argv[0] of C language) +# Side Effects: switch to the real process +# Return Value: none +sub init +{ + my ($main_cf_default, $config_cf_file) = @_; + + # 1. main.cf exists and I can open it? + unless (-f $main_cf_default) { + __CROAK("cannot find $main_cf_default"); + } + my $fh = new IO::File $main_cf_default, "r"; + unless (defined $fh) { + __CROAK("cannot open $main_cf_default"); + } + + # 1.1 parse command line options (preliminary). + # we check @ARGV again after by getopt(). + my $main_cf_file = $main_cf_default; # main.cf by default + for (my $i = 0; $i <= $#ARGV; $i++) { + # -c main.cf + if ($ARGV[ $i ] =~ /^\-c$/) { + $main_cf_file = $ARGV[$i + 1]; + } + } + + # 2.1 o.k. try to load main.cf (1st pass) to resolve @INC + my $main_cf = loader_read_main_cf($main_cf_file); + + # 2.1.1 set up @INC to load FML::Process::Switch + if (defined $main_cf->{ lib_dir }) { + push(@INC, split(/\s+/, $main_cf->{ lib_dir })); + } + else { + __CROAK("\$lib_dir not defined in main.cf"); + } + + # 2.1.2 inherit some parameters to change behaviour + $main_cf->{ _hints } = $hints; + + # arguments to pass off to bootstrap(). + return ($main_cf, $config_cf_file); +} + + +# Descriptions: dispatch. +# Arguments: OBJ($main_cf) STR($config_cf_file) +# Side Effects: none +# Return Value: none +sub bootstrap +{ + my ($main_cf, $config_cf_file) = @_; + + # 3. execute + eval { + main::dispatch($main_cf, $config_cf_file); + }; + if ($@) { + my $reason = $@; + $reason =~ s/[\n\s]*\s+at\s+.*$//m; + __CROAK("cannot load FML::Process::Switch", $reason); + } +} + + +=head2 loader_read_main_cf(cf_file) + +load "key = value" style configuration file and build a hash. +return HASH REFERENCE. + + my $main_cf = loader_read_main_cf($main_cf_file, $params); + +where $param is optional. + +=cut + + +# Descriptions: load "key = value" style configuration. +# It is available to use the following style. +# key = value1 value2 +# value3 +# XXX This file is non-Object Oriented style but +# XXX this is minimum module used in standalone program. +# Arguments: $file $params +# $params is 'key1=value1 key2=value2' syntax. +# Side Effects: $config (hash reference) is allocated on memory here. +# Return Value: hash reference to configuration parameters +sub loader_read_main_cf +{ + my ($file) = @_; + my $config = {}; + + my $fh = new IO::File $file, "r"; + + if (defined $fh) { + my $curkey = ''; + while (<$fh>) { + next if /^\#/; + chomp; + + if (/^([A-Za-z]\w+)\s+=\s*(.*)/) { + my ($key, $value) = ($1, $2); + $curkey = $key; + $config->{$key} = $value; + } + if (/^\s+(.*)/) { + $config->{ $curkey } .= " ". $1; + } + } + $fh->close; + } + else { + __CROAK("Error: cannot open $file"); + } + + loader_expand_variables( $config ); + return $config; +} + + +# Descriptions: expand $var to the value of $var. +# Arguments: $ref_to_config +# Side Effects: rewrite the given $config +# Return Value: none +sub loader_expand_variables +{ + my ($config) = @_; + my (@order) = keys %$config; + + # check whether the variable definition is recursive. + # For example, definition "var_a = $var_a/b/c" causes a loop. + for my $x ( @order ) { + if ((defined $x) && defined ($config->{ $x })) { + if ($config->{ $x } =~ /\$$x/) { + __CROAK("loop1: definition of $x is recursive"); + } + } + } + + # main expansion loop + my $org = ''; + my $max = 0; + KEY: + for my $x ( @order ) { + next KEY unless defined($config->{ $x }); + next KEY if $config->{ $x } !~ /\$/o; + + # we need a loop to expand nested variables, for example, + # a = $x/y and b = $a/c/0 + # + $max = 0; + EXPANSION_LOOP: + while ($max++ < 16) { + $org = $config->{ $x }; + + if ($config->{ $x } =~ /\{/) { # expand ${prefix}/xxx ... + $config->{ $x } =~ s/\$\{([a-z_]+)\}/$config->{$1}/g; + } + $config->{ $x } =~ s/\$([a-z_]+)/$config->{$1}/g; + + last EXPANSION_LOOP if $config->{ $x } !~ /\$/o; + last EXPANSION_LOOP if $org eq $config->{ $x }; + + if ($config->{ $x } =~ /\$$x/) { + __CROAK("loop2: definition of $x is recursive"); + } + } + + if ($max >= 16) { + __CROAK("variable expansion of $x causes infinite loop"); + } + } +} + + +# Descriptions: print error reason +# Arguments: STR($reason) STR($detail) +# Side Effects: print out error reason and exit here +# Return Value: none +sub __CROAK +{ + my ($reason, $detail) = @_; + + print STDERR "fml loader error: $reason\n"; + print STDERR " reason(detail): $detail\n" if defined $detail; + exit($ERROR_EXIT_CODE); +} + + + +# +# MAIN +# + +# main routine to boot off +my (@argv) = (); +BEGIN { + my $prefix = "@prefix@"; + my $exec_prefix = "@exec_prefix@"; + my $main_cf = "@fmlconfdir@/main.cf"; + my $config_cf = "@fmlconfdir@/tinymta.cf"; + + (@argv) = init($main_cf, $config_cf); +} + +# not use Carp.pm to be quiet if needed +eval q{ bootstrap(@argv); }; +if ($@) { print STDERR "Error: ", $@, "\n"; exit($ERROR_EXIT_CODE);} + +exit(0); + + +=head1 SEE ALSO + +L<FML::Process::Switch> + +=head1 AUTHOR + +Ken'ichi Fukamachi + +=head1 COPYRIGHT + +Copyright (C) 2000-2002,2004,2006 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 + +libexec/loader appeared in fml8 mailing list driver package. +See C<http://www.fml.org/> for more details. + +=cut + + +1; diff --git a/regress/tinymta/tinymta.cf.in b/regress/tinymta/tinymta.cf.in new file mode 100644 index 00000000..f533b18b --- /dev/null +++ b/regress/tinymta/tinymta.cf.in @@ -0,0 +1,11 @@ +# +# $FML$ +# + + +queue_dir = /var/tmp/tinymta + +smtp_servers = 127.0.0.1:25 + +smtp_recipient_limit = 1000 + |
