####
####   PPML ver.0.753 --- "Pigeon Post" Mail-list System
####   Copyright (c) 1999 by Kazunori ANDO all rights reserved.
####
####   http://www.kk.iij4u.or.jp/~ando/ppml/README
#### 
#### This program is free software; you can redistribute it and/or
#### modify it under the terms of the GNU General Public License
#### as published by the Free Software Foundation; either version 2
#### of the License, or (at your option) any later version.
#### 
#### This program is distributed in the hope that it will be useful,
#### but WITHOUT ANY WARRANTY; without even the implied warranty of
#### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#### GNU General Public License for more details.
#### 
#### You should have received a copy of the GNU General Public License
#### along with this program; if not, write to the Free Software
#### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#### 02111-1307, USA.
####

INDEX


ChangeLog

2003.04.25 0.753
Bugfix/UnlockDone removed from sitedef.ph
2003.04.12 0.752
Add/ppmilter.pl
2002.09.02 0.751
Add/Variable to save Received header(requested by pigeon-ml)
Add/Config to save Return-Path header(requested by pigeon-ml)
2002.08.30 0.750
Improve/exit code(suggested by moton)
Improve/logic around flock(suggested by minmin)
2002.04.11 0.740
Add/ppml-db patch(Config/Members are handled by DBI/PostgreSQL(contributed by koyama)
Add/PPML_VERSION and PPSERV_VERSION variables are set.(requested by minmin)
Add/approve chirm command to show ppml version(requested by naoto)
2002.04.05 0.736
Add/NOTICE_TO_POSTER to control NOT MEMBER notice to poster(suggested by minmin)
2002.03.14 0.735
Bug fix/Pick-up for boundary of MIME-multipart(suggested by marushin)
2002.03.11 0.734
Bug fix/Pick-up for boundary of MIME-multipart to avoid white space
2002.02.04 0.733
Bug fix/Pick-up for boundary of MIME-multipart
2002.01.29 0.732
Bug fix/Calling point of CheckVirus
2002.01.14 0.731
Change message handling in CheckCache(to avoid reject-loop)
Add/Rejection rule for legal SPAM
2001.12.06 0.730
Improve/Message handling in CheckVirus
2001.11.29 0.724
2001.11.29 0.723
2001.11.26 0.722
Improve/Against W32.Badtrans.B@mm
2001.08.11 0.721
Bugfix/DeletePart
2001.07.31 0.720
Improve/timeout around flock
Improve/MIME multipart analysis of 2nd layer
2001.07.14 0.711
Add/New IPv6 patch
2001.07.11 0.710
Add/SESSIONMAXRCPTS in sitedef.ph
2001.05.25 0.702
Improve/Address form limitation in members.ph
2001.02.11 0.701
Improve/around SMTP
2001.02.09 0.70
Add/CGI-interface for OPTIMIZED_DELIVERLIST
2001.01.31 0.606
Add/Analyze MIME-type multipart/alternative
Bug Fix/DeletePart
Bug Fix/filetype judgement in sitedef.ph
2001.01.15 0.605
Add/OPTIMIZED_SKIP option
Bug Fix/DELIVERLIST handling
2001.01.09 0.604
Add/anti-alias of list for OPTIMIZED_DELIVERLIST
Bug Fix/Auth mechanism
2001.01.04 0.603
Add/OPTIMIZED_DELIVERLIST
2000.12.27 0.602
Bug fix/USERCOMMPERM handling(3)
2000.10.27 0.601
Bug fix/add UnlockDone2 for aborting the process
2000.10.09 0.60
Bug fix/USERCOMMPERM handling if setting isn't in config.ph
Bug fix/password handling in generate command
2000.09.30 0.599
Improve/SMTP handling in Distribute process
2000.09.11 0.597
Bug Fix/use UnlockDone in sitedef.ph
2000.09.08 0.596
Bug Fix/USERCOMMPERM handling(2)
Bug Fix/use UnlockDone in sitedef.ph
2000.09.06 0.595
Bug Fix/USERCOMMPERM handling
2000.09.03 0.594
Add/END command
2000.08.26 0.593
Add/Selection of help file
2000.08.23 0.592
Bug fix/How to devide addresses
2000.08.18 0.591
Bug fix/Inconsistency around the variable name USERCOMMPERM
2000.08.10 0.59
Add/approve get command
Add/Setting to surpress user commands
Improve/Makefile to save old settings when make update
2000.07.31 0.582
Bug Fix/Recognition of entries for GET/DELETE command
Improve/Subject of notice mail for ON command completed
2000.07.25 0.581
Add/approve welcome command
2000.07.25 0.580
Bug Fix/message format of SIZE-LIMIT(MIME w/o multipart)
2000.07.22 0.579
Add/Setting against too long Date header attack to Microsoft Outlook
2000.07.20 0.578
Bug Fix/message format of SIZE LIMIT NOTICE
2000.07.15 0.577
Improve/cd is now default instead of chdir
Add/NOARCHIVE setting to config.ph
Improve/Behavior if no member exist
2000.07.09 0.576
Improve/for "bash on RedHat don't have chdir"
2000.07.06 0.575
Bug Fix/problem "Makefile don't install ppvirus.ph" fixed.
2000.05.23 0.574
Add/Setting against all types of file used for virus attachment
2000.05.08 0.573
Add/Setting against .vbe/.wsh etc. attachment
2000.05.06 0.572
Add/Setting against ILOVEYOU and other VBscript attachment
If you don't need to distribute .vbs file on your ppml, you can
use this setting by uncommenting several lines in sitedef.ph.
2000.05.05 0.571
Add/Setting against ILOVEYOU virus
Pattern match on subject and body to reject ILOVEYOU.
2000.04.03 0.57
BugFix/passwd command
Add/IPv6 patch (contributed by yugawa)
2000.01.14 0.56
Add/Delete multiple space in Subject (suggested by minmin)
2000.01.03 0.55(Y2KCC/JP memorial version)
Add/Continued Line in command(suggested by akiko in Y2KCC/JP)
Add/Address-Form check in approve subscribe proccessing(suggested by minmin in Y2KCC/JP)
1999.12.26 0.54
BugFix/Makefile
Improve/MIME-multipart analyze
Add/Against torojan binary by digital-signature of each parts
Add/sigcheck.pl
1999.12.10 0.53
Improve/To avoid DSN/MDN storm, delete these header in sitedef.ph
Improve/Message added for generate command when $AUTOGENERATE is not set.
1999.11.29 0.52
Improve/Check members.ph before subscribe processing
1999.11.28 0.51
Add/Error Mail Handler(ppeh.pl)
1999.11.21 0.50
Improve/Check existence of Digest::SHA1 in Makefile
Change/Wait parameter
1999.11.18 0.49
Add/IsAuthPoster set $e{'poster'}
1999.11.16 0.48
Add/Against 1-line Command posting
1999.11.12 0.47
Add/Against BubbleBoy virus
1999.11.07 0.46
Improve/Error handling of AutoGenerate
Add/Help files in English
1999.11.03 0.45
Bug Fix/Error in IsAuthPoster (suggested by minmin)
Change/License agreement
1999.11.01 0.44
ADD/AUTOGENERATE feature (thanks to seirios)
1999.10.19 0.43
Change/IsAuthPoster take @LIST as a parameter (suggested by sat,minmin)
1999.10.17 0.421
Bug Fix/a sed flag on Makefile (suggested by sat)
1999.10.17 0.42
Change/Devide help to help and help-admin (suggested by moton)
Bug Fix/avoid "Illegal Command" when command-list include empty-line (suggested by moton)
Change/Mailbox "list-admin" changed to "list-request" for RFC2142 complience (suggested by minmin)
1999.10.13 0.41
Change/Devide IsAuthPoster from AuthPoster (suggested by moton,minmin)
1999.09.18 0.40
Change/Change field-name key format of headers (suggested by minmin)
1999.09.16 0.39
Change/send back articles as-is for get command (suggested by minmin)
1999.09.07 0.386
Change/check ppml.html (thanks to minmin)
Change/check ppml-e.html
1999.09.07 0.385
Change/avoid to add white space after Subject tag (thanks to minmin)
1999.09.06 0.384
Add/$ML_SERVER_ADMIN setting
1999.09.03 0.383
Add/English version of README
1999.08.30 0.382
BUG FIX/not reply for user's command
BUG FIX/hash handling of approve subscribe command (suggested by minmin)
1999.08.28 0.381
Change/locking reduced two methods
1999.08.28 0.38
Add/locking method for flock emulateed by fctrl (for Solaris)
1999.08.27 0.37
Change/locking method with lockfile and link (thanks to minmin)
1999.08.26 0.36
Add/locking method with lockfile
1999.08.24 0.35
Change/optimize file reading
Change/add a white space in signature of Command Report
1999.08.18 0.34
Change/sort commands
BUG FIX/file reading
ADD/ppml.html (converted from README by minmin:)
1999.08.16 0.33
Change/treat addresses in lower case
Change/optimize poster authorization
1999.07.30 0.325
Change/unified authorization process between ppml.pl and ppserv.pl
1999.07.25 0.324
BUG FIX/fix Subject generation in sitedef.ph
1999.07.25 0.323
BUG FIX/Makefile
1999.07.22 0.322
BUG FIX/typo in variable name
1999.07.21 0.321
BUG FIX/typo in sitedef.ph
1999.07.20 0.32
BUG FIX/disappearing headers when transaction devided (thanks to not@iri.co.jp)
Change/message of confirmation completed
Change/move &DefaultMakeHeader to sitedef.ph
1999.07.20 0.31
Change/to set default of header generation, write only one line;
sub MakeHeader { &DefaultMakeHeader(@_); }
1999.07.20 0.30
BUG FIX/absense of Subject
1999.07.19 0.291
Add/detailed logging
1999.07.19 0.29
Change/fix GECOS trouble from KANJI chars
1999.07.19 0.28
Change/fix GECOS trouble from "'" char
1999.07.18 0.27
BUG FIX/in SendInfo routine (thanks to not@iri.co.jp)
Change/pass analysis of multipart if $MAXPARTSIZE is not defined(thanks to minmin@wide.ad.jp)
1999.07.18 0.26
BUG FIX/typo in variables (thanks to sat@tokyonet.gr.jp)
1999.07.17 0.26
Add/limitation of deliver size
Add/limitation of one part size in multipart message
Add/&DeletePart routine
1999.07.16 0.25
BUG FIX/loopcheck
1999.07.16 0.25
Add/Kanji GECOS
Add/explaination of trusted users in README
1999.07.16 0.24
BUG FIX/regular expression for addresses (thanks to not@iri.co.jp)
Change/logging failure of delivering
Change/format of SHA1 and MD5 digest to base64
1999.07.15 0.23
Add/using SHA1 and MD5 digest with CPAN's Digest-MD5-2.07 module
1999.07.11 0.22
Add/example setting in skel/config.ph
Change/checksum is now bodysize-checksum
1999.07.11 0.21
Add/Makefile itself
1999.07.09 0.20
Change/address convert to lower cases
Change/optimize who command
Change/optimize On,Off command
BUG FIX/&AuthMember in ppserv.pl
1999.07.08 0.19
Add/no authorization for poster if @MEMBERLIST is not defined
Change/authorization by lower cased addresses
1999.07.06 0.17
Add/approve passwd
1999.07.06 0.16
BUG FIX/typo in variables
1999.07.05 0.15
Add/auto subscription
1999.07.05 0.14
Change/use &NoticeMail when authorization failed
1999.07.05 0.13
Add/pipe based delivering
1999.07.05 0.12
BUG FIX/approve newinfo
1999.07.05 0.11
BUG FIX/find sitedef.ph
Change/instalation
1999.07.04 0.1
test release

Introduction

Contributors in alpha testing period

Thanks a lot to minmin@matsui.co.jp, sat@tokyonet.gr.jp and other kind persons:)

