Search

How To Send Email With Perl, Part II

0 views

Script Installation and Environment

Before you begin sending mail from Perl, you need a working script that can parse user input, validate it, and hand the data off to an SMTP server. The script that accompanies this part of the tutorial is designed to do all that, plus a few extra niceties that make it easier to use in a real‑world web application.

Download the full script from the official site. The file contains the entire program, not just the snippets that will appear in this guide. It is a single CGI file, ready to drop into your server’s /cgi-bin directory. Click here to fetch it.

The first thing you’ll notice is that the script starts with use strict; and use warnings;. These directives force the interpreter to keep a tighter leash on variable names and catch common mistakes. When a script is run under mod_perl, a popular Apache extension that keeps a Perl interpreter alive between requests, strictness is especially valuable. It prevents subtle bugs from creeping in when many requests hit the same instance of the script.

Because the script runs as a CGI program, it expects to receive data either through the query string (GET) or the request body (POST). In either case, the data arrives in %ENV and %ENV{'QUERY_STRING'}, which the script then parses into a hash called %In. This hash holds every form field name as a key and the user’s input as the value. It works for simple scripts as well as for the more complex auto‑responder example that follows later in this tutorial.

One of the first checks the script performs is whether an e‑mail address was supplied in the URL itself, mirroring the approach used in Part I of the series. If the user navigated to /cgi-bin/part2.cgi?email=john@example.com, the script pulls that value out and stores it in $In{'email'}. It also sets a flag that tells the later code that the address came from the query string rather than a form submission. This flag matters when deciding which validation rules to apply.

Once the script knows where the e‑mail address lives, it proceeds to the next stage: collecting any form field values that might have been posted to it. Because the script can be used in two different ways - either as a simple “send me a mail” link or as a full auto‑responder - it is built to handle both scenarios gracefully. The parsing logic is split into a dedicated subroutine, ParseValues, which loops over the available input and populates the %In hash. If a field is missing, the hash simply has no key for that name; later validation steps can pick that up.

All of this setup may sound like a lot of plumbing, but once it’s in place the rest of the script becomes a straightforward series of checks and actions. The next section walks through the exact flow of data, the validation steps that protect you from malformed input, and the error handling strategy that keeps the user experience smooth even when something goes wrong.

Form Data Processing and Validation

After the input hash is ready, the script must decide whether the information supplied is sufficient to send an e‑mail. Two core concerns dominate this phase: required fields and e‑mail syntax. The code is split into two small but crucial subroutines, CheckRequired and ValidEmail, that work together to enforce these rules.

When you build a form that posts to this CGI script, you can signal which fields are mandatory by adding a hidden input named required. Its value is a comma‑separated list of field names. For example:

<input type="hidden" name="required" value="firstname,email">

The CheckRequired routine splits that string, trims whitespace, and loops over the list. For each field it looks up the corresponding value in %In. If a field is missing or empty, CheckRequired sets an error flag and constructs a user‑friendly message explaining which field needs attention. The message is sent back to the browser through WebPageErrorMessage, a subroutine that outputs a minimal HTML page with the error text. The script then jumps to a label named BOTTOM to skip any further processing, ensuring the e‑mail isn’t sent with incomplete data.

The e‑mail syntax check is performed by ValidEmail. The routine uses a simple regular expression that accepts the majority of legitimate addresses while filtering out obvious mistakes - missing “@”, missing domain, or illegal characters. If the address fails the test, CheckRequired calls WebPageErrorMessage again with a suitable prompt. This layered approach means a single error message can be produced for multiple failures, making it easier for users to correct the form.

It’s worth noting that the script deliberately avoids calling exit() when an error occurs. Under mod_perl, an exit() would kill the interpreter for all users. Instead, the script uses a goto BOTTOM jump, which is a lightweight way to jump to the end of the script while preserving the interpreter’s state. The label BOTTOM sits just before the final HTML output, so control simply slides past the email‑sending section and to the thank‑you page or error message.

Beyond the mandatory field checks, the script also respects a thankyoupage hidden input. If you include that field in your form, the script will redirect the user to the specified URL after a successful send. If you leave it out, the script falls back to a generic “thanks for your submission” page embedded in the code. This flexibility means you can quickly flip between a simple one‑page experience and a fully customized flow that matches your website’s branding.

With validation in place, the next step is to craft the e‑mail itself. The script is built around a single subroutine called SendEmail, which takes care of formatting the headers, choosing between plain text and HTML, inserting placeholder values, and sending the message through the local mail transport. The following section dives into the details of how that subroutine works and how you can tweak it to meet your needs.

Composing and Sending the Email

Once the input is verified, the script calls SendEmail. Inside that routine the first decision is the e‑mail format. The script supports both plain text and HTML, and you can specify the desired type with a form field named html. If the user selects “yes” or sets a hidden input with that value, the routine will treat the body as HTML; otherwise it defaults to plain text. The HTML route adds a couple of headers that inform the recipient’s mail client about the MIME type and character set:

