Dada-ized TFmail

NAME Dada-ized TFmail

SUMMDESCRIPTIONARY

This CGI script converts form input to an email message. It gets its configuration from a configuration file, and uses a minimal templating system to generate output HTML and the email message bodies.

It also allows you to either subscribe or, send a email subscription confirmation to the email address passed to this script from the form that called it, via it's spiffy Dada Mail hooks.

Finally, there has been some simple regular expression checks on the required form fields, to help in warding off any 'bots that may in fact be trying to fill out your forms.

TFmail is *not* a plug-in replacement for FormMail.pl, although it has a lot of the same features. TFmail is configured via text files on the server rather than via hidden form fields, has templates for output, and can support HTTP file uploads.

ORIGINAL SOURCE

This copy of the Dada-ized TFmail.pl is based on the NMS TFmail, version 1.38, which may be downloaded at:

http://nms-cgi.sourceforge.net/scripts.shtml

OBTAINING Dada-ized TFmail.pl

Dada-ized TFmail.pl is located in the, dada/extensions/tfmail directory of the main Dada Mail distribution, under the name, TFmail.pl

COPYRIGHT

(original):

TFMail Copyright 2002 - 2004 London Perl Mongers, All rights reserved

The file MIME_Lite.pm is copyright ZeeGee Software Inc, see the file for details.

Additions and tweaks made by Justin Simoni to add Dada Mail hooks and a simple regular expression check on form field values.

LICENSE

This script is free software; you are free to redistribute it and/or modify it under the same terms as Perl itself.

URL

The most up to date version of this script is available from the nms script archive at <http://nms-cgi.sourceforge.net/>

FILES

In this distribution, you will find the following files:

SCRIPT CONFIGURATION

There are a number of variables that you can change in TFmail.pl which alter the way that the program works.

SCRIPT INSTALLATION

TFmail is installed by copying the file TFmail.pl into your cgi-bin directory. If you don't know where your cgi-bin directory is, then please ask your system administrator.

You must also copy NMStreq.pm and MIME_Lite.pm to the server. You should put them in the location that you configured for LIBDIR above. Leaving LIBDIR set to '.' and uploading the .pm files into your cgi-bin directory will work on most UNIX systems. See the section below on choosing secure locations for files.

You may need to rename TFmail.pl to TFmail.cgi. Again, your system administrator will know if this is the case.

You will probably need to turn on execute permissions to the file. You can do this by running the command "chmod +x TFmail.pl" from your command line. If you don't have command line access to your web server then there will probably be an equivalent function in your file transfer program.

CHOOSING SECURE LOCATIONS FOR FILES

The TFmail.pl CGI requires many support files, such as the .pm files, the .trc configuration files, the .trt output template files and possibly some log files and/or the nms_sendmail script. It's important to consider security when choosing locations for these files on the web server.

The most important consideration is to prevent an attacker from accessing any of these files directly via the web server, and hence gaining information about your setup or access to your log files. Since your e-mail address appears in the configuration file(s), it's important to protect them. The log files may contain even more sensitive information.

For example, if you were to put your configuration files in a 'tfmail' subdirectory of your web site, then an attacker would be able to read them by typing a URL like

  http://www.your.domain/tfmail/default.trc

into his or her web browser. That's bad security.

The best place to put all these files is somewhere outside the web server's document root. That eliminates the whole problem at a single stroke.

However, if you are using an account on a shared web server then you may not have access to any location outside the web server's document root. In that case, you should put all these support files into a subdirectory of your site to which the web server is configured to deny all access. Your hosting provider should be able to tell you how to block HTTP access to a subdirectory of your site.

If you can't configure the web server to deny access to a subdirectory of your site, then maybe you can set up a password protected subdirectory. That's almost as good, so long as you choose a password that nobody will be able to guess.

Finally, if all else fails, a small amount of security can be gained by making the URL to the configuration and other support files hard to guess. For example, if you were to put all the configuration files in a subdirectory of your site but pick the name 'p5xltfe' instead of 'tfmail', then the attacker would need to guess the URL:

  http://www.your.domain/p5xltfe/default.trc

which is harder. Be aware however that this is a weak form of security. Web server bugs and configuration errors that allow this type of precaution to be bypassed are relatively common.

THE CONFIGURATION SCRIPT

If you wish, you can upload the TFmail_config.pl script and use that to interactively create all the .trt and .trc files that you'll need, instead of editing and uploading the .trt and .trc files.

See the comments at the top of TFmail_config.pl for directions as to how to configure that script.

TFmail_config.pl is quite new, and should be considered to be an experimental feature.

OUTPUT CUSTOMIZATION

The .trt files are templates used by TFmail to generate the output HTML and the body of the email. You can edit them to change the look of the output before uploading them to the CONFIG_ROOT directory.