Copyright and License(1999.11.03)

Copyright(c)1999 Kazunori ANDO(ando@kk.iij4u.or.jp) all rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

This package include mime_pls and Digest-MD5-2.07 packages.

Naming of this software

The name of "Pigeon Post" come from first impression of this software. It was named by Mr. Masato MINDA(minmin@wide.ad.jp) in an IRC channel.


About PPML style

Policy

"I can't use unreadable code, therefore I write it simply." Of course, this software is written from scratch. Simple code, safety enough to stop looping, sure processing of fundamental functions as ML driver are important. Its system of command is similar to majordomo, but slightly different. You can customize it completely if you can use PERL language.

Advantage of PPML

Members-list and administrators-list are defined as a hash in .ph files. We can avoid time-loss from linear-search such as poster authorization and get more readable code by using hash. It is impossible to subscribe two same addresses. The hash is also used to record administrator's password and member's GECOS information. The GECOS information is used for who command, because the list of address can be used for SPAM.

To avoid looping/bombing, PPML checks Message-Id and checksum of text part in posted article. Therefore, you can't post the same text to the ML if you don't clear pp.cache file. And powerful logging catch the number of rcpts and delivery status in SMTP.

Now PPML has easy virus checker. Please check virus-mail by sigcheck.pl, add entry to ppvirus.ph, and feedback us the output.

