Email to Script

From DreamHost
Jump to: navigation, search

This article shows how to have all email sent to a shell user piped to a script in a language such as Bash, Perl, PHP and Java for processing. Software packages that are designed to react to incoming email (such as Mailman) will need this setup.

Setting up an email and a shell account

NOTE: As of April 2008, tying an email address configured on the "Manage Email" page of the web panel to a shell user is no longer allowed, so the instructions in this section no longer work. (Email addresses that were already tied to shell users have been grandfathered.) What you can do instead is forward email to the shell user, since every shell user appears to implicitly have an email address of the form user@webserver.dreamhost.com.
Here are instructions on how to forward to a shell user (worked as of 12/23/2010): Shell-linked_E-mail

First of all, you'll need set up a shell user and associate an email to it, because the step 2 works only with email accounts that have shell access. More specifically the email address should be associated with a USER that has shell access.

To create a new user and an email address associated to him:

  1. Go to DreamHost control panel.
  2. Open "Users => Manage Users".
  3. Click on "Add New User".
  4. Select the option "Shell account - allows FTP plus ssh/telnet shell access."
  5. Click on "Add User".
  6. In the next screen, you'll be asked to associate an email address to this user.
  7. Select an email address and that's it.

To create a new email address and associate it to an existing user:

  1. Go to DreamHost control panel.
  2. Open "Mail => Manage Mail".
  3. Click on "Create a new email address".
  4. Be sure to check "Deliver to this mailbox".
  5. Select the user in "Mailbox Login:".
  6. Don't worry about the other configuration, as (except explicit configuration) no email will be actually stored in your mail box.
  7. Click on "Create Address" and that's it.

Creating your .forward.postfix

Now, you have to create a file named .forward.postfix in your home directory. If you have ever worked with a dedicated server, you'll see that this file has the same syntax of /etc/aliases.

It's quite easy. First of all, create a file with just a line (see below what you have to write in it) that will pipe the email to your script. Then, name it .forward.postfix save it in your home directory.

By the way, do you know where's your home directory? It is /home/yourusername. For instance, if the username you just created is "john.smith", save your file as /home/john.smith/.forward.postfix. Please note that if you're logging with ftp (and not sftp), the home is just the upper directory. The same is valid if you are logged with ssh.

Piping your email

What must you write in your .forward.postfix? A line like that (quotes inclusive):

"| /home/username/scripts/myscript"

On Dreamhost you have a file server included in home directory paths. Do not include the file server name. So, if your username is mark and if your file is located here: /home/.diplodocus/mark/storedb.php, your path will be just /home/mark/storedb.php.

The | means that the email will be available in the script's standard input. What you have instead of scripts/myscript may vary. For instance:

If you want to run a Bash script, just do this:

"| /home/username/sh/myscript.sh"

If you want to run a Perl script, it's quite the same:

"| /home/username/perl/myscript"

If you want to run a PHP4 script, you need to call the php interpreter, first:

"| /usr/local/bin/php /home/username/php/myscript.php"

If you want to run a PHP5 script, you need to call the php interpreter, first:

"| /usr/local/php5/bin/php /home/username/php/myscript.php"

If you want to run a Java program, you need to call JVM first:

"| /usr/bin/java /home/username/java/myscript.class"

By the way, if don't you know where your interpreter/virtual machine, find it by typing "which php", "which java" or whatever.

Reading the standard input from your script

Now, you just have to read the standard input and do something with your mail. Here you have some lines of code that do that.

Bash

#!/bin/bash
mailfile=`mktemp`
cat - > $mailfile
# Now you have your email on the temporary file
# pointed by $mailfile and can do whatever you want.
# Just don't forget to end the program with:
rm $mailfile

PHP

$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
  $email .= fread($fd, 1024);
}
fclose($fd);

//Now your mail is in $email and you can save it
//to a database or POST it to somewhere.

You can also forward all incoming mails to another mailbox using PHP:

// read from stdin
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
// handle email
$lines = explode("\n", $email);
// empty vars
$from = "";
$subject = "";
$headers = "";
$message = "";
$splittingheaders = true;
for ($i=0; $i < count($lines); $i++) {
	if(trim($lines[$i])=="") {
		// empty line, header section has ended
		$splittingheaders = false;
	}
	if($splittingheaders) {
		// this is a header
		$headers .= $lines[$i]."\n";
		// look out for special headers
		if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
 		$subject = $matches[1];
		}
		if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
		$from = $matches[1];
		}
	} else {
		// not a header, but message
		$message .= $lines[$i]."\n";
	}
}
mail("recipient@example.com", $subject, $message, "From: $from\r\n");

Java

try {
  BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
  String email = "";
  String str = "";
  while (str != null) {
    System.out.print("> prompt ");
    str = in.readLine();
    email = email + "\n" + str;
  }
} catch (IOException e) {
}

