Colin McCann Dot Com

Automated Email-to-SMS gateway PHP script

Posted on May 7, 2009 at 9:06 am

I’ll cut right to the awesome. This script runs every minute, checks an IMAP mailbox (that happens to be my work email), and detects if any new messages have been received. If so, all HTML and special characters are stripped (leaving plain ASCII text) from the message body, and truncated to 150 characters. Next, the script sends me a text message through Verizon’s email gateway “from” the original sender’s email address.

This is cool for a number of reasons. One, I don’t have to run an IMAP application on my phone; updates are pushed to me from an external source. Two, I don’t need a data plan to use it (although I may need to increase the number of texts I can receive without additional charges). Three, I can reply directly from my phone (although the message is received from a different address, depending on your carrier). Four, it’s got an accuracy of around one minute, and so far that has been more than adequate.

You need to create a web-writable file (chmod 755) called timestamp.txt in the same directory. I’ll include the cron script at the end of this post.

// filename: sms.php
$debug = false; // false = no output
$error = '';

// Define SMS parameters; your gateway may vary
$sms_to   = '4065551234@vtext.com';

// Define IMAP parameters
$imap_user = 'my.gmail.account';
$imap_pass = 'Secret-Password!';
$imap_host = '{imap.gmail.com:993/imap/ssl}INBOX';

// Define SMTP parameters
$smtp_host = 'smtp.mymailserver.com';
$smtp_port = '25';
$smtp_user = 'sms@mydomain.com'; // a valid user must exist
$smtp_pass = 'Another-Password!';

// Open a pointer to the mailbox
if ($debug) { $error = 'error: imap connection failed'; }
$imap = imap_open ($imap_host, $imap_user, $imap_pass) or die ($error);

// Quick exit
if (imap_num_msg ($imap) == 0) {
	imap_close ($imap);
	if ($debug) { $error = 'error: no messages in mailbox'; }
	die ($error);
}

// What was the last new message?
$old_timestamp = file_get_contents ('./timestamp.txt');

// Get last message
$msg_id = imap_num_msg ($imap);

// Gather IMAP message headers
$headers = imap_headerinfo ($imap, $msg_id);

// What's the timestamp on the most recent message?
$new_timestamp = $headers->udate;

// If it's the same message, get out of here.
if ($new_timestamp <= $old_timestamp) {
	imap_close ($imap);
	if ($debug) { $error = 'error: timestamp match'; }
	die ($error);
}

// Write timestamp of new message to external file
$handle = fopen ('timestamp.txt', 'w');
fwrite ($handle, $new_timestamp);
fclose ($handle);

// Let's look at that email we grabbed
$message = imap_get_message ($imap, $msg_id);

// Close IMAP Connection
imap_close ($imap);

// Okay, who's it from? Let's just do the email address...
// This needs cleaned up a bit; not all addresses include brackets.
$from = trim ($message['from']);
$b1 = strpos ($from, '<') + 1;
$b2 = strpos ($from, '>');
$from = substr ($from, $b1, ($b2 - $b1) );
// Here's an oddity -- gmail doesn't work. Go figure.
$from = str_replace ('gmail.com', 'g-mail.com', $from);

// And what's it say? Get rid of everything except alphanumerics and basic punctuation
$body = $message['body'];
$body = strip_tags ($body);
$body = preg_replace ('/[^A-Za-z0-9!-~]/', ' ', $body);
$body = preg_replace ('/\s\s+/', ' ', $body);

// Cut it off at 150 characters; seems to be the max for Verizon, anyway
$body = (strlen ($body) > 150) ? substr ($body, 0, 147).'...' : $body ;

// Set mail parameters; this sets the 'Return-Path' value
$param   = '-r '.$from;
$subject = '';
$headers = '';

// Send it off!
mail ($sms_to, $subject, $body, $headers, $param);