The character combinations "{=" and "=}" are used to mark template directives. Whenever TFmail encounters a template directive, it replaces it with a value. For example, the template directive {= date =} will be replaced with the current date. There are also directives for introducing environment variables and CGI form input values, and a few others.

For example, by default TFmail will use the template email.tft to generate the body of the email. By default that template looks like this:

    %% NMS email template file %%
    Below is the result of your feedback form.  It was submitted
    {= by_submitter =}on {= date =}.
    ----------------------------------------------------------------------
    
    {= FOREACH input_field =}
    {= name =}: {= value =}
    
    {= END =}
    ----------------------------------------------------------------------

The first line just tells TFmail that this is indeed a template file for an email body - it won't be included in the output.

The {= by_submitter =} directive generates the user's email address and real name followed by the string "by ", if there are email address and realname inputs in the form. If TFmail can't work out the user's email address then the {= by_submitter =} directive produces nothing. The "by " string was made part of the directive output so that the sentence would make sense in either case. The word 'by' can be replaced by setting the "by_submitter_by" configuration directive to the required text.

The {= FOREACH input_field =} directive repeats the lines between the FOREACH line and the END line for each field in the form who's name starts with a letter or a number.

If everything goes OK and the email is sent, TFmail presents a success page to the user. By default, that success page comes from the spage.trt template file, which defaults to this:

    %% NMS html template file %%
    <?xml version="1.0" encoding="iso-8859-1"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Thank You</title>
        <link rel="stylesheet" type="text/css" href="/css/nms.css" />
        <style>
           h1.title {
                       text-align : center;
                    }
        </style>
      </head>
      <body>
        <h1 class="title">Thank You</h1>
        <p>Below is what you submitted on {= date =}</p>
        <hr size="1" width="75%" />
    {= FOREACH input_field =}
        <p><b>{= name =}:</b> {= value =}</p>
    {= END =}
        <hr size="1" width="75%" />
        <p align="center">
          <font size="-1">
            <a href="http://nms-cgi.sourceforge.net/">TFmail</a>
            &copy; 2002 London Perl Mongers
          </font>
        </p>
      </body>
    </html>

You can change these templates to anything you like, giving you full control over the look of the output. You don't have to include the {= FOREACH input_field =} directive or the {= date =} directive unless you want to.

You can use "param" directives to get at the values of individual CGI parameters. The directive {= param.foo =} will output the value of the "foo" CGI parameter, if there is one.

For example, if your HTML form has only two inputs, "name" and "age", then your success page template file might look like this:

    %% NMS html template file %%
    <?xml version="1.0" encoding="iso-8859-1"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Thank You</title>
      </head>
      <body>
        <h1>Thank You {= param.name =}</h1>
        <p>
          Thanks {= param.name =} for registering your age
          as <b>{= param.age =}</b>.  Your results have been
          added to our database.
        </p>
      </body>
    </html>

In a similar way, you can access the CGI environment variables via "env" template directives. For example:

    %% NMS html template file %%
    <?xml version="1.0" encoding="iso-8859-1"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>Thank You</title>
      </head>
      <body>
        <h1>Thank You {= param.name =}</h1>
        <p>
          Thanks {= param.name =} for registering your age
          as <b>{= param.age =}</b>.  Your results have been
          added to our database.
        </p>
        <p>
          Note: we have logged your IP address as {= env.REMOTE_ADDR =},
          and we will be reporting you to the FBI if you lied about
          your age.  Have a nice day.
        </p>
      </body>
    </html>

You can also use these "param" and "env" template directives in the email template, to get finer control over the body of the email.

CONFIGURATION FILES

TFmail reads its configuration from a configuration file. By default, that file is default.trc in whatever directory you set for CONFIG_ROOT above.

If you have several forms on your site using TFmail, each will need its own configuration file. You can control TFmail's choice of configuration file via the "_config" hidden form field. For example, if you added the line:

  <input type="hidden" name="_config" value="foo" />

to one of your HTML forms, then TFmail would use foo.trc in the CONFIG_ROOT directory as its configuration file when processing that form.

The choice of configuration file is the only thing that can be configured via a hidden form field.

The first line of the configuration file has to be exactly the text:

  %% NMS configuration file %%

Lines starting with '#' are ignored, and configuration values are set by putting the name of the configuration setting at the start of a line, followed by a ':' character, followed by the value for that setting. The value can be split over several lines.

One configuration value that you must set is "recipient", the email address to which the form results should be mailed. For example, your configuration file might look like:

    %% NMS configuration file %%
    #
    # This is my configuration file
    #
    recipient: me@my.domain

You can have several recipients set, in which case the form results will be copied to all of them:

    %% NMS configuration file %%
    #
    # This is my configuration file
    #
    recipient: me@my.domain, some-else@my.domain

Other things that can be set in the configuration file are:

Dada Mail Hooks in the Configuration Files

