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
July 3rd, 2009 on 12:32 am
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
July 6th, 2009 on 12:39 pm
John — best of luck. I’m glad you found this post helpful.
December 8th, 2009 on 3:32 pm
This is awesome. It helped me fix an unrelated problem. Very well-written code as well. I like the idea.
June 7th, 2010 on 8:32 am
Great post, excelent writing. ps. Where are rss feeds? I would love to sindicate news from this blog.
June 30th, 2010 on 7:51 am
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.
March 17th, 2011 on 1:06 pm
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?
April 16th, 2011 on 4:53 pm
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.
October 25th, 2011 on 5:25 am
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
October 25th, 2011 on 10:13 am
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.
November 26th, 2011 on 11:03 pm
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.
December 13th, 2011 on 9:04 pm
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
January 16th, 2012 on 6:26 am
thanks, it works very well