diff -r 877f952ae2bd -r 6b6c2214f778 web/lib/Zend/Mobile/Push/Apns.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/Zend/Mobile/Push/Apns.php Thu Mar 21 19:52:38 2013 +0100 @@ -0,0 +1,391 @@ +_certificate; + } + + /** + * Set Certificate + * + * @param string $cert + * @return Zend_Mobile_Push_Apns + * @throws Zend_Mobile_Push_Exception + */ + public function setCertificate($cert) + { + if (!is_string($cert)) { + throw new Zend_Mobile_Push_Exception('$cert must be a string'); + } + if (!file_exists($cert)) { + throw new Zend_Mobile_Push_Exception('$cert must be a valid path to the certificate'); + } + $this->_certificate = $cert; + return $this; + } + + /** + * Get Certificate Passphrase + * + * @return string + */ + public function getCertificatePassphrase() + { + return $this->_certificatePassphrase; + } + + /** + * Set Certificate Passphrase + * + * @param string $passphrase + * @return Zend_Mobile_Push_Apns + * @throws Zend_Mobile_Push_Exception + */ + public function setCertificatePassphrase($passphrase) + { + if (!is_string($passphrase)) { + throw new Zend_Mobile_Push_Exception('$passphrase must be a string'); + } + $this->_certificatePassphrase = $passphrase; + return $this; + } + + /** + * Connect to Socket + * + * @param string $uri + * @return bool + * @throws Zend_Mobile_Push_Exception_ServerUnavailable + */ + protected function _connect($uri) + { + $ssl = array( + 'local_cert' => $this->_certificate, + ); + if ($this->_certificatePassphrase) { + $ssl['passphrase'] = $this->_certificatePassphrase; + } + + $this->_socket = stream_socket_client($uri, + $errno, + $errstr, + ini_get('default_socket_timeout'), + STREAM_CLIENT_CONNECT, + stream_context_create(array( + 'ssl' => $ssl, + )) + ); + + if (!is_resource($this->_socket)) { + require_once 'Zend/Mobile/Push/Exception/ServerUnavailable.php'; + throw new Zend_Mobile_Push_Exception_ServerUnavailable(sprintf('Unable to connect: %s: %d (%s)', + $uri, + $errno, + $errstr + )); + } + + stream_set_blocking($this->_socket, 0); + stream_set_write_buffer($this->_socket, 0); + return true; + } + + /** + * Read from the Socket Server + * + * @param int $length + * @return string + */ + protected function _read($length) { + $data = false; + if (!feof($this->_socket)) { + $data = fread($this->_socket, $length); + } + return $data; + } + + /** + * Write to the Socket Server + * + * @param string $payload + * @return int + */ + protected function _write($payload) { + return @fwrite($this->_socket, $payload); + } + + /** + * Connect to the Push Server + * + * @param string $env + * @return Zend_Mobile_Push_Abstract + * @throws Zend_Mobile_Push_Exception + * @throws Zend_Mobile_Push_Exception_ServerUnavailable + */ + public function connect($env = self::SERVER_PRODUCTION_URI) + { + if ($this->_isConnected) { + if ($this->_currentEnv == self::SERVER_PRODUCTION_URI) { + return $this; + } + $this->close(); + } + + if (!isset($this->_serverUriList[$env])) { + throw new Zend_Mobile_Push_Exception('$env is not a valid environment'); + } + + if (!$this->_certificate) { + throw new Zend_Mobile_Push_Exception('A certificate must be set prior to calling ::connect'); + } + + $this->_connect($this->_serverUriList[$env]); + + $this->_currentEnv = $env; + $this->_isConnected = true; + return $this; + } + + + + /** + * Feedback + * + * @return array array w/ key = token and value = time + * @throws Zend_Mobile_Push_Exception + * @throws Zend_Mobile_Push_Exception_ServerUnavailable + */ + public function feedback() + { + if (!$this->_isConnected || + !in_array($this->_currentEnv, + array(self::SERVER_FEEDBACK_SANDBOX_URI, self::SERVER_FEEDBACK_PRODUCTION_URI))) { + $this->connect(self::SERVER_FEEDBACK_PRODUCTION_URI); + } + + $tokens = array(); + while ($token = $this->_read(38)) { + if (strlen($token) < 38) { + continue; + } + $token = unpack('Ntime/ntokenLength/H*token', $token); + if (!isset($tokens[$token['token']]) || $tokens[$token['token']] < $token['time']) { + $tokens[$token['token']] = $token['time']; + } + } + return $tokens; + } + + /** + * Send Message + * + * @param Zend_Mobile_Push_Message_Apns $message + * @return boolean + * @throws Zend_Mobile_Push_Exception + * @throws Zend_Mobile_Push_Exception_ServerUnavailable + * @throws Zend_Mobile_Push_Exception_InvalidToken + * @throws Zend_Mobile_Push_Exception_InvalidTopic + * @throws Zend_Mobile_Push_Exception_InvalidPayload + */ + public function send(Zend_Mobile_Push_Message_Abstract $message) + { + if (!$message->validate()) { + throw new Zend_Mobile_Push_Exception('The message is not valid.'); + } + + if (!$this->_isConnected || !in_array($this->_currentEnv, array( + self::SERVER_SANDBOX_URI, + self::SERVER_PRODUCTION_URI))) { + $this->connect(self::SERVER_PRODUCTION_URI); + } + + $payload = array('aps' => array()); + + $alert = $message->getAlert(); + foreach ($alert as $k => $v) { + if ($v == null) { + unset($alert[$k]); + } + } + if (!empty($alert)) { + $payload['aps']['alert'] = $alert; + } + if (!is_null($message->getBadge())) { + $payload['aps']['badge'] = $message->getBadge(); + } + $payload['aps']['sound'] = $message->getSound(); + + foreach($message->getCustomData() as $k => $v) { + $payload[$k] = $v; + } + $payload = json_encode($payload); + + $expire = $message->getExpire(); + if ($expire > 0) { + $expire += time(); + } + $id = $message->getId(); + if (empty($id)) { + $id = time(); + } + + $payload = pack('CNNnH*', 1, $id, $expire, 32, $message->getToken()) + . pack('n', strlen($payload)) + . $payload; + $ret = $this->_write($payload); + if ($ret === false) { + require_once 'Zend/Mobile/Push/Exception/ServerUnavailable.php'; + throw new Zend_Mobile_Push_Exception_ServerUnavailable('Unable to send message'); + } + // check for errors from apple + $err = $this->_read(1024); + if (strlen($err) > 0) { + $err = unpack('Ccmd/Cerrno/Nid', $err); + switch ($err['errno']) { + case 0: + return true; + break; + case 1: + throw new Zend_Mobile_Push_Exception('A processing error has occurred on the apple push notification server.'); + break; + case 2: + require_once 'Zend/Mobile/Push/Exception/InvalidToken.php'; + throw new Zend_Mobile_Push_Exception_InvalidToken('Missing token; you must set a token for the message.'); + break; + case 3: + require_once 'Zend/Mobile/Push/Exception/InvalidTopic.php'; + throw new Zend_Mobile_Push_Exception_InvalidTopic('Missing id; you must set an id for the message.'); + break; + case 4: + require_once 'Zend/Mobile/Push/Exception/InvalidPayload.php'; + throw new Zend_Mobile_Push_Exception_InvalidPayload('Missing message; the message must always have some content.'); + break; + case 5: + require_once 'Zend/Mobile/Push/Exception/InvalidToken.php'; + throw new Zend_Mobile_Push_Exception_InvalidToken('Bad token. This token is too big and is not a regular apns token.'); + break; + case 6: + require_once 'Zend/Mobile/Push/Exception/InvalidTopic.php'; + throw new Zend_Mobile_Push_Exception_InvalidTopic('The message id is too big; reduce the size of the id.'); + break; + case 7: + require_once 'Zend/Mobile/Push/Exception/InvalidPayload.php'; + throw new Zend_Mobile_Push_Exception_InvalidPayload('The message is too big; reduce the size of the message.'); + break; + case 8: + require_once 'Zend/Mobile/Push/Exception/InvalidToken.php'; + throw new Zend_Mobile_Push_Exception_InvalidToken('Bad token. Remove this token from being sent to again.'); + break; + default: + throw new Zend_Mobile_Push_Exception(sprintf('An unknown error occurred: %d', $err['errno'])); + break; + } + } + return true; + } + + /** + * Close Connection + * + * @return void + */ + public function close() + { + if ($this->_isConnected && is_resource($this->_socket)) { + fclose($this->_socket); + } + $this->_isConnected = false; + } +}