$unreads = $device['unread_notifications'];
            // look for an old version of this message
            $old_position = array_search($message['tag'], $unreads);
            if ($old_position !== FALSE) {
                array_splice($unreads, $old_position, 1);
            }
            if (intval($message['has_badge'])) {
                $unreads[] = $message['tag'];
            } else {
                $data->noBadge = 1;
            }
            $data->aps->badge = count($unreads);
            $data->tag = $message['tag'];
            d_echo("sending message: " . json_encode($data));
            $apns_server->send_push_notification($data, $device['device_token']);
            APNS_DB::record_notification_attempt(TRUE, $message['id']);
        } else {
            APNS_DB::record_notification_attempt(FALSE, $message['id']);
        }
        // backend needs to keep track of which messages are not yet read
        APNS_DB::save_unread_notifications($device['device_id'], $unreads);
    }
    APNS_DB::close_notifications($messages);
}
$apns_server->close();
$daemon->stop();
function get_module_name($tag)
{
    $parts = split(":", $tag);
    return $parts[0];
}
 public function queue_notification($device_id) {
   APNS_DB::create_notification($device_id, "stellar:{$this->subject_id}", $this->data);    
 }
	    $message = "$shuttle arriving at $stopname in $minutes minutes ($timestr)";

	    break;
	  case 'schedule':
	    // $stopname was defined earlier
	    $message = "$shuttle (NOT GPS TRACKED) scheduled to arrive at $stopname in $minutes minutes ($timestr)";
	    break;
	  }

	  //$stopname = ShuttleSchedule::get_stop_title($route_id, $stop_id);
	  switch ($row['device_type']) {
	  case 'apple':
	    $aps = array('aps' => 
			 array('alert' => $message,
			       'sound' => 'default'));
	    APNS_DB::create_notification($row['device_id'], "shuttletrack:$route_id:$stop_id", $aps, False);
	    break;
	  }

	  // make sure to unsubscribe this person so they don't 
	  // get a message every 10 seconds until their subscription times out
	  $sql = "DELETE FROM ShuttleSubscription "
	    .     "WHERE device_id='" . $row['device_id'] . "' "
	    .       "AND device_type='" . $row['device_type'] . "' "
	    .       "AND route_id='" . $route_id . "' "
	    .       "AND stop_id='" . $stop_id . "'";
	  if (!db::$connection->query($sql)) {
	    d_error("unsubscribe failed: {$db->errno} {$db->error} in $sql");
	  }

	} // if
    break;

  case 'term':
    $data = array('term' => StellarData::get_term());
    break;

  case 'myStellar':
    require_once 'push/apns_lib.php';
    $pass_key = intval($_REQUEST['pass_key']);
    $device_id = intval($_REQUEST['device_id']);       
    $device_type = $_REQUEST['device_type'];       
    $subject = $_REQUEST['subject'];
    $term = $_REQUEST['term'];

    if($device_type == 'apple') {
      if(!APNS_DB::verify_device_id($device_id, $pass_key)) {
	Throw new Exception("invalid {$pass_key} for {$device_id}");
      }
    } else {
      Throw new Exception("Device type='${device_type}' not yet supported");
    }

    switch($_REQUEST['action']) {
    case 'subscribe':
      StellarData::push_subscribe($subject, $term, $device_id, $device_type);
      $data = array('success' => True);
      break;
    case 'unsubscribe':
      StellarData::push_unsubscribe($subject, $term, $device_id, $device_type);
      $data = array('success' => True);
      break;
  $data = $emergency->get_feed();
  if($data !== False) {
    $new_version = intval($data[0]['version']);
  }

  if($version && ($new_version > $version)) {
    // there is emergency unfortunately we now have to notify ALL devices

    db::ping();
    $emergency_apns = array('aps' => 
      array('alert' => substr($data[0]['text'], 0, 100), 'sound' => 'default')
    );

    $result = APNS_DB::get_all_devices();
    while($row = $result->fetch_assoc()) {
      APNS_DB::create_notification($row['device_id'], "emergencyinfo:", $emergency_apns);
    }
    $result->close();
  }

  if($new_version > 0) {
    $version = $new_version;
    save_version($version);
  }
}

$daemon->stop();


/* these functions are for saving and grabbing the emergency version from disk
 that way the daemon is robust to being restarted */
#!/usr/bin/php
<?php 
define("APNS_FEEDBACK_REST_TIME", 5 * 60 * 60);
require_once "DaemonWrapper.php";
$daemon = new DaemonWrapper("apns_feedback");
$daemon->start($argv);
require_once 'apns_lib.php';
$apns_server = new ApplePushNotificationConnection();
while ($daemon->sleep(APNS_FEEDBACK_REST_TIME)) {
    // this is a daemon so loop forever
    $apns_server->open_feedback_connection();
    $messages = $apns_server->get_feedback_messages();
    db::ping();
    foreach ($messages as $message) {
        d_echo("received a deactivate message from apple for:{$message['device_token']}");
        APNS_DB::record_device_uninstalled_app($message['device_token'], $message['unixtime']);
    }
    $apns_server->close_feedback_connection();
}
$daemon->stop();
 public static function create()
 {
     $pass_key = intval($_REQUEST['pass_key']);
     $device_id = intval($_REQUEST['device_id']);
     $device_type = $_REQUEST['device_type'];
     if ($device_type != 'apple') {
         error_log("wrong device type {$device_type} for APNS");
         return FALSE;
     }
     if (!APNS_DB::verify_device_id($device_id, $pass_key)) {
         error_log("invalid pass key {$pass_key} for device {$device_id}");
         return FALSE;
     }
     return new self($device_id, $device_type);
 }