Mime-Version: 1.0
Content-type: text/html; charset="iso-8859-1"

These headers are crucial for proper rendering. Many older mail clients ignore them and display the raw HTML tags, so you should keep the content as simple as possible if you expect a broad audience. For example, avoid using advanced CSS or JavaScript, and stick to tables, <b>, <i>, and <a> tags. Also, always use fully qualified URLs (starting with http:// or https://) for images and links so that the message looks correct no matter where it is opened.

Both plain text and HTML bodies are defined directly inside the script, between marker lines THE_PLAIN_EMAIL and THE_HTML_EMAIL. This design keeps the code self‑contained and eliminates the need to read external files. It also makes it easy to switch between the two by simply editing the text inside the markers. For example, a plain‑text body might read:

To: []
Subject: Welcome to our mailing list!

Hi [[firstname]],
Thank you for signing up. We hope you enjoy our content.

Notice the double square brackets around placeholder names like [[firstname]]. The SendEmail routine scans the body for these markers and replaces them with the corresponding value from %In. If a placeholder has no matching form field, the routine removes the marker, leaving no trace in the final message. This approach gives you true personalization: the user sees their own name or other data you collected in the form.

HTML bodies work the same way. In addition to the To: header, you can include any valid HTML you like. The placeholder substitution runs before the email is handed off to the mail transport, so every instance of [[firstname]] or any other name will appear with the user’s input. If you need to embed dynamic data inside an HTML table or a styled section, the same substitution logic applies.

When it comes to actually sending the message, the script relies on Perl’s built‑in Net::SMTP module. This module talks to the local mail transfer agent (often sendmail or postfix) and queues the message for delivery. Because the script runs in a web environment, it does not open a full SMTP session for each request; instead, it uses the default local configuration, which is usually sufficient for small to medium traffic sites.

The routine also includes basic error handling. If Net::SMTP fails - say, the local mail service is down - the script logs the failure to the server’s error log and displays a generic “something went wrong” message to the user. In a production environment you might want to add more sophisticated retry logic or notifications to an admin account, but the default behaviour keeps the script lightweight and easy to understand.

Finally, after a successful send, the routine triggers the thank‑you page logic. If the user supplied a thankyoupage URL, the script redirects there; otherwise it falls back to a short embedded message that thanks the user and informs them that a confirmation email has been sent. This final step completes the cycle from form submission to e‑mail delivery and back to the user’s browser.

Putting It All Together: Auto‑Responder Form Example

The power of this script shines when you pair it with a well‑designed HTML form. Below is a full example that you can drop into a page and adjust to your own style. The form uses the POST method and points to the CGI script.

<form method="POST" action="/cgi-bin/part2.cgi">
  <input type="hidden" name="thankyoupage" value="http://example.com/thankyou.html">
  <input type="hidden" name="required" value="firstname,email">
  First Name: <input type="text" name="firstname"><br>
  Email: <input type="text" name="email"><br>
  <input type="radio" name="html" value="yes">HTML
  <input type="radio" name="html" value="no">Plain text
  <input type="submit" value="Subscribe">
</form>

In this example, the required hidden input tells the script that both the first name and the email are mandatory. The thankyoupage field ensures that after a successful send the user lands on a branded thank‑you page. The radio buttons let the visitor choose whether they prefer an HTML or plain‑text version of the confirmation email.

Because the script replaces placeholders on the fly, you can embed any additional fields you need. For instance, if you ask for a user’s company name, add a field named company to the form and then use [[company]] inside the e‑mail body. The same logic works for both plain text and HTML formats.

Looking ahead, the script is already designed with extensibility in mind. Future versions will add the ability to load e‑mail templates from external files, so you can edit messages without touching code. You’ll also get support for sending a single message that contains both plain text and HTML parts, ensuring that every client - whether it can render rich content or not - receives a suitable copy. Attachment handling will be added as well, allowing you to send PDFs, images, or other files directly from the form.

In addition to the classic auto‑responder pattern, the script can be repurposed to handle subscription requests sent to a dedicated address such as subscribe@example.com. By parsing the incoming e‑mail, you can trigger the same sending logic, automatically sending a welcome note back to the new subscriber. This makes it easy to maintain a clean, single‑source workflow for all your mailing list interactions.

Whether you’re building a small newsletter signup or a complex marketing automation system, this script provides a solid foundation. By tweaking the placeholder logic, adding custom validation, or extending the email composition, you can adapt it to any workflow while keeping the code clear and maintainable.

Suggest a Correction

Found an error or have a suggestion? Let us know and we'll review it.

Share this article

Comments (0)

Please sign in to leave a comment.

No comments yet. Be the first to comment!

Related Articles