Perl is a terrific text-processing language. This makes it an ideal tool for building an automated response system that reads and responds to commands via e-mail. Such a system is broken into two logical pieces. The main portion of the code deals with reading the mail from the Post Office Protocol (POP) server, parsing it into commands, and sending responses via a Simple Mail Transfer Protocol (SMTP) server. The other portion of the code deals with each individual command using a plug-in architecture.
This program requires the Net::POP3 and Net::SMTP modules. If you don’t have these installed, visit your nearest CPAN archive to download and install them.
The basic system
The function of the system is quite simple (see Listing A). The configuration section at the top defines everything needed to operate the script. By consolidating the information into the configuration section, you isolate changes and minimize the risk of missing an important configuration parameter. Consolidating configuration information also forces you to think more in the abstract and therefore create more reusable code. See the comments in the code to set the values for various configuration parameters.
First, we create or open the log file and select it as the default for output. This means that all of our print statements will go into the log instead of to the screen. If you want something on the screen, you must use the STDERR handle. If you want your log to contain only information from the last run, simply change the double >> in the open call to a single >.
Next, we open and read the alias file. See the next section, “Using aliases,” for information about what aliases are and how they are used. For now, it’s just important that we read and process the alias file. The results end up in the hash alias.
The last major step is to process each mail account specified in the POPACCTS array. This array holds the server, user name, and password for each mail account you want to process. This array is really an array of array references. When we iterate across each element of POPACCTS, we get back an array reference. To get at the actual data in the array, we have to dereference the variable and then access the array element. In essence, POPACCTS is an array of pointers to (memory addresses of) anonymous arrays. Each of these anonymous arrays contains three elements: a server, a user ID, and a password.
The last step is to restore output to the standard output handle and then close the log file.
The actual processing of inbound mail is done by the CheckMailbox() function. This is really the heart of the program. CheckMailbox opens a connection to the POP server, logs in, and retrieves the number of messages that are waiting on the server. The code reads each waiting message and processes the header looking for the from and subject fields.
Once the header has been processed, the code moves on to the body of the message. We check to see that there are no more than 20 lines in the message. If there are, this is probably not a set of commands but some sort of text message that we should ignore. If the message is acceptable, each line is processed.
To process a line, we split it after the first space. The word before the space is the command; everything after the space is considered parameters to the command. Next, the code checks the command against the alias array to determine the name of the program to execute. If there is no alias, the program name is assumed to be the same as the command. We then check to confirm that the program file exists. If it doesn’t exist, we return an error. If it does exist, we execute it, passing whom the message was from, the original command (not the translated alias), and the parameters. Whatever output the plug-in writes to standard output is captured and returned to the sender.
A note about security
Anytime you accept data from outside your system, you have to treat it as though it is dangerous. In the case of the automation system, we are passing user-supplied data on the command line to the plug-in module. Both the from address and the optional command parameters get passed along. Someone who wanted to violate your system could include a command separator in the from address. This could cause you to format the hard disk or delete system files or send your password file to the hacker. To prevent this from happening, you have to run any text that will get passed on the command line through a filter to strip out dangerous characters.
The automation system supports aliases for commands (see alias.cfg). When a command comes in, it is processed against the alias list before searching for a plug-in to execute. This allows you to have several commands that are all handled by the same plug-in. For example, if you have a list server plug-in that manages a list of names for a mailing list, it would make sense to use the same code for both the SUBSCRIBE and the UNSUBSCRIBE commands.
To create an alias, put a line in the alias.cfg file that lists the command followed by one or more spaces and then the plug-in used to execute that command. Here is an example from the included alias.cfg file:
This says that subscribe and unsubscribe are both handled by the listbot plug-in module.
Building plug-in modules
Plug-in modules can do anything your imagination can conceive and programming skills can build. The plug-in interface is simple. The plug-in gets passed the e-mail address of the sender, the command, and any parameters. It is up to the plug-in to determine which of these fields to use, if any.
To return a response to the sender, the plug-in simply needs to print the output to standard out. Anything that is printed will be captured and returned to the sender by the main program.
Example plug-in modules
I’ve included a few sample plug-in modules to get you started. You can use these as a jumping-off point to create your own modules that will handle whatever you need automated. You can download the code for this article, as well as the modules below:
- The listbot module is a simple script that processes commands to add and remove e-mail addresses on a list. The list is implemented as a file. This script makes use of the alias commands subscribe and unsubscribe.
- The weather module allows you to retrieve text weather forecasts from the National Weather Service. The script is configured by default for the state of Utah. You can pass a zone number to get weather for different zones.
- The UPS module allows you to retrieve tracking information from the UPS Web site based on a package ID. You must specify a UPS package ID as a single string of alphanumeric characters without any spaces.
Run the script every few minutes
The last step is to schedule your automation script to run every few minutes to check for mail. You can do this using the scheduling services available on your operating system. If you are on a UNIX variant, you can use a CRON job to schedule this task. Under Windows, use the scheduler service to run the job. There are also a number of freeware, shareware, and commercial task scheduling tools available for Windows.
The basic automation module handles all the processing of inbound e-mail, splitting and processing the text, and the generation and sending of responses. This relieves you of the major housekeeping chores associated with managing the e-mail session. You can develop any plug-in module that suites your needs. The CPAN archive is full of great tools to retrieve and process information from the Web. This makes it very simple to create modules to monitor the weather, retrieve stock quotes, track packages, manage e-mail lists, monitor bids on eBay, or anything else you need your automated assistant to do for you.
Please share your ideas for plug-in modules by posting to the discussion at the bottom of this article.