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
9 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. james bryan

    hi this is wat i need for my thesis… huhuhuhuhuhuhuh,, hope there is a man can help on my problem

  4. Stephen

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

  5. Clone Scripts

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

  6. Neelkamal

    Hello there,

    I am finding the code useful, could you please guide how could i integrate the SMS HTTP API that will check for each mails and would later send sms for enabled users.

    Thank you.

  7. admin

    I have to be honest with you — I’m not quite sure what you’re asking…

  8. Neelkamal

    Thank you for your reply.

    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.

    Hope this helps.

    Regards.

  9. adforte

    SMS Gateway Developer API

    We’ve gone to great lengths to ensure any developer who wants to interface an application, site or system with our messaging gateway can do so reliably and simply. Our SMPP and HTTP interfaces are fast, simple and reliable, and built in such a way that they are easily manipulated to fit with any system.
    www adforte com

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...

Archives

All entries, chronologically...