3

I created a small shell script which logs all of it's input to a log file, with which I had thought I could replace the sendmail binary and thus achieve a simple way to simulate e-mail delivery without actually setting up a working sendmail.

This failed, however. For reasons I cannot understand.

I've looked at the PHP mail.c source and as far as I can understand (mind you, I'm not very experienced in C), PHP executes and talks directly to the binary (set in sendmail_path). But no log files are being created when I replace the sendmail binary with my script and the script replacing sendmail will always create a log file when it's executed, regardless of if there's input present or not.

The script itself works fine. Its' return codes should conform to that of sendmail's. With the difference that my script always returns 0, regardless of the input, since I'm not really interested in checking if the input is valid - just that I'm getting some.

Is it possible to achieve what I want, i.e. using a sendmail simulator?

The script source is provided below:

#!/bin/bash

LOGDIR=/tmp/sendmail-sim
NOW=$(date +%Y%m%dT%H%M)
CNT=1
FILENAME="$LOGDIR/$NOW.$CNT.log"

while [ -f $FILENAME ]; do
    CNT=$(($CNT + 1))
    FILENAME="$LOGDIR/$NOW.$CNT.log"
done

echo "$0 $*" > $FILENAME

while read BUF
do
    echo $BUF >> $FILENAME
done

exit 0

PS. My current sendmail (or actually, postfix) does receive email from PHP, but I don't want to actually send any email or need to go digging in its' mail queue in development.

nikc.org
  • 14,808
  • 5
  • 44
  • 80
  • To confirm - your sendmail script works when called from the command line, but not from PHP? I'm not up on how the PHP runtime works but I wonder if it is trying to link in the mail binary object, rather than hosting out to the Unix environment?? – JulesLt Sep 14 '10 at 16:36
  • Yes, it works. I haven't tried using the mail command though. (On a tram now, but will test shortly.) – nikc.org Sep 14 '10 at 16:47
  • @Jules: I have now tested my script using the `mail` command and can confirm that it works. Now that I revised the script, it even works correctly. (The first version, even though it worked, only read `stdin` until an empty line, which lead to the message part being omitted.) – nikc.org Sep 14 '10 at 17:31
  • Boy do I feel stupid now. The problem was write permissions on the log folder. – nikc.org Sep 14 '10 at 17:34
  • At least it's not the same problem in shipping software. I'm looking at you, emusic download manager. – JulesLt Sep 15 '10 at 09:35
  • 3
    You should mark this as answered then. – jergason Dec 08 '10 at 01:57
  • 1
    @nikc: FYI it's OK to answer your own question and accept it. This question was useful to me because I'm trying to conditionally discard or pass the message on to sendmail based on the contents. – Steve Clay Jun 30 '11 at 02:06
  • @mrclay, @jergason: Oops, I really meant to do that ages ago. But then I learned about procrastination. – nikc.org Jun 30 '11 at 11:51

4 Answers4

2

I've used Fakemail for this purpose in the past. It accepts SMTP connections but writes all mail to files rather than sending them along as email. There are both python and perl implementations.

http://www.lastcraft.com/fakemail.php

We setup apache to serve the directory that Fakemail was writing to. That was a quick and easy way for staff to view the messages that Fakemail was receiving and review for content, destination, etc. Formatting of HTML emails was a bit whacky for various reasons, so it was not so useful for vetting formatting of html emails.

1

If you need to to test you PHP application's ability to properly format and send email without actually sending them, I suggest you use the Pear Mail package. Fiddling with your system is not a good idea.

If you use Mail from Pear you could switch from sendmail to smtp to a mock implementation of the mail interface by changing the driver from 'sendmail' or 'smtp' to 'mock'.

http://pear.php.net/package/Mail/docs/latest/Mail/Mail.html

http://pear.php.net/package/Mail/docs/latest/Mail/Mail_mock.html

If your code looks like this:

mail('me@example.com', 'My Subject', $message);

Then change it to be testable using PEAR Mail:

include('Mail.php');
function sendEmail($recipient, $subject, $body, $driver = 'mail') {
    $m = Mail::factory("mail");
    $headers = array(
        "From"=>"me@example.com",
        "To" => $recipient,
        "Subject"=> $subject);
    $m->send($recipient, $headers, $body);
    return $m;
}

// In Production:
sendEmail('me@example.com', 'My Subject', $message);

// During testing:
$m = sendEmail('me@example.com', 'My Subject', $message, 'mock');
var_dump($m->sentMessages);

This is very crude, since you should be using PHPUnit or SimpleTest, but this is a topic for another time and place :)

Dan Vatca
  • 301
  • 1
  • 3
  • I appreciate the input, but I need a solution that will work regardless of technology, not just for PHP scripts. PHP was of course the original reason for writing this, but it has been found useful later on with e.g. shell scripts. Dry runs can have one less layer of abstraction in their simulation. – nikc.org Mar 04 '11 at 05:35
  • Even though my example was for PHP I think that you must use a similar method for other languages instead of replacing system binaries. If you do replace them you will have problems. 1. Your software will run differently on another machine until you replace the binary. 2. Other software running on the same machine will behave in a different way than intended. – Dan Vatca Mar 04 '11 at 12:12
1

The problem was User Error, as usually. So, boys and girls, don't forget to check write permissions on all the relevant folders.

nikc.org
  • 14,808
  • 5
  • 44
  • 80
  • 1
    It's not enough nowadays! Most distros now come with Apache configured to use its own /tmp, see: https://stackoverflow.com/questions/10752396/cant-write-to-tmp-with-php-despite-777-permissions-and-no-open-basedir-value . So don't be surprised if "nothing" is written to /tmp/sendmail-sim . – Niccolo M. Sep 13 '18 at 18:23
  • 1
    I wouldn't use this anymore. Use something like Mailcatcher instead https://mailcatcher.me/ – nikc.org Mar 14 '19 at 10:19
1

A note, if you just want to grab stdin and write it into a file, you don't need to loop one line at a time: you can write

cat - >> $FILENAME
glenn jackman
  • 207,528
  • 33
  • 187
  • 305