#! /usr/bin/perl -T
# ====================================================================
# @(#)  CGI program for generic, automatic forms processing
# ====================================================================
# $Id: process_form,v 1.7 2012/03/09 17:06:48 dboldt Exp $
#
#    Written by: David Boldt                              21 Mar 2001
# --------------------------------------------------------------------
$VERSION = (split(/\s+/,'$Revision: 1.8 $'))[1];  #')) RCS magic
($me = $0) =~ s#^.*/##;

# Taintperl security settings
$ENV{CDPATH} = '';
$ENV{ENV}    = '';
$ENV{IFS}    = ' '  if $ENV{IFS} ne ' ';
$ENV{PATH}   = '/bin:/usr/bin';
$ENV{SHELL}  = '/bin/sh'  if $ENV{SHELL};

%SUFFIX = (
           'text' => 'txt',
           'xml' => 'xml',
          );

use lib '/afs/usgs.gov/www/water/cgi-bin/library';
use cgi_lib;

# Global variables
($RW_PATH = $ENV{DOCUMENT_ROOT}) =~ s%/afs/usgs.gov%/afs/.usgs.gov%;   # AFS R/W
$WRITEURL = "/usgs/${me}";  # URL path (from Document root) where files will be saved
$WRITEDIR = "$RW_PATH$WRITEURL";   # this directory should be world writable
$DELIMITER = "=";           # delimiter between variables and values when form is mailed
$FILE_DELIMITER = "\t";     # delimiter between variables and values when form is filed
$FILE_TYPE = "text";        # format of file to which form data will be saved
$HOST = $ENV{SERVER_NAME};  # host on which this program runs
$defaultmail = $ENV{'SERVER_ADMIN'} || 'h2oteam@usgs.gov';

# -- Instructions for using the program

$USAGE = "              Instructions for Using $me
                   an Html Form Processor
           ==========================================

Your form will need to post to the URL given below. Use hidden
variables to control for processing behavior, what to do with the
results.  The main options are to send the contents of a form to
one or more persons in email and/or to save them to a file.

Sample FORM html using $me:

   <form title=\"<i>Sample Form</i>\" method=\"POST\"
      action=\"https://${HOST}$ENV{SCRIPT_NAME}\">

      <p><input type=\"hidden\" name=\"${me}:Subject\"
         value=\"<i>Sample Subject</i>\"></p>
      <p><input type=\"hidden\" name=\"${me}:To\"
         value=\"<i>MyEmail</i>\"></p>


Magic form variables.  Assign values to these to change form
processing behavior.

Email Options
-------------

   ${me}:Subject      - Subject for email message with form results

   ${me}:To           - Who to mail the message containing the form
                               results. To reduce use of this field by
                               spammers, no '\@' is allowed for these
                               addresses, and \@usgs.gov will automatically
                               be appended. To send to a contractor address
                               append '_contractor' to the username.
                               Email cannot be sent outside of USGS.

   ${me}:Cc           - Who to 'carbon copy' the mail message to.
                               To reduce use of this field by
                               spammers, no '\@' is allowed for these
                               addresses, and \@usgs.gov will automatically
                               be appended.  To send to a contractor address
                               append '_contractor' to the username.
                               Email cannot be sent outside of USGS.

   ${me}:From         - Where replies to the mail message should be
                               sent. To reduce use of this field by
                               spammers, no '\@' is allowed for these
                               addresses, and \@usgs.gov will automatically
                               be appended. Email cannot be sent outside
                               of USGS.

   ${me}:MailDelimiter   - Character(s) to use between variable and
                                  value in mailed messages

Save To File Options
--------------------

   ${me}:FileType  - Format of data if ${me}:File is set.
                            Valid options are 'text' (the default)
                            and 'xml'.  Form variable names are used
                            for element names.

   ${me}:XMLparentTag  - Name of highest level tag in XML file.
                            The default is 'member'.

   ${me}:File      - Write information from each form submitted
                            to a file.  The file will beplaced in
                            directory $WRITEDIR/
                            on computer '$HOST'.
                            A '.txt' suffix will be appended the
                            filename unless the FileType is set to
                            'xml'. Do not specify a full pathname.

                            For example, if you specify 'sam', a file
                            named  $WRITEDIR/sam.txt
                            will be created.

                            That file will also be visible from
                            https://$HOST$WRITEURL/