Now PPML is IPv6 ready. A patch from Mr.Takahiro Yugawa is in the "patches" directory of ppml source. You will be able to use ppml in IPv6 environment as well as IPv4.

After 0.603, PPML can handle an enhanced format of member-list in order to avoid multiple emails posted to some lists at once. The new format of concentrated member-list is shown below:

%members = (
    'user1@enkai.org' => ['Kazunori ANDO','info','# staff','support',],
    'user2@hitokai.com' => ['Kazunori ANDO','info','support',],
    'user3@kk.iij4u.or.jp' => ['Kazunori ANDO','support','staff',],
);
1;

The priority of delivering is maximum at the left side of these array. You can use this format by the setting in each config.ph:

### Optimized $OPTIMIZED_DELIVERLIST = 1; @MEMBERLIST = ('../opt-members.ph','admins.ph'); @DELIVERLIST = ('../opt-members.ph');

and you can set anti-alias of the list in config.ph. PPML commands are available even if you use this format of list.

Environment

PPML worked on perl5.005_03. And it need a SMTP relaying host (at least localhost:) or the sendmail program.

Treat of Data

main routine

##### main #####
&ReadSiteConfig;     # read sitedef.ph
&ChdirByArg;         # chdir to ppml.pl's argument in "include" file
&PPLockWait;         # lock
&ReadConfig;         # read config.ph
&ParseInput(*e);     # read STDIN and set %e hash
&CheckCache(*e);     # check checksum and Message-Id
&AuthPoster(*e);     # poster authorization
&IncrSeqNum(*e);     # generate seq-number
&MIMEdecode(*e);     # MIME decoding for KANJI headers
&MakeHeader(*e);     # generate headers for deliver
#&DebugPrint(*e);    # not permitted :-p
&MIMEencode(*e);     # MIME encoding for KANJI headers
&SaveAsFile(*e) unless ($NOARCHIVE);     # save article
&Distribute(*e);     # deliver in SMTP or execute commands
&UnlockDone;         # unlock
################

