<?php // apress.php // quick and dirty apns "self-contained" example server for apress chapter // reflects messages from known users to the entire user base // AUTHOR INFO // Copyright Joe Pezzillo, 2009, All Rights Reserved // // NO WARRANTY, USE AT OWN RISK // USE OF CODE IS AGREEMENT TO HOLD AUTHOR HARMLESS AGAINST ANY CLAIMS // AUTHOR MAKES NO WARRANTIES OR REPRESENTATIONS OF ANY KIND WHATSOEVER // REQUIRES: BASIC PHP SITE HOSTING EXPERIENCE // YOUR CERTIFICATE $certificate = ''; $certificate_password = ''; // passphrase for the certificate file, we left it blank $APPLE_SERVER = 'ssl://gateway.sandbox.push.apple.com:2195'; // YOUR MYSQL DATABASE CONNECTION $hostname = 'HOST'; $username = 'USERNAME'; $password = 'PASSWORD'; // bogus token, if you use it, remember to remove it from your DB! $example_token = '1dfc17527407d39bcd1d69defa7ab3e7ae5f9d7a87b0cc69bd3020b369c0b8d9'; // STEP ONE: // SETUP THIS DATABASE: /* CREATE TABLE `apresschapter`.`apressdevices` ( `device_token` VARCHAR( 64 ) NOT NULL , `username` VARCHAR( 32 ) NOT NULL ) ENGINE = MYISAM COMMENT = 'Simple database to hold Apress Push Chapter Sample Server Registrations' */ $database = 'apresschapter'; $table = 'apressdevices'; $db_link = mysql_connect($hostname, $username, $password); mysql_select_db( $database ) or die('ConnectToMySQL: Could not select database: ' . $database ); $result = ini_set ( 'mysql.connect_timeout' , '60' ); // take in either a registration, or a message and respond apropriately // registration: incoming: devicetoken + (arbitrary, username?) // message: incoming: devicetoken + message // ?token=TOKEN&cmd=(REG|MSG)&(name|msg)=(NAME|MSGBODY) if( ! isset( $_GET['token'] ) ) { // all commands require a device token $q = "SELECT DISTINCT(device_token),username FROM $database.$table"; $result = mysql_query($q); $count = mysql_num_rows($result); if( $count > 0 ) { $rec = mysql_fetch_assoc($result); $example_token = $rec['device_token']; // the first device registered, presumably the developer's } $infotext = <<<INFOTEXT <font face="monospace"> <br/> <blockquote> Apress.php<br/> Sample server program for use with Apress Push Notification Chapter<br/> <br/> Usage:<br/> ?token=DEVICE_TOKEN&cmd=(reg|msg)&(name|msg)=(USERNAME OR MESSAGE)<br/> <br/> e.g.<br/> <br/> Register a token:<br/> ?token=$example_token&cmd=reg&name=test<br/> <br/> Send a message:<br/> ?token=$example_token&cmd=msg&msg=Hello,%20World!<br/> <br/> <br/> <br/> You have $count devices in your database. </blockquote> </font> INFOTEXT; print( $infotext ); } else { if( isset( $_GET['cmd'] ) && ( $_GET['cmd'] == 'reg' || $_GET['cmd'] == 'msg' ) ) { if( $_GET['cmd'] == 'reg' ) { if( isset($_GET['name']) ) { // GOOD: write token + name to persistent registered devices list print( "Write token to registrations<br/>" ); $token = mysql_escape_string($_GET['token']); $name = mysql_escape_string($_GET['name']); $q = "INSERT INTO $database.$table (`device_token`,`username`) VALUES ('$token','$name' )"; $result = mysql_query($q); if( mysql_affected_rows() > 0 ) { print( "success registering" ); } else { print( "error while registering" ); } } else { // registration requires a name to be passed it to go with token } } else if( $_GET['cmd'] == 'msg' ) { if( isset($_GET['msg']) ) { // GOOD: we now have the message body // is token in registered devices list? // is message compliant (length, characters) // send it print( "Send message<br/>" ); $q = "SELECT DISTINCT(device_token),username FROM $database.$table"; $result = mysql_query($q); $devices = array(); $validated_sender = FALSE; $sender_name = 'Unknown'; while( $rec = mysql_fetch_assoc($result) ) { if( $_GET['token'] == $rec['device_token'] ) { $validated_sender = TRUE; $sender_name = $rec['username']; } print( "user+device:" . $rec['username'] . '+' . $rec['device_token'] . "<br/>" ); // build the device list to be sent to here array_push($devices,$rec['device_token']); } if( $validated_sender ) { // send them in one batch here // check the name here? - future development: is the submitted name the same as the registered name? if( isset( $_GET['name'] ) && strlen($_GET['name']) ) { $sender_name = $_GET['name']; } $alert = $sender_name . ' says ' . $_GET['msg']; $sound = ''; $badge = ''; $custom = $sender_name; send_apns_to_devices($devices,$alert,$sound,$badge,$custom); } else { // we don't know the sender token } } else { // message command requires a message } } else { // unknown command - shouldn't get here, but for future expansion } } else { // invalid command, either command not present, or not valid command } } function send_apns_to_devices($device_list,$alert,$sound,$badge,$custom) { global $APPLE_SERVER; global $certificate; global $certificate_password; // The actual notification payload $notification = array(); if( strlen($alert) > 0 ) { $notification['alert'] = $alert; } if( strlen($sound) > 0 ) { $notification['sound'] = $sound; } if( is_numeric($badge) ) { $notification['badge'] = $badge; } $body['aps'] = $notification; // presumes $custom is string only if( strlen($custom) != 0 ) { $body['custom'] = $custom; } // CONNECT $ctx = stream_context_create(); stream_context_set_option($ctx, 'ssl', 'local_cert', $certificate ); stream_context_set_option($ctx, 'ssl', 'passphrase', $certificate_password ); $fp = stream_socket_client( $APPLE_SERVER, $err, $errstr, 15, STREAM_CLIENT_CONNECT, $ctx); if (!$fp) { print "Failed to connect $err $errstr\n"; return; } // SEND foreach( $device_list as $device ) { $payload = json_encode($body); // format the message $msg = chr(0) . chr(0) . chr(32) . pack('H*', $device) . chr(0) . chr(strlen($payload)) . $payload; fwrite($fp, $msg); } // CLOSE CONNECTION fclose($fp); } ?>