Tuesday, February 10, 2009

SMSFuBake for CakePHP

Simple CakePHP Component and Helper for sending and receiving SMS text messages to/from mobile devices at no cost using public email to SMS gateways and a gmail account.

At Pascal Metrics, we often use CakePHP to prototype webapps before diving into full J2EE development. Today, we have publicly released the source for a Component and Helper combination we used in a recent prototype that facilitates the free delivery and receipt of SMS messages to/from mobile devices. We are happy to share this contribution with the CakePHP and larger Open Source community.

Usage:
First, add the following Helper and Component to your project. (Mac users can download an installer for all the software used below)

<?php
/**
* SMSFuBake Helper for CakePHP
* @author Eric Simmerman <eric.simmerman @nospam pascalmetrics.com>
* @copyright Pascal Metrics
* @link http://www.cakephp.org CakePHP
* @link http://www.pascalmetrics.com Pascal Metrics, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
class CarrierGatewaySelectorHelper extends AppHelper {

/**
* Helpers used.
**/
var $helpers = array('Form');

/**
* Returns the HTML for the carrier selection form input
*/
function render($additionalOptions = null){
$supportedCarriers = array(
"message.alltell.com" => "Alltell",
"paging.acswireless.com" => "Ameritech",
"txt.att.net" => "AT&T",
"blsdcs.net" => "BellSouth Mobility",
"blueskyfrog.com" => "Blueskyfrog",
"myboostmobile.com" => "Boost",
"csouth1.com" => "Cellular South",
"mobile.kajeet.net" => "kajeet",
"mymetropcs.com" => "MetroPCS",
"ptel.net" => "Powertel",
"sms.pscel.com" => "PSC Wireless",
"qwestmp.com" => "Qwest",
"page.southernlinc.com" => "Southernlink",
"messaging.sprintpcs.com" => "Sprint",
"tms.suncom.com" => "Suncom",
"tmomail.net" => "T-Mobile",
"vmobl.net" => "Virgin",
"vtext.com" => "Verizon",
);
$options = array(
'div' => null,
'empty' => true,
'label' => 'Destination Carrier',
'options' => $supportedCarriers,
);
if( !empty($additionalOptions) ){
$options = array_merge($options,$additionalOptions);
}
return $this->Form->input('carrier', $options);
}
}
?>


<?php
/**
* SMSFuBake Component for CakePHP
* @author Eric Simmerman <eric.simmerman @nospam pascalmetrics.com>
* @copyright Pascal Metrics
* @link http://www.cakephp.org CakePHP
* @link http://www.pascalmetrics.com Pascal Metrics, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
class SmsGatewayComponent extends Object {

var $components = array('Email');

public function sendSms($message, $toPhoneNumber, $carrierGateway, $options=array(), $smtpOptions=null) {

$this->Email->to = '<'.$toPhoneNumber.'@'.$carrierGateway.'>';
if(isset($options['subject'])){
$this->Email->subject = $options['subject'];
}
if(isset($options['from'])){
$this->Email->from = $options['from'];
}
$this->Email->template = null;
$this->Email->sendAs = 'text';

if($smtpOptions){
$this->Email->smtpOptions = $smtpOptions;
$this->Email->delivery = 'smtp';
}
$this->Email->send($message);
return $this->Email->smtpError;
}

}
?>
Now use the following sample controller and view as the basis for your own SMS delivery and receipt app!

<?php
/**
* SMSFuBake Controller for CakePHP
* @author Eric Simmerman <eric.simmerman @nospam pascalmetrics.com>
* @copyright Pascal Metrics
* @link http://www.cakephp.org CakePHP
* @link http://www.pascalmetrics.com Pascal Metrics, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
uses('sanitize');
class SampleTextController extends AppController {

var $name = "SampleText";
var $uses = array();
var $helpers = array('Html', 'Form', 'CarrierGatewaySelector', 'Ajax', 'Session');
var $components = array('Email','Session','SmsGateway','RequestHandler');

function send() {
$options = array('from'=>'<sms@example.com>');
$smtpOptions = array(
'port'=>'465',
'host' => 'ssl://smtp.gmail.com',
'username'=>'sms@example.com',
'password'=>'gmailpassword' );

$error = $this->SmsGateway->sendSms($this->data['SampleText']['message'],$this->data['SampleText']['phonenumber'],
$this->data['SampleText']['carrier'],$options,$smtpOptions);

if($error){
$this->set('deliveryResult', $error);
}else{
$this->set('deliveryResult', "success");
}
$this->render('send',null,null);
}

/**
* Uses IMAP to check the gmail account used in the FROM field of our delivered text messages to receive any SMS replies from
* the given phone number.
*/
function __readResponse($phonenumber) {

$result = null;
$mailbox = imap_open('{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX','sms@example.com', 'gmailpassword')
or die("can't connect: " . imap_last_error());

$message_count = imap_num_msg($mailbox);
for ($i = 1; $i <= $message_count; ++$i) {
$header = imap_header($mailbox, $i);
if (isset($header->from[0]->personal)) {
$personal = $header->from[0]->personal;
} else {
$personal = $header->from[0]->mailbox;
}
$pos = strpos($personal, $phonenumber);
if( $pos !== false ){
$body = imap_body($mailbox, $i);
$this->log("Match! returning body: ".$body);
$result = trim(substr($body, 0, 100));
imap_delete($mailbox,$i);
break;
}else{
$this->log("didn't find ".$personal." in ". $phonenumber);
}
}
imap_close($mailbox);
return $result;
}

}
?>


<div id="maincontent">
<h3>Create a message for delivery via SMS</h3>
<?php
if(isset($deliveryResult)){
if($deliveryResult == 'success'){
echo "<span style='color:green'>Message Delivered!</span>";
}else{
echo "<span style='color:red'>".$deliveryResult."</span>";
}
}
?>
<p>
<?php
echo "\n".$form->create('SampleText', array('action' => 'send', 'id' => 'SampleTextCreationForm'));
echo "\n".$form->input('message', array('label'=>'Your Message', 'tabindex' => 1, 'type' => 'textarea', 'cols' => '30', 'rows' => '5'));
echo "\n".$form->input('phonenumber', array('size' => 20, 'tabindex' => 2, 'label'=>'Destination Phonenumber', 'class'=>'text', 'div'=>'row'));
echo "\n".$carrierGatewaySelector->render(array('tabindex' => 3));
echo "\n".$form->submit('Send My Text', array('tabindex'=>4));
echo "\n".$form->end();
?>
</p>
</div>


Credits:
  • The name SMSFuBake was coined with a nod to Brendan Lim's SMS Fu contribution to the Ruby On Rails community.

1 comment: