#!/usr/bin/perl -w ## STAMP ## Secure, Template-Aware Mail Processor ## Benjamin Trott / ben@rhumba.pair.com # $Id: stamp.cgi,v 1.9 2001/10/01 21:09:40 btrott Exp $ use strict; use vars qw( $VERSION ); $VERSION = '0.06'; my $CFG_FILE = "sample.cfg"; use App::Config; use Mail::Mailer; use CGI; my $q = CGI->new; my($form, $cfg); error($q, qq(No _form parameter defined.)) unless $form = $q->param('_form'); error($q, qq(Config file does not exist or is not readable.)) unless -R $CFG_FILE; { my $form_matched; $cfg = App::Config->new({ LINEPARSE => sub { my($tag, $value) = split /\s+/, $_[3], 2; if (lc $tag eq "form") { $form_matched = $value eq $form ? 1 : 0; return 1; } !$form_matched; }}) or error($q, qq(Config object creation failed.)); } $cfg->define($_) for (qw( Form Redirect MailTemplate Required PrintBlankFields Referrers Sort EncryptKey EncryptPublicKeyRing EncryptCompat )); $cfg->define('To', { ALIAS => 'Recipient' }); $cfg->define('Subject', { DEFAULT => 'WWW Form Submission' }); $cfg->define('MailTransfer', { DEFAULT => 'sendmail' }); $cfg->define('SMTPServer', { DEFAULT => 'localhost' }); $cfg->cfg_file($CFG_FILE); error($q, qq(To and Redirect are required config options.)) unless $cfg->To && $cfg->Redirect; if (my $req = $cfg->Required) { my @missing = grep !$q->param($_), split /\s*,\s*/, $req; error($q, <$_", @missing ]} HTML } if (my $ref = $cfg->Referrers) { my $match = join '|', map "\Q$_\E", split /\s*,\s*/, $ref; error($q, <$ENV{HTTP_REFERER}. HTML } my($body); if (my $tmpl_path = $cfg->MailTemplate) { require HTML::Template; my $tmpl = HTML::Template->new(filename => $tmpl_path, associate => $q) or error($q, "Failed to create HTML::Template object: $!"); $body = $tmpl->output; } else { my($sort, @order) = ($cfg->Sort); $sort = '' unless defined $sort; if ($sort eq 'alphabetical') { @order = sort $q->param; } elsif ($sort =~ s/^order:\s*//) { @order = split /\s*,\s*/, $sort; } else { @order = $q->param; } for my $p (@order) { next if $p =~ /^_/ || (!$cfg->PrintBlankFields && !$q->param($p)); $body .= $p . ": " . $q->param($p) . "\n\n"; } } if (my $recip = $cfg->EncryptKey) { eval { require Crypt::OpenPGP; }; error($q, "Crypt::OpenPGP is required for PGP encryption") if $@; my $pub = $cfg->EncryptPublicKeyRing; error($q, "You did not set an EncryptPublicKeyRing; this is " . "required when encrypting STAMP email messages.") unless $pub; my $compat = $cfg->EncryptCompat or error($q, "You did not set a compatibility mode for encryption."); my $pgp = Crypt::OpenPGP->new( PubRing => $pub ) or error($q, "Failed to create Crypt::OpenPGP object: " . Crypt::OpenPGP->errstr); $body = $pgp->encrypt( Compat => $compat, Data => $body, Recipients => $recip, Armour => 1, ) or error($q, "Error while encrypting: " . $pgp->errstr); } my @args = $cfg->MailTransfer eq 'smtp' ? (Server => $cfg->SMTPServer) : (); my $mailer = Mail::Mailer->new($cfg->MailTransfer, @args); my $from = $q->param('email'); ## XXX: add _realname $mailer->open({ To => $cfg->To, ($from ? (From => $from) : ()), Subject => $cfg->Subject, }); print $mailer $body; $mailer->close; print $q->redirect($cfg->Redirect); sub error { my($q, $msg) = @_; print $q->header, $msg; exit; } __END__ =head1 NAME STAMP - Secure, Template-Aware Mail Processor =head1 VERSION $Revision: 1.9 $ $Date: 2001/10/01 21:09:40 $ =head1 SYNOPSIS I is a secure, generic HTML form mailer written in Perl. It is easy to set up and provides security against your system being used as a spam gateway. I also lets you customize generated email submissions using templates, and provides optional PGP encryption of email messages. =head1 DESCRIPTION I provides secure form processing and email generation, and supports using templates to control and customize the generated email message. It is more secure than the standard I script because it relies on a server-side configuration file to set the recipient and subject of an email message; this makes it impossible for spammers to use your mail processing system as a spam gateway. I also allows you to PGP-encrypt all messages sent through the system. This functionality requires that you have the I module installed on your server; I provides a pure-Perl implementation of the OpenPGP standard, and is compatible with all PGP implementations (PGP, GnuPG, etc.). I provides many configuration options to customize the appearance of the email messages it generates. Foremost among these options is the ability to use user-defined templates to control the appearance. These templates act like form letters, in that they contain hooks to insert the values of the fields on your form. =head1 CONFIGURATION OPTIONS I uses server-side configuration files with hard-coded recipient and subject values to provide security against spammers. In addition to those fields, configuration files are used to set many other form-specific options. The configuration file has the following format: Empty lines and lines starting with C<#> are comments. All other lines are of the format I. Possible keywords are: =over 4 =item * Form Delimits the configuration section for one form; the following declarations, up to the next I
keyword, will be applied to the form name given after the keyword. The form name must I match the I<_form> value (from your HTML form). To include configuration settings for multiple forms in one config file, use the I keyword to start each section. For example: Form foo To foo@bar.com Redirect http://www.foo.com/ Form bar To bar@bar.com Redirect http://www.bar.com/ The indentation is not required, but it can be helpful in providing visual cues as to which settings apply to which form. =item * Recipient =item * To Specifies the email address of the recipient(s) of this form submission. You can use either I or I. Multiple recipients can be specified by separating the email addresses with commas (C<,>). This keyword is required for each form. =item * Redirect Specifies the URL of the page to which the user will be redirected after submitting the form. This should be an absolute URL. This keyword is required for each form. =item * Subject Specifies the subject of the email message containing the form submission. This keyword is optional; the default is C, though this may change in the future. =item * Required A comma-separated list of required fields on your form; if any of these fields are not filled out, the user will get an error. This keyword is optional. =item * Referrers A comma-separated list of valid referrers. If present, the referrer (the C environment variable) will be checked against each of these; if it does not match, the user will get an error. I that a referrer can be forged relatively easily, so you should rely on this only as a first measure of security. This keyword is optional. =item * MailTemplate The filesystem path to an I template, from which the form submission email message will be generated. This keyword is optional. If not specified, the email message will simply be a dump of the form fields and their values. =item * Sort Specifies a sort order for the form fields to appear in the form submission email message. This option only applies if you have not specified a I for this form. The argument for I can be either I, which will sort the field names alphabetically; or you can use a custom sorting order by listing the field names in the order in which you'd like them to appear, separated by commas, and prefixed by C. For example: Sort order:foo,bar,baz This keyword is optional. If not specified, the form fields will be in an undefined order; generally, this order is the order in which they appeared in the form, but you should not count on this. =item * PrintBlankFields By default, when not using a mail template (I, above), blank form fields will be omitted from the email message. You can force their inclusion by setting this option to 1: PrintBlankFields 1 This keyword is optional; the default is to exclude blank fields. =item * EncryptKey Turns on encryption for I email messages. If set, the value for this option should be either the key ID or the email address of the intended recipient; this will be used to look up the recipient's public key in the public keyring. That public key will then be used to encrypt the message. Obviously, if you wish to be able to decrypt the message, the value for I should be the key ID or email address for your public key. Note that encryption requires that your server have I installed; this module is available on CPAN (F). Note also that if you supply the I parameter, you I also supply the I and I options. This keyword is optional; the default is no encryption. =item * EncryptPublicKeyRing If using encryption (see I, above), specifies the path to your public keyring, or your exported public key. All PGP applications allow you to export your key; you can export it in either ASCII-armoured text, or plain binary format. I will understand either format. You could also simply set the path to your entire public keyring. This is not advised, however, as it will slow down I, because I must then look through all of the keys in your public keyring until it finds the key for the intended recipient. This keyword is required if using encryption, ignored otherwise. =item * EncryptCompat If using encryption (see I, above), specifies the compatibility mode for encryption. I (or, rather, I, which I uses for all PGP functionality) is compatible with all PGP implementations that support the OpenPGP standard. The compatibility mode gives hints to I as to which symmetric cipher to use, which compression algorithm to use, etc. The value for I should be one of the following strings: C, C, or C. You should set the value to the PGP implementation that you will use for decrypting the messages; note that if you are using a version of PGP greater than 5, just set this value to C. This keyword is required if using encryption, ignored otherwise. =item * MailTransfer Specifies the method by which you'd like the email message to be sent. Possible values are: C, to send through the Unix C program; C, to send through an SMTP server (see I, below); and C, to send through the Unix F program. This keyword is optional; the default method used is C. =item * SMTPServer If you select a transfer method of I, you may also need to set the address of your SMTP server. This keyword is optional; the default server used is C. =back =head1 MESSAGE ENCRYPTION To set up encryption of all messages sent through I, you must first ensure that your server has I installed. Talk to your system administrators if you're not sure. Once you know that I is installed, follow these instructions to set up encryption: =over 4 =item 1. Export your PGP public key. You should not include the private/secret key part of your key; only the public part is required. =item 2. Upload the exported key to your webserver, and take not of the location where you uploaded it. =item 3. Open your I configuration file. =item 4. Add an I directive, specifying the email address associated with the public key that you exported--this is probably your personal email address. For example: EncryptKey foo@bar.com =item 5. Add an I directive, specifying the path to the exported key you uploaded in Step 2. For example: EncryptPublicKeyRing /path/to/key.asc =item 6. Add an I directive, specifying the compatibility mode for the encryption; this should correspond to the PGP implementation that you will be using to decrypt encrypted I messages. Take a look at the documentation for I, above, for a list of possible values. For example: EncryptCompat PGP2 =back That's it, you're done! All mail sent through the particular I form for which you set up encryption will now be encrypted. =head1 TEMPLATE SYNTAX A mail template for email generation might look like this: Hi, This is an automated message generated from a form submission on your web site. The form was submitted by , who is interested in the aspect of your business. Here is the information posted through the form: First Name Last Name Age Phone Number Email Address Please contact this person as soon as possible. In the above examples, the labels like C, C, etc. should match the labels for the fields in your form. The ETMPL_VAR NAME="foo"E statements will be completely replaced by the values of those fields, when the form is submitted. For example, if you have a form field called C: and a user fills in the value C<56>, the C line above will read Age 56 =head1 FORM FIELDS To use I, your HTML form must contain a field called I<_form>; this will generally be a hidden input field, and it tells I which form was submitted, so that it can pull the necessary information out of the configuration file. The I<_form> field might look like this: In addition, if you'd like the I address of the generated email message to contain the user's email address, you should create an I field in your form for the user to fill in: The value of this field will be used as the I address. =head1 AUTHOR & COPYRIGHTS Benjamin Trott, ben@rhumba.pair.com Except where otherwise noted, I is Copyright (c) 2001, Benjamin Trott. All rights reserved. I is free software; you may redistribute it and/or modify it under the same terms as Perl itself. =cut