Once you have TFmail.pl running well without the Dada Mail hooks, it's a good time to add them. The Dada Mail hooks for TFMail are simply configuration name/value pairs, like the ones above.

In you form, you'll basically want to pick an email address that will be submitted from the form itself, and either subscribe the email address to a list or, send a confirmation email to the email address for a list.

The trick is to tell Dada Mail where all this information is coming from. Easy enough.

In your configuration template, the value set for, email_input will be the email address that the Dada Mail hooks will use. If you set, email_input like so:

 email_input:    email

in your configuration template file, and your form looks like this:

 <input type="text" name="email" /> 

and a person fills out this field with, me@example.com, Dada Mail will work with, me@example.com. Hope that's clear.

The Dada Mail hooks introduce one new configuration name/value pair. It is:

In the form itself, you'll also need to have one of two fields, named either:

These two form fields, are form fields, so that they are toggle-able - always give the form-filler-outer a chance to say, yes or no.

As far as Dada Mail hooks go - you are done. Wasn't too hard, no?

Reqular Expressions for Required Form Fields in Configuration Files

Another tweak that makes the Dada-ized TFmail.pl somewhat different than the original TFmail.pl is that there's a simple way to validate the actual values of the form field.

For example, let's use an example of one of the Dada Mail-specific form fields, dada_mail_confirm_email.

Let's say we want this field to be, required, and also want it to be either, 1, for, Yes! Send me a confirmation to subscribe! or, 0 for, No thanks!

A snippet of the HTML form itself may look like this:

 <p>
  <input type="radio" name="dada_mail_confirm_email" value="1" />Subscribe to my fancy list!<br /> 
  <input type="radio" name="dada_mail_confirm_email" value="0" />DO NOT Subscribe  to my fancy list!
 </p>

Our configuration template would have the following entry:

 required: dada_mail_confirm_email

That will tell TFmail that we *need* something for, dada_mail_confirm_email, even if it's, 0

Add to this, the following:

 required_regex_dada_mail_confirm_email: (1|0)

This is a special, handy-dandy method to pass a regular expression to TFmail, concerning the dada_mail_confirm_email form field.

You can use this for any required field, by adding an entry named, required_regex_name_of_you_field:, followed by a regular expression.

For example, Let's say we have a form field we'd like to have people put in their favorite color, but we'd like only to use the primaries, like red, yellow or blue. In our configuration template, we could put:

 required: favorite_color
 required_regex_favorite_color: (red|yellow|blue)

You can probably try to get fancier with the regular expressions, but I'd suggest keeping it pretty simple.

INLINE TEMPLATES

If you don't want to have a separate file for each template, you can choose to put some or all of the templates directly into the configuration file instead, putting a '%' character at the start of each line.

For example, rather than having the configuration file line:

 email_template: my_email_template

... and a separate my_email_template.trt file with the text:

    %% NMS email template file %%
    This is the email.
    
    remote address: {= env.REMOTE_ADDR =}
    user agent:     {= env.HTTP_USER_AGENT =}
    referer:        {= env.HTTP_REFERER =}
    
    The name they entered was: {= param.name =}

... you could just have the block:

    email_template:
    %This is the email.
    %
    %remote address: {= env.REMOTE_ADDR =}
    %user agent:     {= env.HTTP_USER_AGENT =}
    %referer:        {= env.HTTP_REFERER =}
    %
    %The name they entered was: {= param.name =}

... in the configuration file. You can do this for any of the configuration variables listed above that expect a template file as a value.

Note that you must leave out the %% NMS ???? template file %% line when using an inline template like this.

WRITING TO A LOG FILE

If you want to keep your log files in /usr/local/tfmail/logs, then you would set LOGFILE_ROOT to '/usr/local/tfmail/logs', and you might add the following lines to your .trc file:

    logfile: foo
    log_template:
    %{= date =}|{= env.REMOTE_ADDR =}|{= param.email =}

One problem with log files done this way is that the values input by the user can contain '|' and newline characters, so it's possible for a malicious user to mess up your log files.

However, TFmail will change tag-like constructs such as "<foo>" found in the user input into "< foo>" when it writes the log file, so using an HTML-like format for log files makes them more resilient:

    logfile: foo
    log_template:
    %{= date =}<x>{= env.REMOTE_ADDR =}<x>{= param.email =}<eol>

FILE UPLOADS

To enable file uploads, you will need to set the ENABLE_UPLOADS constant in the script to 1. You will also need to set the correct encoding in your HTML form:

 <form method="post" enctype="multipart/form-data" action="/cgi-bin/TFmail.pl">
 ...

...and you will need to configure one or more inputs to be file uploads in your configuration file. The configuration directive to make an input act as a file upload is upload_*, with the * replaced by the name of the input. The value is a list of acceptable file extensions for the uploaded file, with the last one being the default that gets used if the uploaded file has another extension.