Process of &ParseInput(*e)

article -> $e{'Body'}   -> $e{'checksum:body'},
                          $e{'size:body'},
                          $e{'line:body'},
                          # Case of MIME multipart message
                          $e{'mp:00'},           # number of parts
                          $e{'mp:00.01'},        # part 1
                          $e{'mhead:mp:00.01'}   # MIME header of part 1
                          $e{'mbody:mp:00.01'}   # Body of part 1
                          $e{'size:mp:00.01'},   # size of part 1
                          $e{'checksum:mp:00.01'}# digital-signature of part 1
                          $e{'mtype:mp:00.01'}   # MIME-type of part 1
                          $e{'mchar:mp:00.01'}   # charset of part 1
                          $e{'mcode:mp:00.01'}   # encoding of part 1
                          $e{'mname:mp:00.01'}   # filename of part 1
                          $e{'mp:00.02'},        # part 2
                          $e{'mhead:mp:00.02'}   # MIME header of part 2
                          $e{'mbody:mp:00.02'}   # Body of part 2
                          $e{'size:mp:00.02'},   # size of part 2
                          $e{'checksum:mp:00.02'}# digital-signature of part 2
                          $e{'mtype:mp:00.02'}   # MIME-type of part 2
                          $e{'mchar:mp:00.02'}   # charset of part 2
                          $e{'mcode:mp:00.02'}   # encoding of part 2
                          $e{'mname:mp:00.02'}   # filename of part 2
                          ...
                          $e{'mp:02'},           # number of parts in part 2
                          $e{'mp:02.01'},        # sub-part 1 in part 2
                          $e{'mhead:mp:02.01'}   # MIME header of sub-part 1
                          $e{'mbody:mp:02.01'}   # Body of sub-part 1
                          $e{'size:mp:02.01'},   # size of sub-part 1
                          $e{'checksum:mp:02.01'}# digital-signature of sub-part 1
                          $e{'mtype:mp:02.01'}   # MIME-type of sub-part 1
                          $e{'mchar:mp:02.01'}   # charset of sub-part 1
                          $e{'mcode:mp:02.01'}   # encoding of sub-part 1
                          $e{'mname:mp:02.01'}   # filename of sub-part 1
                          ....

          $e{'Header'} -> $e{'size:header'}
                          $e{'OH:From'},     # left part of header(field-name)
                          $e{'H:From'},      # right part of header
                          $e{'OH:Subject'},  # left part of header(field-name)
                          $e{'H:Subject'},   # right part of header
                          $e{'OH:Message-Id'}, # left part of header(field-name)
                          $e{'H:Message-Id'},  # right part of header
                          ...

      $e{'H:Received'} → $e{'received:0'},    # right part of origin received
                          $e{'received:1'},    # right part of relay1 received
                          $e{'received:2'},    # right part of relay2 received
                          ...

          # In %e hash, the format of key(field-name) is the first char and following char of '-' are uppercase, the others are lowercase.
          # In %e hash, the 'H:' indicate it is header.