Options to Enhance What is Sent or Saved
----------------------------------------

   ${me}:Date      - The date and time the  form was submitted
                            will be included. To change the format
                            of the time and date use a value based
                            on the Unix man page for \"date\")

   ${me}:ENV       - Include specified environment
                            variable, include all enviroment
                            variables if blank

   ${me}:Verbose   - If necessary, turn on to help debug your
                            use of this script.  Enabling will
                            display all email headers and ${me}
                            settings.

Misc
----

   ${me}:RedirectURL  - Web page to send user to once form has been
                            completed

   ${me}:HoneyPot     - This is an anti-spam configuration, created
                        to help avoid the large number of e-mail that
                        can be sent as result of USGS security scans.

                        For example, set this variable equal to 'email'
                        and then include a hidden variable as below in 
                        your form:

                        <input type=&quot;hidden&quot; name=&quot;email&quot;>

                        If the form is submitted with content in this
                        variable, an error page will be displayed and no
                        submission values will be saved or e-mailed.


                  To Make a Field Mandatory

To make a text field mandatory (so it cannot be submitted empty)
you will need a bit of javascript inserted in the HEAD section of
your HTML document.  Below is an example which makes two fields
manadatory: 'aaa' and 'bbb' (use whatever names you used in your
own form, eg: NAME=\"aaa\").

   <script language=\"JavaScript\">
   <!--
   function validate(frm) {
      //
      // Check the fields to see if they are empty
      //
      if (frm.aaa.value.length == 0) {
         alert(\"Please enter information for Field One.\")
         frm.aaa.focus()
         return false
      }
      //
      if (frm.bbb.value.length == 0) {
         alert(\"Please enter information for Field Two.\")
         frm.bbb.focus()
         return false
      }
   }
   //-->
   </script>

For each mandatory field add a new section, where XXX is the variable
name and YYY is what will be displayed in a pop-up window for the
user if they neglect to fill in that field:

      if (frm.XXX.value.length == 0) {
         alert(\"Please enter information for YYY.\")
         frm.XXX.focus()
         return false
      }

Below is an example to make a check box or radio button, which
has three possible settings, mandatory:

      if ( !( frm.ccc[0].checked
           || frm.ccc[1].checked
           || frm.ccc[2].checked
         ) ) {
         alert(\"Please select CCC.\");
         return false
      }

where you must repeat  \"|| frm.ccc[].checked\" for each option
of a check box, where ccc gets replaced by your NAME and the
numbering starts at 0 and goes to 1 less than the number of
options.  Replace CCC with the text for the warning message you
want displayed if none of the options are checked.

You must also add two attributes to the FORM tag in your html:

   name=\"SampleForm\"  onSubmit=\"return validate(SampleForm)\"

so that your FORM entry would look something like this:

   <form  title=\"Sample Form\"  method=\"POST\"
      name=\"SampleForm\"  onSubmit=\"return validate(SampleForm)\"
      action=\"https://${HOST}$ENV{SCRIPT_NAME}\">

Note that the form title can have spaces but variable names cannot.


version: $VERSION
\n";

# -- get form data

%form = &cgi_lib::load_FORM();

# -- if we are not processing a form, display usage and exit

unless (%form) {
    $message = $USAGE;
    $message =~ s/</&lt;/g;
    $message =~ s/>/&gt;/g;
    $message =~ s/&lt;(\/?)i&gt;/<$1i>/g;
    $message =~ s% (https://\S+)% <A HREF=\"$1\">$1</a>%g;

    $message .= "\n<HR>\n";
    $message .= "<b>Environment Variables</b>\n\n";
    for $envar (keys %ENV) {
        $env = $ENV{$envar};
        $env =~ s/</&lt;/g;
        $env =~ s/>/&gt;/g;
        $message .= "$envar$DELIMITER$env\n";
    }

    &cgi_lib::html_header("https://${HOST}$ENV{SCRIPT_NAME}");
    print "   <pre>\n$message\n   </pre>\n";
    &cgi_lib::html_trailer();
    exit;
}

# --  default enclosing XML tag name

$XMLparentTag = 'member';

# -- OK, lets process a form

# is honeypot anti-spam variable being used?

if (defined($form{"${me}:HoneyPot"})) {  # honeypot var defined
    my $honeypot = $form{"${me}:HoneyPot"};
    if ($form{$honeypot}) {  # honeypot var has content!  its spam!!
        &cgi_lib::html_header("We have reason to believe you are a web robot");
        print "<p>\nYour submission has not been processed.\n</p>\n";
        &cgi_lib::html_trailer();
        exit;
    }
}

# see if verbose flag set
$verbose = (defined $form{"${me}:Verbose"});
# if ENV value empty, dump all CGI environment variables
if (defined($form{"${me}:ENV"}) && ! $form{"${me}:ENV"}) {
    for $envar (keys %ENV) {
        $message .= "$envar$DELIMITER$ENV{$envar}\n";
    }
    $message .= "\n";
}

# USGS addresses only, @ symbol not allowed to suppress used by spammers

for $mailid (split(/[\s,]+/,$form{"${me}:To"})) {
    next unless $mailid;
    if ($mailid =~ /\@/) {
      $message .= "WARNING: '\@' not allowed in address, \@usgs.gov will be appended. $mailid' removed from send list.\n";
    }
    else {
        $maillist .= ','  if $maillist;
        if ( $mailid =~ /_contractor$/ ) {
            $mailid =~ s/_contractor$//;
            $maillist .= &cgi_lib::sanitize("$mailid\@contractor.usgs.gov");
        }
        else {
            $maillist .= &cgi_lib::sanitize("$mailid\@usgs.gov");
        }
    }
}
$form{"${me}:To"} = $maillist;

if (defined($form{"${me}:Cc"})) {
    $maillist = '';
    for $mailid (split(/[\s,]+/,$form{"${me}:Cc"})) {
        next unless $mailid;
        if ($mailid =~ /\@/) {
            $message .= "WARNING: '\@' not allowed in address, \@usgs.gov will be appended. $mailid' removed from send list.\n";
        }
        else {
            $maillist .= ','  if $maillist;
            if ( $mailid =~ /_contractor$/ ) {
                $mailid =~ s/_contractor$//;
                $maillist .= &cgi_lib::sanitize("$mailid\@contractor.usgs.gov");
            }
            else {
                $maillist .= &cgi_lib::sanitize("$mailid\@usgs.gov");
            }
        }
    }
    $form{"${me}:Cc"} = $maillist;
}

if (defined($form{"${me}:From"})) {
    $maillist = '';
    for $mailid (split(/[\s,]+/,$form{"${me}:From"})) {
        next unless $mailid;
        if ($mailid =~ /\@/) {
          $message .= "WARNING: '\@' not allowed in address, \@usgs.gov will be appended. $mailid' removed from From: list.\n";
        }
        else {
            $maillist .= ','  if $maillist;
            if ( $mailid =~ /_contractor$/ ) {
                $mailid =~ s/_contractor$//;
                $maillist .= &cgi_lib::sanitize("$mailid\@contractor.usgs.gov");
            }
            else {
                $maillist .= &cgi_lib::sanitize("$mailid\@usgs.gov");
            }
        }
    }
    $form{"${me}:From"} = $maillist;
}

# mail message delimiter between variables and values

if (defined($form{"${me}:MailDelimiter"})) {
    $DELIMITER = $form{"${me}:MailDelimiter"};
    undef($form{"${me}:MailDelimiter"});
}

$filetype = $FILE_TYPE;
# extract form variables and values
foreach $key (@cgi_lib::FORM_order) {
    $form{$key} =~ s/\0/,/g;     # separate checkbox values with comma
    chomp($form{$key});          # remove trailing newline
    $form{$key} =~ s/\s/ /g;     # multiple lines get turned into spaces

    # skip honeypot variable if we defined one
    next if ( $form{"${me}:HoneyPot"} and $key eq $form{"${me}:HoneyPot"} );

    # append to variable for body of email message
    if ($key =~ /^${me}:/) {
        if ($verbose) {
            $message .= "$key$DELIMITER$form{$key}\n";
        }
        # CGI environment variables
        if ($key eq "${me}:ENV") {
            $envar = &cgi_lib::sanitize($form{$key});
            $message .= "$envar$DELIMITER$ENV{$envar}\n"  if ($ENV{$envar});
        }
        # Time and Date
        elsif ($key eq "${me}:Date") {
            $time_fmt = &cgi_lib::sanitize($form{$key}, '-a-zA-Z0-9_.,% ');
            if ($time_fmt) {
                chop($date = `date +${$time_fmt}`);
                $date = &cgi_lib::sanitize($date, '-a-zA-Z0-9: ');
            }
            else {
                chop($date = `date`);
                $date = &cgi_lib::sanitize($date, '-a-zA-Z0-9: ');
            }
            $form{$key} = $date;
            $message .= "$key$DELIMITER$date\n";
        }
        # append output to file
        elsif ($key eq "${me}:File") {
            $file = &cgi_lib::sanitize($form{$key},'-a-zA-Z0-9_.,');
            $write_file =  &cgi_lib::sanitize("$WRITEDIR/${file}.txt",'-a-zA-Z0-9_.,/');

        }
        # file format
        elsif ($key eq "${me}:FileType") {
            $filetype = &cgi_lib::sanitize($form{$key});
            $filetype = $FILE_TYPE  unless ($SUFFIX{$filetype});  # allow only valid file types
        }
        # XML enclosing tag name
        elsif ($key eq "${me}:XMLparentTag") {
            $XMLparentTag = &cgi_lib::sanitize($form{$key},'-a-zA-Z0-9_');
        }
    }
    else {
        $message .= "$key$DELIMITER$form{$key}\n";
    }
}

# write to file if requested

if ($write_file) {
   # write to the file
   if (open (FILE, ">>$write_file")) {
      # select which fields to save to file
      if ($verbose) {
         @fields = @cgi_lib::FORM_order;
      }
      else {
         @fields = grep(!/^${me}:/, @cgi_lib::FORM_order);
      }
      # file format, xml or plain text
      if ($filetype eq 'xml') {
         print FILE "<$XMLparentTag>\n";
         foreach $key (@fields) {
             print FILE "   <$key>$form{$key}</$key>\n";
         }
         print FILE "</$XMLparentTag>\n";
      }
      else {
         # replace any occurrences of the delimiter character with spaces
         foreach $key (@fields) {
            $form{$key} =~ s/$FILE_DELIMITER/ /g;
         }

         # if the file is brand-new, print the header
         if ( -s $write_file == 0 ) {
            print FILE join($FILE_DELIMITER, @fields), "\n";
         }
         print FILE join($FILE_DELIMITER, @form{@fields}), "\n";
      }
      close (FILE);
   }
   else {
     $message .= "ERROR: Unable to write to file '$write_file'\n";
   }
}

# -- who will get mail

if ($form{"${me}:To"}) {

    # -- remove nasty chars from email subject, if any

    if (defined($form{"${me}:Subject"})) {
        $form{"${me}:Subject"} =~ s/^\s+//;  # remove any leading blanks
        $form{"${me}:Subject"} =~ s/\s+$//;  # remove any trailing blanks
        $subject = &cgi_lib::sanitize($form{"${me}:Subject"},'-a-zA-Z0-9_., ');
    }

    $subject = $subject || 'Form Data';

    # -- send the message

    &cgi_lib::mail_message($subject,$form{"${me}:To"},$message,$form{"${me}:From"},$form{"${me}:Cc"});
}

# send back thank you Page

if ($form{"${me}:RedirectURL"}) {
  &cgi_lib::refer_url($form{"${me}:RedirectURL"});
}
else {
  my $title = $form{"${me}:Subject"} || 'Thank You for Participating';
  &cgi_lib::html_header($title);
  $message =~ s/</&lt;/g;
  $message =~ s/>/&gt;/g;
  print "      <pre>\n$message\n</pre>\n";
  &cgi_lib::html_trailer();
}

# Done.


# --------------------------------------------------------------------
# $Log: process_form,v $
# Revision 1.7  2012/03/09 17:06:48  dboldt
# Add Honey Pot variable to control SPAM generated by USGS security scans
#
# Revision 1.6  2010/03/25 21:55:40  dboldt
# Cross Site Scripting exploit closed
#
# Revision 1.5  2007/02/09 00:19:50  dboldt
# no longer allow '@' in e-mail addresses to mail form results to, '@usgs.gov' will be
# appended to all e-mail addresses specified. This should suppress used by spammers.
#
# Revision 1.4  2006/10/27 21:44:31  dboldt
# secure up the filename variable.  added the ability to define the upper level XML element name.
#
# Revision 1.3  2004/04/06 21:33:08  dboldt
# option added for redirect URL to be supplied for response web page.
# this required moving response to end of processing.
#
# Revision 1.2  2003/07/30 21:21:26  dboldt
# Added process_form:From and process_form:MailDelimiter variables
# to provide additional options for e-mail messages.
# Email addresses are sanitized.
#
# Revision 1.1  2002/03/25 21:30:46  dboldt
# first attempt at xml support
#
# Revision 1.0  2001/11/27 15:07:04  dboldt
# Initial revision
#
# --------------------------------------------------------------------