// IMAP message retrieval; returns a basic array
function imap_get_message ($imap_connection, $msg_no) {

	$header  = imap_header($imap_connection, $msg_no);
	$message = array();
	$message['subject'] = $header->subject;
	$message['from']    = $header->fromaddress;
	$message['to']      = $header->toaddress;
	$message['date']    = $header->date;

	$struct = imap_fetchstructure($imap_connection, $msg_no);
	@$parts = $struct->parts;
	$i = 0;

	if (!$parts) {
		$message['body'] = imap_body ($imap_connection, $msg_no);
	} else {
		$endwhile = false;

		$stack = array(); // Stack while parsing message
		$message['body'] = ''; // Content of message

		while (!$endwhile) {
			if (@!$parts[$i]) {
				if (count ($stack) > 0) {
					$parts = $stack[count ($stack) - 1]["p"];
					$i     = $stack[count ($stack) - 1]["i"] + 1;
					array_pop($stack);
				} else {
					$endwhile = true;
				}
			}

			if (!$endwhile) {
				// Create message part first (example '1.2.3')
				$partstring = '';
				foreach ($stack as $s) {
					$partstring .= ($s["i"]+1) . ".";
				}
				$partstring .= ($i+1);
				if (strtoupper ($parts[$i]->subtype) == "PLAIN") {
					$message['body'] .= imap_fetchbody ($imap_connection, $msg_no, $partstring); // Message
				}
			}

			if (@$parts[$i]->parts) {
				$stack[] = array("p" => $parts, "i" => $i);
				$parts = $parts[$i]->parts;
				$i = 0;
			} else {
				  $i++;
			}
		} // While loop
	}
	return $message;
}

Cron script:

# add to your crontab with 'crontab -e'
* * * * * /home/username/sms-gateway/sms.php
Code
12 comments for this entry:
  1. John

    Hi Colin,

    Thanks for your code, looks awesome. I have an API from http://www.smsgol.com/en/developers which is sending SMS to my customers and to me from my osCommerce store, but now I wanted to change it to send SMS via email address, your code gave me a BIG picture of how to do it and now I’m giving a try.

    Regards and thanks a lot,
    John

  2. admin

    John — best of luck. I’m glad you found this post helpful.

  3. Stephen

    This is awesome. It helped me fix an unrelated problem. Very well-written code as well. I like the idea. :)

  4. Clone Scripts

    Great post, excelent writing. ps. Where are rss feeds? I would love to sindicate news from this blog.

  5. Neelkamal

    I am trying to execute a shell/php script that will be integrated with the postfix server config file which will be executed when a new mail arrives and the shell/php script would check a config file for the domain(s) that are configured to send mail2sms alert and if found would send sms by processing the mail body to the sms gateway api which could be an generic http link for sending sms.

    Regards.

  6. seo services delhi

    Hello! I just wanted to ask if you ever have any problems with hackers? My last blog (wordpress) was hacked and I ended up losing months of hard work due to no back up. Do you have any solutions to prevent hackers?

  7. admin

    Actually, yes, I have a number of passive intrusion detection scripts running on most of my websites designed specifically to prevent this sort of thing (and alert me in the process). I’ve been hacked before, and it’s no fun, but I learned a LOT from it, and I don’t expect it to happen again anytime soon due to the measures I’ve taken. Expect a post detailing some of the methods I use within the next month or two.

  8. R. Wilkins

    Colin, nice post. You have a lot of useful advice. Where do the scripts get saved? Can I use them in a shared environment?

    Thanks,
    Bob

  9. admin

    Bob — the scripts can be saved anywhere that you can reference in your crontab. I was using this script in a shared environment (Dreamhost) for a couple years, up until I got a phone with a data plan that made this obsolete for me.

  10. farooq

    Hi

    How can I connect to my hosting server to access my email through this script. what should I place in {imap.gmail.com:993/imap/ssl}INBOX

    I am trying to connect to imap through port 110 which I have verified with my hosting company.

  11. Website Creation

    Dear Support,

    I have a website built in php and i would like you to insert this code to the website. Can you please inform me of the charges

  12. Rashad

    thanks, it works very well

Leave a Reply

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit our friends!

A few highly recommended friends...