Install

1. Prepare USER and GROUP for PPML

Get root, decide or make USER and GROUP for PPML, add USER to trusted user in sendmail.cf(user:group = ppml:ppml in explanation below)

/etc/sendmail.cf

# this is equivalent to setting class "t"
T root daemon uucp ppml

2. Set instalation path etc. in Makefile, and type "make install"

##################### SET VARIABLES HERE ####################
UID                     = ppml
GID                     = ppml
DOMAIN                  = enkai.org
FQDN                    = axia.enkai.org
PERL                    = /usr/local/bin/perl
PPMLDIR                 = /usr/local/ppml
ML_SETTING_ROOT         = /usr/local/lib/ppml
CHDIR                   = cd
#############################################################

"make install" install Digest-MD5-2.07 module, ppml scripts and setting example(skel)

2.1 "make README" generate README from ppml.html

This depends on lynx and nkf programs. So I prepare the README file in PPML package :-)

3. Check settings

/usr/local/ppml/sitedef.ph

$Logfile = "pp.log";
$Savedir = "archive";
$Seqfile = "pp.seq";
$Cache = "pp.cache";
$PPEHLOG = "ppeh.log";
$ERRORLOG = "pp.err"; # ErrorMail analyse log
# $USE_LOCKFILE = 1; 
# undef: use flock(config.ph)
#     1: use link and lockfile
# If you use lockfile, add this line in /etc/rc;
#         rm -f $ML_SETTING_ROOT/*/archive/.lockfile
$CHKSUMTYPE = 'SHA1'; # SHA1(160bit),MD5(128bit),undef(32bit) is selectable.
# $ALIASES = "/etc/aliases";
# $AUTOGENERATE = 1;
# if you set these two variables, you can use "generate" command
# to create new ML.
$ML_SETTING_ROOT = "/usr/local/lib/ppml";  # top dir of settings
$ML_CONTROL = 'ppserv@enkai.org';          # like as majordomo address
$ML_SERVER_ADMIN = 'ppserv-request@enkai.org'; # server admin address
$PPML_HOST = 'axia.enkai.org';             # FQDN of localhost
$SMTP_HOST = 'localhost:25';               # relaying host
$SESSIONMAXRCPTS = 100;                    # Maximum of rcpts/SMTP session
$MIMEANALYZE = 1;                          # Analyze MIME-multipart
$MAXBODYSIZE = 500000;                     # deliver size limit
$MAXPARTSIZE = 200000;                     # deliver size limit/part
# If you set $SENDMAIL, article is piped to the deliver program.
# $SENDMAIL = '/usr/sbin/sendmail';   
# If IgnoreDot is False in sendmail.cf, set as below.
# $SENDMAIL = '/usr/sbin/sendmail -i';    
%USERCOMMPERM = (
		'index',       '1',
		'get',         '1',
		'on',          '1',
		'off',         '1',
		'unsubscribe', '1',
		'who',         '1',
		'subscribe',   '1',
		'confirm',     '1',
		'info',        '1',
		'help',        '1',
		);