//Now your mail is in email and you can save it
//to a database or do something else

Ruby

open("/home/#{username}/mailrecv.txt", "a") do |f|
  f.puts "=" * 50
  f.puts Time.now
  $stdin.each_line do |line|
    f.puts line
  end
  f.puts
  f.puts
end

Handling multiple addresses

The easiest way to handle mail received at multiple addresses through a single shell user is to forward to plus extensions of the shell user, for example:

  • alice@domain -> shelluser+alice@webserver.dreamhost.com
  • bob@domain -> shelluser+bob@webserver.dreamhost.com

Then the mail delivery system will pass your script an environment variable EXTENSION containing the part of the address between the + and @ signs. You can test that variable to pass the mail on to the right place.

If your application is capable of generating a Postfix-style aliases file specifying what to do for each address @domain at which it wants to receive mail (e.g., Mailman can do this), your script can use the /usr/sbin/postalias -q "$EXTENSION" /path/to/aliases command to look up the arrived mail in that file. You might also wish to write a script to translate the aliases file to a form that you can copy and paste into the "Bulk-Edit Forwarding-Only Addresses" box on the Web panel.


Example Code

  • I will use shelluser as the example shell username. Darkflib 10:17, 7 October 2009 (UTC)

We assume that you have already setup the forwards for the email addresses so that <user>@<domain>.com goes to <shelluser>@<webserver>.dreamhost.com eg. shelluser@mountaindew.dreamhost.com

Then create the following files...

.forward.postfix

"|/home/shelluser/filter.php"


filter.php

#!/usr/local/php5/bin/php
<?php
$data=print_r($_SERVER,1);
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
  $email .= fread($fd, 1024);
  }
  fclose($fd);   
  //Now your mail is in $email and you can save it
  //to a database or POST it to somewhere.
file_put_contents('/home/txtd/filter.log',$data.$email);
?>

Note the first line. This allows the php script to be ran like a normal shell script. If you require php4 instead (although it will probably be removed in the future) replace it with /usr/local/bin/php


Then change the permissions of filter.php to allow it to be ran as a standalone script

chmod a+x filter.php

Now whenever an email comes into <shelluser>@<webserver>.dreamhost.com it will pass through filter.php The logic in the example script just dumps some of the environmental vars and the email itself out to the log file filter.log but you can do anything you want with the data.

A word of warning though, PEAR's MIME decoding is very memory hungry, so try to avoid using it with anything requiring the processing of massive emails automatically.

filter.log

Output from script...

Array
(
   [MAIL_CONFIG] => /etc/postfix
   [PATH] => /usr/bin:/bin
   [HOME] => /home/shelluser
   [LOGNAME] => shelluser
   [USER] => shelluser
   [SENDER] => sender@aardvarksrus.com
   [RECIPIENT] => shelluser@mountaindew.dreamhost.com
   [LOCAL] => shelluser
   [SHELL] => /bin/bash
   [DOMAIN] => mountaindew.dreamhost.com
   [PHP_SELF] => /home/shelluser/filter.php
   [SCRIPT_NAME] => /home/shelluser/filter.php
   [SCRIPT_FILENAME] => /home/shelluser/filter.php
   [PATH_TRANSLATED] => /home/shelluser/filter.php
   [DOCUMENT_ROOT] => 
   [REQUEST_TIME] => 1254910894
   [argv] => Array
       (
           [0] => /home/shelluser/filter.php
       )
   [argc] => 1
)
From mike@example.com  Wed Oct  7 03:21:34 2009
Return-Path: <mike@example.com>
X-Original-To: shelluser@mountaindew.dreamhost.com
Delivered-To: shelluser@mountaindew.dreamhost.com
Received: from spaceymail-a2.g.dreamhost.com (caibbdcaaaaf.dreamhost.com [208.113.200.5])
        by mountaindew.dreamhost.com (Postfix) with ESMTP id 56BAF94CD
        for <shelluser@mountaindew.dreamhost.com>; Wed,  7 Oct 2009 03:21:34 -0700 (PDT)
Received: from [192.168.20.172] (cpc3-nfds13-2-0-cust424.8-2.cable.virginmedia.com [80.1.249.169])
        by spaceymail-a2.g.dreamhost.com (Postfix) with ESMTP id D984CEE351
        for <submit@example.me>; Wed,  7 Oct 2009 03:21:33 -0700 (PDT)
Message-ID: <4ACC6BB8.9090606@technomonk.com>
Date: Wed, 07 Oct 2009 11:21:44 +0100
From: Mike <mike@example.com>
Organization: Technomonk Industries
User-Agent: Thunderbird 2.0.0.23 (Windows/20090812)
MIME-Version: 1.0
To: submit@example.me
Subject: test for wiki
X-Enigmail-Version: 0.96.0
OpenPGP: id=C6D70DDC
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
test body text