#!/usr/bin/perl ##--------------------------------------------------------------------------## ## File: ## $Id: mk-procmailrc,v 1.25 2003/08/09 17:51:04 ehood Exp $ ## Description: ## Program to create a procmail recipe file from lists.def. ## Run script with '-man' option to view manpage for this program. ##--------------------------------------------------------------------------## ## Copyright (C) 2001-2002 Earl Hood ## ## 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 ##--------------------------------------------------------------------------## package MHArc::mk_procmailrc; ##--------------------------------------------------------------------------## # BEGIN { die qq/CGI use FORBIDDEN!\n/ if (defined($ENV{'GATEWAY_INTERFACE'})); } my $Dir; BEGIN { $Dir = `dirname $0`; chomp $Dir; } use lib "$Dir/../lib"; # Add relative lib to search path # ##--------------------------------------------------------------------------## # use MHArc::Config; my $config = MHArc::Config->load("$Dir/../lib/config.sh"); # ##--------------------------------------------------------------------------## use Getopt::Long; use MHArc::ListDef; use MHArc::Util qw( usage ); my $Verbose = 0; my $Vfh = \*STDOUT; MAIN: { my @htaccess = (); my %opt = ( ); my $clstatus = GetOptions(\%opt, 'catch-address=s', 'catch-archive=s', 'disable-catch-archive!', 'final-dest=s', 'home=s', 'mbox-dir=s', 'msgid-cache-size=i', 'out=s', 'procmail-path=s', 'verbose!', 'help', 'man' ); usage(0) unless $clstatus; usage(1) if $opt{'help'}; usage(2) if $opt{'man'}; $Verbose = $opt{'verbose'}; my $basedir = $opt{'home'} || $config->{'SW_ROOT'} || "$Dir/.."; my $mbox_dir = $opt{'mbox-dir'} || $config->{'MBOX_DIR'} || join('/',$basedir,'mbox'); my $out_file = $opt{'out'} || $config->{'PROCMAILRC'} || join('/', $basedir, 'procmailrc.mharc'); my $procmail_path = $opt{'procmail-path'} || $config->{'PROCMAIL_PATH'}; my $cache_size = $opt{'msgid-cache-size'} || $config->{'MSGID_CACHE_SIZE'} || 16384; my $catch_addr = $opt{'catch-address'} || $config->{'CATCH_ADDRESS'} || ""; my $catch_arc = $opt{'catch-archive'} || $config->{'CATCH_ARCHIVE'} || '.catch'; my $nocatch = defined($opt{'disable-catch-archive'}) ? $opt{'disable-catch-archive'} : ($config->{'DISABLE_CATCH_ARCHIVE'} || 0); my $final_dest = $opt{'final-dest'} || $config->{'FINAL_MSG_DESTINATION'} || '/dev/null'; ## Read lists definition file my $file = shift(@ARGV) || $config->{'LISTS_DEF_FILE'} || "$basedir/lib/lists.def"; my $listdef = MHArc::ListDef->new($file); my $extract_date_prg = join('/', $basedir, 'bin', 'extract-mesg-date'); local(*OUTFILE); my $outfh; if (!defined($out_file) || ($out_file eq "") || ($out_file eq '-')) { $outfh = \*STDOUT; $Vfh = \*STDERR; } else { open(OUTFILE, ">$out_file") || die qq/ERROR: Unable to create "$out_file": $!\n/; $outfh = \*OUTFILE; $Vfh = \*STDOUT;; } ## Print procmailrc header print $outfh <get_names) { print $Vfh "Generating rule for $name...\n" if $Verbose; @addr = ( ); @from_addr = ( ); if (defined($listdef->{$name}{'address'})) { @addr = @{$listdef->{$name}{'address'}}; } if (defined($listdef->{$name}{'from-address'})) { @from_addr = @{$listdef->{$name}{'from-address'}}; } if (!scalar(@addr) && !scalar(@from_addr) && !defined($listdef->{$name}{'procmail-condition'})) { # no addresses defined warn qq/Warning: No addresses or conditions defined for '$name'\n/; next; } $pm_conditions = ''; # create procmail regex for list if (scalar(@addr) || scalar(@from_addr)) { $pm_conditions .= '* ('; if (scalar(@addr)) { $pm_conditions .= '^TO_'; $pm_conditions .= '(' if (scalar(@addr) > 1); $pm_conditions .= join('|', @addr); $pm_conditions .= ')' if (scalar(@addr) > 1); $pm_conditions .= '|(^(Delivered-To:|List-Post:).*'; $pm_conditions .= '(' if (scalar(@addr) > 1); $pm_conditions .= join('|', @addr); $pm_conditions .= ')' if (scalar(@addr) > 1); $pm_conditions .= ')'; } if (scalar(@from_addr)) { $pm_conditions .= '|(' if (scalar(@addr)); $pm_conditions .= '^From:(.*[^-a-zA-Z0-9_.])?'; $pm_conditions .= '(' if (scalar(@from_addr) > 1); $pm_conditions .= join('|', @from_addr); $pm_conditions .= ')' if (scalar(@from_addr) > 1); $pm_conditions .= ')' if (scalar(@addr)); } $pm_conditions .= ')' . "\n"; } if (defined($listdef->{$name}{'procmail-condition'})) { foreach $str (@{$listdef->{$name}{'procmail-condition'}}) { $pm_conditions .= "$str\n"; } } # check if doing monthly or yearly archives $period = lc($listdef->{$name}{'period'}[0]) || 'month'; $period = 'month' if ($name eq $catch_arc); if ($period eq 'year') { $time_fmt = '%Y'; } else { $time_fmt = '%Y-%m'; } # check if rule should not be final if matched if ($listdef->{$name}{'final'}) { $pm_copy = ''; } else { $pm_copy = ' c'; } # check if sender specified no archiving should be honored if ($listdef->{$name}{'check-no-archive'}) { $noarchive =<<'EOT'; :0 * ^(X-no-archive: yes|Restrict: no-external-archive) /dev/null EOT } else { $noarchive = ""; } # check if separating out cvs commits if (($check_cvs = $listdef->{$name}{'cvs-commits'}[0]) && ($pm_conditions =~ /\S/)) { my $cvs_prefix = $listdef->{$name}{'cvs-subject-prefix'}[0] || 'CVS commit'; print $outfh < program. The procmailrc is generated from Cmharc-rootE/lib/lists.def>. This program is typically invoked by calling C from the mharc root directory with configuration options specified in Cmharc-rootE/lib/config.sh>. =head1 LIST DEFINITION FILE The list definition file, Cmharc-rootE/lib/lists.def>, controls how the procmailrc used by L is generated. The format of the file is simple and more convenient than writing the procmailrc file yourself. The basic format of the file is as follows: =over =item * Any blank links or lines starting with a C<#> are ignored. =item * Lines with the following format: Option-Name: Option-Value is an option. =back =head2 lists.def Supported Options =over =item Name Starts, and defines, the name of an archive. This name serves as the directory name containing archive data and the list title (the C<$LIST-TITLE$> MHonArc resource variable) for archive pages. A common practice is to use the list address, but this is not required, especially if the archive is a combination of multiple lists, or it is prefered to use a more abstract name for simplicity. The name also is used when the L script looks for an archive specific MHonArc resource settings. If the following resource file exists, $MHA_RC_DIR/.mrc where C<$MHA_RC_DIR> is the value of the MHA_RC_DIR C variable and CnameE> is the name of the archive, L will pass the resource file to MHonArc when processing the HTML archive. This provides a convenient way to provide list-specific customization to the archive. =item Address Mail address of the list. Multiple C
options can be specified for an archive if a list has more than one known address (e.g. due to migration) or the archive is a collection of multiple lists. B The address is technically treated as part of a procmail regular expression. Take the following as an example: Address: mharc-users@mhonarc.org In regular expressions, the '.' character represents any character. Therefore, the following strings would match the above specification: mharc-users@mhonarc#org mharc-users@mhonarcXorg mharc-users@mhonarc@org In practice, this technicality will generally have no affect, but if you want to be pendantic, do the following: Address: mharc-users@mhonarc\.org The '\' tells procmail to treat the '.' literally. Because the actual address string given is treated as part of a regular expression, you can specify a range of addresses with a single option. For example, mharc-[^@]*@mhonarc\.org will match the following addresses: mharc-users@mhonarc.org mharc-rules@mhonarc.org mharc-rocks@mhonarc.org mharc-is-the-best@mhonarc.org ... =item All-Lists-Name Label to use for Name column of all-lists index. If not specified, the name provided by the C option is used. =item Check-No-Archive Boolean option (C<0> or C<1>) if author specified archiving permission is honored. The author can request no archiving should be done by defining one of the the following header fields: Restrict: no-external-archive X-no-archive: yes If C is enabled, a message to the list with either field defined will not be archived. =item CVS-Commits Boolean option (C<0> or C<1>) if CVS commit messages should be archived separately. Use this option for development lists that have CVS project commits mailed to the list, and you want to avoid cluttering regular discussion mail. =item CVS-Subject-Prefix Specifies the C prefix denoting CVS commits to the list. This option is only used if C is specified. =item Description Brief description of archive and serves as the main title of archive index pages. =item Final Boolean option (C<0> or C<1>) if generated rule should be final. I.e. If a message matches, further rules will not be examined. Use this option to short-circuit messages from being stored in multiple archives. For example, you may want to catch messages cross-posted to a special address to only be archived in a special archive and not any of the regular archives. Another example is if you use the special C "C<.catch>" (or the C<-catch-archive> setting described in L<"OPTIONS">). Using "C<.catch>" is handy for C definitions to pre-catch messages that should not be placed in any list archive. =item From-Address Mail address of the list. This option is an alternative to C
where a list can only be donoted by the C field of messages. This is fairly common for one-way lists, like newsletters, where subscribers receive list messages but cannot post to the list. Multiple C options can be specified if a list has more than one known address (e.g. due to migration) or the archive is a collection of multiple lists. =item Hide-From-All-Lists Boolean option (C<0> or C<1>) if archive should not be listed in the all-lists index. If set to C<1>, the archive will not be shown. The default value for this option is C<0>. =item Lang Sets the language/locale of the archive. B Only set this option if using a version of MHonArc (v2.6.7, or later) that supports the LANG resource. Otherwise, you will get a runtime error when L is invoked. See the LANG resource reference page of the MHonArc documentation for more information. =item MHonArc-Options Additional MHonArc command-line options. =item No-Raw-Link Boolean option (C<0> or C<1>) if links to raw archives should exist. If set to C<1>, links will not be created or disabled. The default value for this option is C<0>. Use this option if your HTML archives have been customized to obscure addresses to prevent address harvesting. =item No-Search Boolean option (C<0> or C<1>) if searching should be disabled. If set to C<1>, no search index is created and the C<$SEARCH-FORM$> custom MHonArc resource variable will be set to the empty string. B Disable searching will diable some navigational features that are dependent upon the search index. =item Period If archive is a yearly or monthly archive. Legal values are C or C. If Period is not defined, C is the default. =item Procmail-Condition Additional condition to apply to base address check. The condition must be legal procmailrc syntax and should include any prefixing C<*>, C, et. al. This option can be specified multiple times. C can also be used inplace of C
and C to provide arbitrary matching rules for archiving messages. This is useful for rare cases where messages to be archived cannot be determined by receipient or from addresses. B Exercise caution when using this option, especially if C is true. When C is true, an additionaly rule already exists to check for the C setting. =back =head2 lists.def Notes =over =item * Every archive defined must define at least one C
, C, or C option. =item * The order of archive definitions is mirrored in the generated procmail rcfile. This is important to properly honor the sematics of archives with the C option specified. =back =head2 lists.def Example ## In this definition, we define multiple addresses to check. Name: mhonarc-users Description: MHonArc Users Address: mhonarc-users@mhonarc.org Address: mhonarc@ncsa.uiuc.edu Address: mhonarc@rosat.mpe-garching.mpg.de ## This definition defines a list that receives CVS commits and those ## commits should be separated into a special archive as to not ## pollute the discussion messages with cvs commit messages Name: mhonarc-dev Description: MHonArc Development Address: mhonarc-dev@mhonarc.org CVS-Commits: 1 CVS-Subject-Prefix: CVS: =head1 OPTIONS You should never have to invoke this program with any options since C specifies any options used by this program. However, for advanced uses, or you do not care if you mess things up, the following options are available: =over =item C<-catch-address> I The name of the email address to forward all unmatched message to. This is an alternative to C<-catch-archive>, and will supercede C<-catch-archive>, if defined. If this option is not specified, the C variable in C is used. =item C<-catch-archive> I The name of the I archive. The I archive collects all messages that do not match any list rules. If this option is not specified, the C variable in C is used, else the name "C<.catch>" is used. B If you use this option, it is recommended that the name starts with a C<.> (a dot). This insures that no search index is built and it will not be listed in the all-lists page. =item C<-disable-catch-archive> If specified, no I archive will be defined. B Care should be used when using this option since any message that does not match a normal rule will be lost. =item C<-final-dest> I The destination of messages that make it to the end of the procmailrc. It is normal for messages to make it to the end since the list matching rules create copies of the message during filtering. Hence, it is normal to see "C" destinations in the procmail log and it does not indicate that a message was lost. Message copying is done inorder to properly archive a message that has been cross-posted to multiple lists. Message copying is not done for archives with the C option set to 1, for CVS commit archives, or for messages that are captured by the catch archive. This option is generally only used for debugging purposes. If C<-final-dest> is not specified, the C variable in C is used, else C is used. =item C<-help> Print out usage information. =item C<-home> I B. Root pathname of software and archives. If not specified, C variable in C is used, else the parent directory that containing this program is used. =item C<-man> Print out entire manpage. =item C<-mbox-dir> I Root pathname containing raw mailbox archives. If not specified, C variable in C is used, else C/mbox> is used. =item C<-msgid-cache-size> I The maximum size, in bytes, of the message-id cache. The message-id cache helps avoid processing duplicate messages. If this option is not specified, the C variable in C is used, else 16384 will be used. =item C<-out> I Output filename. If this option is not specified, the C variable in C is used, else C/procmailrc.mharc> is used. If "-" is the I, then the procmailrc will be dumped to standard out. =item C<-procmail-path> I The search path for C to use. If this option is not specified, the C variable in C is used. =back =head1 FILES =over =item Cmharc-rootE/lib/lists.def> Mailing lists definition file. =item Cmharc-rootE/lib/config.sh> Main configuration file for mharc. =back =head1 VERSION $Id: mk-procmailrc,v 1.25 2003/08/09 17:51:04 ehood Exp $ =head1 AUTHOR Earl Hood, earl@earlhood.com This program is part of the mharc archiving system and comes with ABSOLUTELY NO WARRANTY and may be copied only under the terms of the GNU General Public License, which may be found in the mharc distribution. =cut