# Default of header generation
sub DefaultMakeHeader{
    $0 = "MakeHeader";
    local(*e) = @_;
    ### if you don't allow a field-name like "suBJeCt",
    # foreach $kk (keys(%e)){
    #     if ($kk =~ /^OH\:/){
    #         delete $e{$kk};
    #     }
    # }
    ### delete text/html part(sample:)
    # for ($b = 0 ; $b <= $e{'mp:00'} ; $b++){
    #   $mcb = sprintf("mp:%02d",$b);
    #   for ($i = 1 ; $i <= $e{$mcb} ; $i++){
    #     $mcs = sprintf("mp:%02d.%02d",$b,$i);
    #     if ($e{"mtype:$mcs"} =~ /text\/html/i){
    #         &DeletePart(*e,$b,$i);
    #     }
    #   }
    # }
    ### delete files based on extentions from attachment part(sample:)
    # for ($b = 0 ; $b <= $e{'mp:00'} ; $b++){
    #   $mcb = sprintf("mp:%02d",$b);
    #   for ($i = 1 ; $i <= $e{$mcb} ; $i++){
    #     $mcs = sprintf("mp:%02d.%02d",$b,$i);
    #     if (($e{"mname:$mcs"} =~ /(\.ade|\.adp|\.bas|\.bat|\.chm|\.cmd)/i)||
    #        ($e{"mname:$mcs"} =~ /(\.com|\.cpl|\.crt|\.css|\.exe|\.hlp)/i)||
    #        ($e{"mname:$mcs"} =~ /(\.hta|\.inf|\.ins|\.isp|\.js|\.jse)/i)||
    #        ($e{"mname:$mcs"} =~ /(\.lnk|\.mdb|\.mde|\.msc|\.msi|\.msp)/i)||
    #        ($e{"mname:$mcs"} =~ /(\.mst|\.pcd|\.pif|\.reg|\.scr|\.sct)/i)||
    #        ($e{"mname:$mcs"} =~ /(\.shs|\.url|\.vb|\.vbe|\.vbs|\.wsc)/i)||
    #        ($e{"mname:$mcs"} =~ /(\.wsf|\.wsh|\.csp|\.cab|\.pnf|\.ida)/i)){
    #        &DeletePart(*e,$b,$i);
    #     }
    #   }
    # }
    ### against BubbleBoy virus
    if (($e{'H:Subject'} =~ /BubbleBoy is back\!/i ) &&
        ($e{'Body'} =~ /http\:\/\/www\.towns\.com\/dorms\/tom\/bblboy\.htm/i )){
        &Log("$0:VIRUS: BubbleBoy detected");
        &NoticeMail($ML_ADMIN,"BubbleBoy detected($BRACKET)",$e{'Header'}.$e{'Body'},*r);
        exit;
    }
    ### against 1-line Command
    if (defined($e{'seq'}) && ($e{'line:body'} <= 2)){
        if ($e{'Body'} =~ /^\s*(info|on|off|who|help|subscribe|unsubscribe)\s*\S*/i){
            &Log("$0:COMM: Command from $e{'poster'} rejected");
            &NoticeMail($e{'poster'},"Command rejected($BRACKET)",$e{'Header'}.$e{'Body'},*r);
            &DecrSeqNum(*e);
            exit;
        }
    }
    ### delete header
    delete $e{'H:Received'};
    delete $e{'H:Return-Path'};
    delete $e{'H:X-Authentication-Warning'};
    delete $e{'H:Return-Receipt-To'};           # to avoid DSN storm
    delete $e{'H:Disposition-Notification-To'}; # to avoid MDN storm
    ### change header
    $e{'H:Subject'} =~ s/\n//;
    $e{'H:Subject'} =~ s/\[$BRACKET\s+\d+\]//;
    1 while $e{'H:Subject'} =~ s/\s*Re\:\s*Re\:\s*/Re\: /i ;
    $e{'H:Subject'} = "[$BRACKET $e{'seq'}] ".$e{'H:Subject'}."\n";
    $e{'H:Subject'} =~ s/(\s)+/$1/g;
    $e{'H:Reply-To'} = $e{'H:Reply-To'} || "$ML_ADDR\n";
    ### overwrite header
    $e{'H:Sender'} = "$ML_ADMIN\n";
    $e{'H:Precedence'} = "bulk\n";
    $e{'H:X-Sequense'} = "$BRACKET $e{'seq'}\n";
    $e{'OH:X-Ml-System'} = "X-ML-System";
    $e{'H:X-Ml-System'} = "Pigeon Post ver.0.753\n";

    return 1;
}
1;