For example, suppose you want the user to be able to upload a picture. You might put in the configuration file:

 upload_picture: gif jpg jpeg png xxx

and you might put in the HTML form:

  A picture of yourself:
  <input type="file" name="picture" />

If you want the MIME content-type of the upload to be recorded in the email, then you should add:

  {= content_type.picture =}

to the email template file.

INSERTING TEXT INTO HTML FILES

As well as sending email and writing log files, TFmail can be made to insert some text into the middle of an existing HTML file. The HTMLFILE_ROOT constant in the script should be set to the filesystem path under which all of the HTML files that you might want TFmail to modify reside.

For example, to use TFmail.pl as a guestbook script, with your guestbook stored in the file /www/sites/31337/guestbook.html, you might set the HTMLFILE_ROOT constant to '/www/sites/31337' and put the following in a guestbook.trc file:

    %% NMS configuration file %%
    
    no_email: 1
    
    modify_html_files: guestbook
    
    htmlfile_template_guestbook:
    %   <p><b>Date:</b>{= date =}</p>
    %   <p><b>Name:</b>{= param.name =}</p>
    %   <p><b>Comments:</b>{= param.comments =}</p>
    %   <hr />
    
    required: name,comments
    
    missing_template:
    %<?xml version="1.0" encoding="iso-8859-1"?>
    %<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    % "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    %<html>
    % <head>
    %  <title>Missing Fields</title>
    %  <link rel="stylesheet" type="text/css" href="/css/nms.css" />
    % </head>
    % <body>
    %  <p>
    %   You must fill in both the <b>name</b> and <b>comments</b> fields.
    %   Please try again.
    %  </p>
    %  <form method="post">
    %   <input type="hidden" name="_config" value="guestbook" />
    %   <p>Your Name: <input type="text" name="name" size="30" value="{= param.name =}" /></p>
    %   <p>
    %    Comments:<br />
    %    <textarea name="comments" cols="60" rows="4">{= param.comments =}</textarea>
    %   </p>
    %   <p><input type="submit" /> * <input type="reset" /></p>
    %  </form>
    %  <hr />
    % </body>
    %</html>
    
    redirect: http://www.your.domain/thankyou.html

... and you might put something like this in the guestbook.html file:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
     <head>
      <title>Guestbook</title>
      <link rel="stylesheet" type="text/css" href="/css/nms.css" />
     </head>
     <body>
      <h1>Guestbook</h1>
      <p>Thank you for visiting our pages.  We would love it if you would
       <a href="addguest.html">Add</a> to this guestbook we are keeping!</p>
      <hr />
    <!-- NMS insert below -->
     </body>
    </html>

... and addguest.html would probably look something like:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
     <head>
      <title>Add to our Guestbook</title>
      <link rel="stylesheet" type="text/css" href="/css/nms.css" />
     </head>
     <body>
      <h1>Add to our Guestbook</h1>
      <p>Fill in the blanks below to add to our guestbook.</p>
      <hr />
      <form method="post" action="http://www.your.domain/cgi-bin/TFmail.pl">
       <input type="hidden" name="_config" value="guestbook" />
       <p>Your Name: <input type="text" name="name" size="30" /></p>
       <p>Comments:<br /><textarea name="comments" cols="60" rows="4"></textarea></p>
       <p><input type="submit" /> * <input type="reset" /></p>
      </form>
      <hr />
     </body>
    </html>

ADVANCED GUESTBOOK

The trouble with the guestbook scheme described above is that it writes the entries directly into the guestbook HTML file, so you can't change the style in which the entries are shown without going back and editing all the old entries by hand.

There is an alternative scheme, using the script GBview.pl, which formats the guestbook when it is displayed. GBview.pl also allows you to split a long guestbook into multiple pages.

To use the GBview scheme, adapt the example GBview configuration file supplied as gb.trc to your requirements, and upload it. You will also need to edit the configuration section of GBview.pl and upload it to the same place as TFmail.pl. Give all the configuration constants in GBview.pl the same values that you used for TFmail.pl.

You will need to have the HTMLFILE_ROOT configuration constant set in both TFmail and GBview. Since the HTML file that TFmail rewrites isn't viewed directly by the user in this case, it's best to set HTMLFILE_ROOT to a location outside of the web server's document root. See the section on CHOOSING SECURE LOCATIONS FOR FILES above.

You will need an addguest.html file, such as the one shown in the example above. You must change the value of the hidden "_config" input to "gb" in the HTML form. You will also need to upload an empty gbdata.html file to the HTMLFILE_ROOT directory on the server.

See http://cleaton.net/nms/agb.html for a working example of a guestbook of this type.

SUPPORT

For support of this script please email:

  <nms-cgi-support@lists.sourceforge.net>

Dada Mail Project

Download

Installation

Support