#### #### 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. ####
sub MakeHeader { &DefaultMakeHeader(@_); }
Thanks a lot to minmin@matsui.co.jp, sat@tokyonet.gr.jp and other kind persons:)
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.
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.
"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.
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.
PPML worked on perl5.005_03. And it need a SMTP relaying host (at least localhost:) or the sendmail program.
##### 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 ################
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.
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
##################### 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)
This depends on lynx and nkf programs. So I prepare the README file in PPML package :-)
/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
/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;
/etc/aliases
# New ML Setting newml: :include:/usr/local/lib/ppml/newml/include newml-request: ando@enkai.org # Don't forget newaliases.
Send "generate" command with ML's name and initial password to ppserv:
generate newml initpasswdNow, you finished to create a new ML and approve subscribe to add members.
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
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
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