/usr/local/ppml/include

 "|/usr/local/ppml/ppserv.pl "

/etc/aliases

# PPML Command Interface
ppserv: :include:/usr/local/ppml/include
ppserv-request: ando@enkai.org
# ML Setting
skel: :include:/usr/local/lib/ppml/skel/include
skel-request: ando@enkai.org
# Don't forget newaliases

4. Check setting in each ML dir

/usr/local/lib/ppml/skel/include

"|/usr/local/ppml/ppml.pl /usr/local/lib/ppml/skel "

# Set setting-dir as a ppml.pl's argument

/usr/local/lib/ppml/skel/admins.ph

# Set addresses and passwords of admins.
%members = (
         'ando@enkai.org','PASSWORD',
         'ando@kk.iij4u.or.jp','PASSWORD',
);
1;

/usr/local/lib/ppml/skel/config.ph

#
#  ppml configuration file -- config.ph
#
@ADMINLIST = ('admins.ph');                # files of admins auth
# Anyone can post if you comment out @MEMBERLIST.
### Normal
$OPTIMIZED_DELIVERLIST = 0;
@MEMBERLIST = ('admins.ph','members.ph');
@DELIVERLIST = ('members.ph');
### Optimized
# $OPTIMIZED_DELIVERLIST = 1;
# @MEMBERLIST = ('../opt-members.ph','admins.ph');
# @DELIVERLIST = ('../opt-members.ph');
@H_ATOM = ('Subject','From','To','Cc','Date','Reply-To','Sender');
$BRACKET = 'PP-GENUINE';                   # tag [PP-GENUINE 119]
$ML_ADMIN = 'skel-request@enkai.org';      # alias of admins
$ML_ADDR = 'skel@enkai.org';               # address of ML
%MLALIAS = (
   'alias-of-skel' => 'skel',              # anti-alias of ML
);
# $AUTOSUBSCRIBE = 1;                      # auto subscribe
# $NOARCHIVE = 1;                          # no archive is needed

