#!/usr/bin/perl ############################################################################## # FormMail Version 1.6v # # Copyright 1995-1997 Matt Wright mattw@worldwidemart.com # # Created 06/09/95 Last Modified 05/02/97 # # Matt's Script Archive, Inc.: http://www.worldwidemart.com/scripts/ # ############################################################################## # COPYRIGHT NOTICE # # Copyright 1995-1997 Matthew M. Wright All Rights Reserved. # # # # FormMail may be used and modified free of charge by anyone so long as this # # copyright notice and the comments above remain intact. By using this # # code you agree to indemnify Matthew M. Wright from any liability that # # might arise from its use. # # # # Selling the code for this program without prior written consent is # # expressly forbidden. In other words, please ask first before you try and # # make money off of my program. # # # # Obtain permission before redistributing this software over the Internet or # # in any other medium. In all cases copyright and header must remain intact # ############################################################################## # Define Variables # # Detailed Information Found In README File. # # $mailprog defines the location of your sendmail program on your unix # # system. # $mailprog = '/bin/sendmail'; # @referers allows forms to be located only on servers which are defined # # in this field. This security fix from the last version which allowed # # anyone on any server to use your FormMail script on their web site. # @referers = ('YOUR-DOMAIN.NAME','YOUR.IP.ADDR.ESS'); # @ok_recipients allows forms to be sent only to certain recipients. If no # # recipients are given (or the @ok_recipients array is commented out) # # formmail.pl does not check the recipient(s). This means a) that _any_ # # recipient is considered valid and b) you are susceptible to relay attacks # # via formmail.pl. # # # # Because referer headers are easily spoofed, it is recommended that you # # specify a list of valid recipients in the @ok_recipients array. If you are # # using formmail.pl to send email to arbitrary or user specified recipients, # # you are susceptible to relay attacks on your sendmail server. # # # # To only allow mail to be sent to someone on your server, uncomment the # # @ok_recipients array below and replace YOUR-DOMAIN.NAME with your domain # # name and make sure the recipient in the formmail HTML form matches at # # least one of the recipient domains you specify. Any valid regular # # expression will work. # # # # If you do not wish to specify recipients and would like to open up your # # server as a spammer relay (possibly causing your server to crash or have # # poor response times for your valid customers), simply comment out the # # @ok_recipients array by placing a hash mark (#) in front of it. # # # # It is important to add a dollar sign ($) at the end of each domain name. # # If you do not add a dollar sign, a shady character might try to spoof your # # formmail script with a recipient like this: # # # # ^your-domain.name@evilhackers.dom' # # # # which might work at some level or another, possibly opening up your server # # not as a final MTA but perhaps as a secondary relay in a chain of relays. # # # # Some examples of useful regular expressions are: # # # # '^bob@domain.name$' # # '^bob@somewhere.else$' # # '^your@email.foo$' # # '@another-domain-on-your-server.com$' # # # @ok_recipients = ( '@YOUR-DOMAIN.NAME$' ); # Done # ############################################################################## # Check Referring URL &check_url; # Retrieve Date &get_date; # Parse Form Contents &parse_form; # Check Recipients &check_recipients; # Check Required Fields &check_required; # Return HTML Page or Redirect User &return_html; # Send E-Mail &send_mail; # Uncomment the next line for Autorespond #&autorespond; sub check_url { # Localize the check_referer flag which determines if user is valid. # local($check_referer) = 0; # If a referring URL was specified, for each valid referer, make sure # # that a valid referring URL was passed to FormMail. # if ($ENV{'HTTP_REFERER'}) { foreach $referer (@referers) { if ($ENV{'HTTP_REFERER'} =~ m|https?://([^/]*)$referer|i) { $check_referer = 1; last; } } } else { $check_referer = 1; } # If the HTTP_REFERER was invalid, send back an error. # if ($check_referer != 1) { &error('bad_referer') } } sub get_date { # Define arrays for the day of the week and month of the year. # @days = ('Sunday','Monday','Tuesday','Wednesday', 'Thursday','Friday','Saturday'); @months = ('January','February','March','April','May','June','July', 'August','September','October','November','December'); # Get the current time and format the hour, minutes and seconds. Add # # 1900 to the year to get the full 4 digit year. # ($sec,$min,$hour,$mday,$mon,$year,$wday) = (localtime(time))[0,1,2,3,4,5,6]; $time = sprintf("%02d:%02d:%02d",$hour,$min,$sec); $year += 1900; # Format the date. # $date = "$days[$wday], $months[$mon] $mday, $year at $time"; } sub parse_form { # Define the configuration associative array. # %Config = ('recipient','', 'subject','', 'email','', 'realname','', 'redirect','', 'bgcolor','', 'background','', 'link_color','', 'vlink_color','', 'text_color','', 'alink_color','', 'title','', 'sort','', 'print_config','', 'required','', 'env_report','', 'return_link_title','', 'return_link_url','', 'print_blank_fields','', 'missing_fields_redirect',''); # Determine the form's REQUEST_METHOD (GET or POST) and split the form # # fields up into their name-value pairs. If the REQUEST_METHOD was # # not GET or POST, send an error. # if ($ENV{'REQUEST_METHOD'} eq 'GET') { # Split the name-value pairs @pairs = split(/&/, $ENV{'QUERY_STRING'}); } elsif ($ENV{'REQUEST_METHOD'} eq 'POST') { # Get the input read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); # Split the name-value pairs @pairs = split(/&/, $buffer); } else { &error('request_method'); } # For each name-value pair: # foreach $pair (@pairs) { # Split the pair up into individual variables. # local($name, $value) = split(/=/, $pair); # Decode the form encoding on the name and value variables. # $name =~ tr/+/ /; $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; # If they try to include server side includes, erase them, so they # aren't a security risk if the html gets returned. Another # security hole plugged up. $value =~ s///g; # If the field name has been specified in the %Config array, it will # # return a 1 for defined($Config{$name}}) and we should associate # # this value with the appropriate configuration variable. If this # # is not a configuration form field, put it into the associative # # array %Form, appending the value with a ', ' if there is already a # # value present. We also save the order of the form fields in the # # @Field_Order array so we can use this order for the generic sort. # if (defined($Config{$name})) { $Config{$name} = $value; } else { if ($Form{$name} && $value) { $Form{$name} = "$Form{$name}, $value"; } elsif ($value) { push(@Field_Order,$name); $Form{$name} = $value; } } } # The next six lines remove any extra spaces or new lines from the # # configuration variables, which may have been caused if your editor # # wraps lines after a certain length or if you used spaces between field # # names or environment variables. # $Config{'required'} =~ s/(\s+|\n)?,(\s+|\n)?/,/g; $Config{'required'} =~ s/(\s+)?\n+(\s+)?//g; $Config{'env_report'} =~ s/(\s+|\n)?,(\s+|\n)?/,/g; $Config{'env_report'} =~ s/(\s+)?\n+(\s+)?//g; $Config{'print_config'} =~ s/(\s+|\n)?,(\s+|\n)?/,/g; $Config{'print_config'} =~ s/(\s+)?\n+(\s+)?//g; # Split the configuration variables into individual field names. # @Required = split(/,/,$Config{'required'}); @Env_Report = split(/,/,$Config{'env_report'}); @Print_Config = split(/,/,$Config{'print_config'}); } sub check_recipients { ## no ok_recipients found? Well, alright. It's your gun and your ## foot. Do what you want with them both. ;o) ## $GAPING_SECURITY_HOLE = 1; return 1 unless scalar(@ok_recipients); ## Good, we have some recipients. Grab them and clean them up. $Config{'recipient'} =~ s/\s+//g; ## strip spaces $Config{'recipient'} =~ s/\0//g; ## strip null bytes my @form_recipients = split( /,/, $Config{'recipient'} ); $Config{'recipient'} = ''; ## reset recipient my @good_recipients = (); ## good recipients ## find a match for this recipient TEST_RECIP: for my $test_recipient ( @form_recipients ) { for my $ok_recipient ( @ok_recipients ) { if( $test_recipient =~ /$ok_recipient/i ) { push @good_recipients, $test_recipient; next TEST_RECIP; } } ## $test_recipient did not match any @ok_recipients print STDERR "Address '$test_recipient' is not in \@ok_recipients\n"; &error('bad_recipient'); } ## all recipients were ok $Config{'recipient'} = join( ',', @good_recipients ); return 1; } sub check_required { # Localize the variables used in this subroutine. # local($require, @error); if (!$Config{'recipient'}) { if (!defined(%Form)) { &error('bad_referer') } else { &error('no_recipient') } } # For each require field defined in the form: # foreach $require (@Required) { # If the required field is the email field, the syntax of the email # # address if checked to make sure it passes a valid syntax. # if ($require eq 'email' && !&check_email($Config{$require})) { push(@error,$require); } # Otherwise, if the required field is a configuration field and it # # has no value or has been filled in with a space, send an error. # elsif (defined($Config{$require})) { if (!$Config{$require}) { push(@error,$require); } } # If it is a regular form field which has not been filled in or # # filled in with a space, flag it as an error field. # elsif (!$Form{$require}) { push(@error,$require); } } # If any error fields have been found, send error message to the user. # if (@error) { &error('missing_fields', @error) } } sub return_html { # Local variables used in this subroutine initialized. # local($key,$sort_order,$sorted_field); # If redirect option is used, print the redirectional location header. # if ($Config{'redirect'}) { print "Location: $Config{'redirect'}\n\n"; } # Otherwise, begin printing the response page. # else { # Print HTTP header and opening HTML tags. # print "Content-type: text/html\n\n"; print "\n
\n"; # Print out title of page # if ($Config{'title'}) { print "\n"; # Sort alphabetically if specified: # if ($Config{'sort'} eq 'alphabetic') { foreach $field (sort keys %Form) { # If the field has a value or the print blank fields option # # is turned on, print out the form field and value. # if ($Config{'print_blank_fields'} || $Form{$field}) { print "$field: $Form{$field}
\n"; } } } # If a sort order is specified, sort the form fields based on that. # elsif ($Config{'sort'} =~ /^order:.*,.*/) { # Set the temporary $sort_order variable to the sorting order, # # remove extraneous line breaks and spaces, remove the order: # # directive and split the sort fields into an array. # $sort_order = $Config{'sort'}; $sort_order =~ s/(\s+|\n)?,(\s+|\n)?/,/g; $sort_order =~ s/(\s+)?\n+(\s+)?//g; $sort_order =~ s/order://; @sorted_fields = split(/,/, $sort_order); # For each sorted field, if it has a value or the print blank # # fields option is turned on print the form field and value. # foreach $sorted_field (@sorted_fields) { if ($Config{'print_blank_fields'} || $Form{$sorted_field}) { print "$sorted_field: $Form{$sorted_field}
\n"; } } } # Otherwise, default to the order in which the fields were sent. # else { # For each form field, if it has a value or the print blank # # fields option is turned on print the form field and value. # foreach $field (@Field_Order) { if ($Config{'print_blank_fields'} || $Form{$field}) { print "$field: $Form{$field}
\n"; } } } print "
\n"; # Check for a Return Link and print one if found. # if ($Config{'return_link_url'} && $Config{'return_link_title'}) { print "
| Bad Referrer - Access Denied |
|---|
| The form attempting to use
FormMail
resides at $ENV{'HTTP_REFERER'}, which is not allowed to access
this cgi script. If you are attempting to configure FormMail to run with this form, you need to add the following to \@referers, explained in detail in the README file. Add '$host' to your \@referers array. A Free Product of Matt's Script Archive, Inc. |
| FormMail |
|---|
| Copyright 1995 - 1997 Matt Wright Version 1.6 - Released May 02, 1997 A Free Product of Matt's Script Archive, Inc. |
|---|
| Error: Request Method |
|---|
| The Request Method of the Form you submitted did not match
either GET or POST. Please check the form and make sure the
method= statement is in upper case and matches GET or POST.
A Free Product of Matt's Script Archive, Inc. |
| Error: Bad Recipient |
|---|
| A bad Recipient was specified in the data sent to FormMail. Please
make sure you have filled in the 'recipient' form field with a valid e-mail
address and that address is defined in '@ok_recipients' array near the top
of your FormMail script. More information on filling in recipient form fields
can be found in the README file. A Free Product of Matt's Script Archive, Inc. |
| Error: No Recipient |
|---|
| No Recipient was specified in the data sent to FormMail. Please
make sure you have filled in the 'recipient' form field with an e-mail
address. More information on filling in recipient form fields can be
found in the README file. A Free Product of Matt's Script Archive, Inc. |
| Error: Blank Fields |
|---|
| The following fields were left blank in your submission form:
These fields must be filled in before you can successfully submit the form. Please use your browser's back button to return to the form and try again. A Free Product of Matt's Script Archive, Inc. |