# Flexible changing headers
sub MakeHeader{
    $0 = "MakeHeader";
    local(*e) = @_;
    &DefaultMakeHeader(*e);
    ### delete text/html part(sample:)
    # for ($i = 1 ; $i <= $e{'mp'} ; $i++){
    #    if ($e{"mp:$i"} =~ /Content-Type:\s*text\/html/){
    #        &DeletePart(*e,$i);
    #    }
    # }
    ### change Reply-To with AuthPoster
    ### please delete some lines in sitedef.ph
    # if ($e{'auth'} == 1){
    #     $e{'H:Reply-To'} = $e{'H:Reply-To'} || "$ML_ADDR\n";
    # }else{
    #     $e{'H:Reply-To'} = "$ML_ADDR\n";
    # }
    ### add footer(sample:)
    # $e{'Body'} .= "\n\n-- This is footer. Thanks!\n";
    return 1;
}
1;

5. type "newaliases" and approve subscribe to add members


How to create new ML by generate command

1. add entris of new ML to aliases

Before using this feature, you MUST set $AUTOGENERATE and $ALIASES in sitedef.ph.

/etc/aliases

# New ML Setting
newml: :include:/usr/local/lib/ppml/newml/include
newml-request: ando@enkai.org
# Don't forget newaliases.

2. send generate command to ppserv

Send "generate" command with ML's name and initial password to ppserv:

generate newml initpasswd

Now, you finished to create a new ML and approve subscribe to add members.


The system of PPML commands

Commands for administrator

approve PASSWORD subscribe MLNAME GECOS-ADDRESS
  ex. approve Der.S subscribe ppmltest Kazunori ANDO <ando@kk.iij4u.or.jp>
  ex. approve Des.S subscribe ppmltest ando@kk.iij4u.or.jp (Kazunori ANDO)
  ex. approve Dem.S subscribe ppmltest ando@kk.iij4u.or.jp
approve PASSWORD unsubscribe MLNAME ADDRESS
approve PASSWORD off MLNAME ADDRESS
approve PASSWORD on MLNAME ADDRESS
approve PASSWORD passwd MLNAME NEWPASSWORD
  # Admins can set each original password.
approve PASSWORD newinfo MLNAME
  # Write informations in the lines after "approve newinfo".
approve PASSWORD welcome MLNAME
  # Write welcome message in the lines after "approve welcome".
approve PASSWORD who MLNAME
  # Addresses and GECOSes are listed.
approve PASSWORD help MLNAME
approve PASSWORD delete MLNAME numbers
  ex. approve Den.S delete ppmltest 1-3,5,8,9
  ex. approve Die.S delete ppmltest 1-4 6 10
approve PASSWORD get MLNAME numbers
  ex. approve Der.S get ppmltest 1-3,5,8,9
  ex. approve Der.S get ppmltest 1-4 6 10

Commands for members

get MLNAME numbers
  ex. get ppmltest 1-3,6,7,8
  ex. get ppmltest 2-6 9 10
index MLNAME
who MLNAME
  # Only GECOSes are listed.
unsubscribe MLNAME
off MLNAME
on MLNAME

Commands for everyone

info MLNAME
subscribe MLNAME
confirm AUTH-NUMBER MLNAME
  ex. confirm 931067337-00000001893 ppmltest
  # AUTH-NUMBER can get in the reply of subscribe command.
generate MLNAME PASSWORD
  # Create new ML directory and files along /etc/aliases entry.
  # PASWSWORD is used for initial admin's password.
help