Apple Push notification with PHP via proxy - php

I need to connect to Apple PNS via proxy. I am using PHP to develop the application. Currently, I have googled a lot but nowhere I am able to find an example of using Proxy with stream context.
Also did the steps mentioned here:
Using Charles and SSL Apple push address
Below is the code snippet. Anyone please suggest, its highly urgent
if(PROXY_ADDRESS != '' && PROXY_PORT !='')
{
$opts = array('http' => array('proxy' => 'tcp://'.PROXY_ADDRESS.':'.PROXY_PORT, 'request_fulluri' => true));
$streamContext = stream_context_create($opts);
}
else
{
$streamContext = stream_context_create();
}
stream_context_set_option($streamContext, 'ssl', 'local_cert', $this->_privateKey);
if($this->_privateKeyPassphrase !== '')
stream_context_set_option($streamContext, 'ssl', 'passphrase', $this->_privateKeyPassphrase);
$fp = fopen('https://gateway.sandbox.push.apple.com:2195', 'r', false, $streamContext);
I am not getting any error/exception, but the push message is not received.

To those who may encounter the same problem:
Apple PNS doesn't allow connections coming via Proxy Server. It only accepts direct connections. My problem got resolved when I made a direct connection instead of going via a proxy.

Related

Php stream socket: The connection to wss://host:port was interrupted while the page was loading

I am having issue with web socket connection/handshake from Firefox to my server written in Php.
Problem is when I try to connect client(using js/php or even websocket.org/echo.html) I get issue and the issue is client is firstly connected and then disconnected automatically. And at server, I am using fwrite to to send message to client. And i have also noticed that for a client from firefox fwrite returns 0 as a response, but at client side I can see server has posted handshake data.
Now I see problem is here, when server is writing but response is 0 and still browser(firefox) is getting response but it gets disconnected with the server at the same time. I even tried to compare(calculate) data in bytes, which server is posting to client. I see its exactly same.
Note:-
Just to mention here, same php server script is working fine for Opera/Chrome/Safari browsers.
Below are the things which are involved in this issue;
Firefox [71.0]
Php [7.0]
Apache
Let's Encrypt SSL
# STREAM SOCKET CREATION OVER SSL(WSS)
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'local_cert', 'ssl-test.pem'); #change to your own ssl.pem path
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
$sock = stream_socket_server("ssl://".HOST_NAME.":".PORT,$errno,$errstr,STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context) or die('Unable to start server, seems server is already running!');
// stream_set_blocking($sock,TRUE);
# PUTTING ALL(CURRENT/UPCOMING) SOCKET CONNECTIONS IN ARRAY
$clients = array();
# INFINTE LOOP THOURGH ALL SOCKECT CURRENT/UPCOMING CONNECTIONS, SEND/RECEIVE DATA
while (true) {
# ALL ACTIVE SOCKET CONNECTIONS
$read = $clients;
foreach($received_header as $key => $header){
if($header['time']+1 < microtime(true)){
$headers = [];
// echo $header['data'];
$lines = preg_split("/\r\n/", $header['data']);
foreach($lines as $line) {
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches)){
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
// create handshake header
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake" .PHP_EOL.
"Upgrade: websocket" .PHP_EOL.
"Connection: Upgrade" .PHP_EOL.
"Accept: text/html" .PHP_EOL.
"Sec-WebSocket-Accept: $secAccept".PHP_EOL.PHP_EOL;
// send handshake packet
if(!fwrite($clients[$key], $upgrade)){
file_put_contents('handshakes-est.txt', ($upgrade));
}
$handshakes[$key] = true;
$unset_key = $key;
}
}
/* i have rest of the script here which do further communication after connection is setup */
}
Below are some screen-shots to better understand connection behavior issue on Firefox.
Headers during handshake:
Connection behavior over websocket:

how to run php script for sending push notification

I need help implementing push notifications in swift. I followed the ray wenderlich tutorial http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1 but it does not mention how you actually call or run the php push notification script in Xcode. This is how I am attempting to call the script right now:
let request = NSMutableURLRequest(URL: NSURL(string: "http://website.com/pushNotification")!)
request.HTTPMethod = "POST"
let dataDictionary:[String:String] = ["NotificationData":"\(deviceTokenString)<*&*>password<*&*>my first push notification"]
let data:NSData = try! NSJSONSerialization.dataWithJSONObject(dataDictionary, options: [])
request.HTTPBody = data
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
// Create a NSURLSession task with completion handler
let task:NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
// Convert the data into a dictionary
let response:[String:String] = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! [String:String]
// Check if there was an error
if let result = response["result"] {
if result == "success"{
NSLog("Message deleviered successfully")
}
else if result == "error"{
NSLog("Message could not be deleviered")
}
}
})
// Run the task
task.resume()
Heres the php script it hits:
<?php
// Get the data from the request
$json = file_get_contents('php://input');
$data = json_decode($json, true);
$pushData = $data['NotificationData'];
// Format data
$keywords = explode("<*&*>", $pushData);
// Assign data into variables
$deviceToken = $keywords[0];
$passphrase = $keywords[1];
$message = $keywords[2];
////////////////////////////////////////////////////////////////////////////////
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'signingcertificate.p12');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp) {
//exit("Failed to connect: $err $errstr" . PHP_EOL);
}
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result) {
echo '{"result" : "error"}';
}
else {
echo '{"result" : "success"}';
}
// Close the connection to the server
fclose($fp);
?>
But then Xcode gives me this error:
fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}: file /Library/Caches/com.apple.xbs/Sources/swiftlang_PONDEROSA/swiftlang_PONDEROSA-700.1.101.6/src/swift/stdlib/public/core/ErrorType.swift, line 50
Im almost positive the error has something to do with the way I call for the php script but I don't know how this type of php script should be called. Please help! Any suggestions or insight you have will be appreciated!
The link does tell you what to do with the PHP script. You aren't suppose to call that through Xcode.
As the tutorial states:
As I’ve mentioned a few times before, you need to set up a server that sends the push notifications to your app. For this first test, you’re not going to set up a server just yet. Instead, I’ll give you a very simple PHP script that sets up a connection to APNS and sends a push notification to a device token that you specify. You can run this straight from your Mac.
...
You should copy the device token from the app into the $deviceToken variable. Be sure to leave out the spaces and brackets; it should just be 64 hexadecimal characters. Put your private key’s passphrase into $passphrase, and the text you wish to send in $message.
Copy your ck.pem file into the SimplePush folder. Remember, the ck.pem file contains both your certificate and the private key.
Then open a Terminal and type: .....
The PHP is a simple example of what your server would do. You need to build the server as well that will invoke a call to apple's APNS when a certain event occurs. The mobile app itself doesn't invoke a push notification.

Run PHP Script in Background(Without Using Cron)

I want to run following code in Background at certain occurrence:
File A :
// ------------------- Some Code Before Following -----------------------
// send notification to given device token
$notification = new Notification();
$notification->sendNotification($token,$count);
// ------------------- Some Code After Above ------------------------
This will called following class :
// Here I am sending notification to given device token (APNS Push Notification)
<?php
class Notification
{
public function sendNotification($token,$count)
{
if(strlen($token)%16 == 0)
{
// set Device token
$deviceToken = $token;
$passphrase = 'xxxxx';
$badge = $count;
// Displays alert message here:
$message = 'Hello! There.';
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $err,$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
echo("Connected to APNS.");
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'badge' => $badge,
'sound' => 'default'
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo("\n\nMessage not delivered.\n\n");
else
echo("\n\nMessage successfully delivered.\n\n");
// Close the connection to the server
fclose($fp);
}
}
}
?>
When Execution of this class will be completed then it will go back to file A and it will continue execution after sending notification.
But here it will take time to send notification (around 3-5-7 seconds) and for that time user has to wait which is not a good idea.
So what I want to do is that send notification in Background process instead of main thread.So user don't has to wait unnecessarily.
Please note that I don't want to use Cron.
I found that exec/shell_exec/passthru command can help me. But which command I used out of these three commands in this case?
Please Guide me on this. Any help will be appreciated.
passthru("/usr/bin/php /path/to/yourFile.php >> /path/to/log_file.log 2>&1 &");
There are a few thing that are important here.
First of all: put the full path to the php binary, because this command will run under the apache user, and you will probably not have command alias like php set in that user.
Seccond: Note 2 things at the end of the command string: the 2>&1 and the &. The 2>&1 is for redirecting errors to the standard IO. And the most important thing is the & at the end of the command string, which tells the terminal not to wait for a response.
There are a few options here:
You can fork a separate PHP process with pcntl_fork (http://php.net/manual/en/function.pcntl-fork.php)
You can use a cronjob to check for unsent modifications which you do not want to (you failed to mention WHY, this is why I mention it)
You can use a job queue system for it. By far the best solution but takes some time to get started. Gearman is the one I have experience with and that works like a charm.
Follow the instructions here to run a PHP script in the background:
php execute a background process
It requires the use of shell_exec to call the script from File A, so you will have to create a new file for that code to be called. It also assumes you are using linux

PHP Apple APNS Read IMMEDIATE Notification Status_code

for the past 3 days i have been trying to make things work with my php code and apns.
So far i'm able to deliver notifications (i only have one device so i don't know if this is working for multiple devices).
When i try to test sending one notification for 2 devices where the first is an device token invalid (invented by me) and the second is my device token, im unable to deliver the notification to my device... From what i read, when a notification is unreadable or erroneous, apns shuts the connection down and sends an error of 6 bytes long, where the 2nd byte (status_code) is the error type. If no error happens, im still unable to understand if apns send the status_code 0 (No errors encountered) or if it does not send anything at all.
So far i wasn't able to ever find this status_code, despite of following several codes found on internet.
CODE
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '.\certif\ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Create the payload body
//file_put_contents('logs/debug_not.log', 'body'. "\n", FILE_APPEND);
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
// Encode the payload as JSON
//file_put_contents('logs/debug_not.log', 'json body'. "\n", FILE_APPEND);
$payload = json_encode($body);
$open = true;
foreach ($devices as $device) {
//file_put_contents('logs/debug_not.log', 'inicio ciclo'. "\n", FILE_APPEND);
if($open == true){
//file_put_contents('logs/debug_not.log', 'abrir ligacao'. "\n", FILE_APPEND);
// Open a connection to the APNS server
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp){
//file_put_contents('logs/debug_not.log', 'erro abrir ligacao'. "\n", FILE_APPEND);
throw new \Exception;
}
}
// Build the binary notification
//file_put_contents('logs/debug_not.log', 'criar payload'. "\n", FILE_APPEND);
$msg = chr(0) . pack('n', 32) . pack('H*', str_replace(' ', '', $device['token'])) . pack('n', strlen($payload)) . $payload;
// Send it to the server
//file_put_contents('logs/debug_not.log', 'enviar payload'. "\n", FILE_APPEND);
$result = fwrite($fp, $msg, strlen($msg));
stream_set_blocking ($fp, 0);
$errorResponse = #fread($apns, 6);
//if i var_dump($errorResponse) it gives me null/false all the time, no matter what
//this is my workaround for invalid device tokens-- its not working or at least is not send the valid tokens
if(!$result || !$fp)
{
//file_put_contents('logs/debug_not.log', 'erro na not '. chr($data["status_code"]). "\n", FILE_APPEND);
fclose($fp);
$open = true;
} else{
//file_put_contents('logs/debug_not.log', 'suc na not'. "\n", FILE_APPEND);
$open = false;
}
//file_put_contents('logs/debug_not.log', 'fim ciclo'. "\n", FILE_APPEND);
}
// Close the connection to the server
if($fp){
//file_put_contents('logs/debug_not.log', 'fechar connection'. "\n", FILE_APPEND);
fclose($fp);
}
fread on my connection gives null/false all the time, even when i receive my notification or i'm trying with a wrong token.
My logic for connect after error, seems to be not working.
can please someone help me??? i dont intend to use 3rd classes or code, would like to have a simple basic one working for an array of device tokens, even if there are some that apns gives error.
Thanks everyone.
You have made your connection as $fp = stream_socket_client ......
and you are trying to $errorResponse = #fread($apns, 6); which will never give you any output as $apns is not defined.
Rather you should do this :
$errorResponse = #fread($fp, 6);
and your code will work fine
If you receive an error (most likely in your case because your invented device token does not exist on Apple's servers for your app), then an error message is returned just before the connection is closed. If the message is successful, there is no response, but the connection remains open.
Here's how you could handle this:
Send your message.
Read the error with a timeout (or in non-blocking mode). If you get an error response, the message failed. (You'll need a new connection.)
If the read times out (or returns nothing in non-blocking mode), the message may be successful. (Do nothing.)
Send the next message. If the send is successful (still connected), the last message was definitely successful.
Keep track of your last successful message. You could always follow up a series of messages by sending your own device a message just to prevent any false positives.
You might want to check out the source code for EasyAPNS, which is an open source PHP implementation of this. Also, you can read the details in Apple's documentation: Provider Communication with Apple Push Notification Service

Retrieve SSL Certificate with Zend Http Request and Response

I'm trying to connect to a third party API and I'm using the Zend 1.7.0 framework to access it. To authenticate, I have to first check an SSL certificate returned from a URL. This is what I'm doing in my code.
$client = new Zend_Http_Client($url);
$response = $client->request('GET');
The response is always OK, and when I navigate to '$url' I can see the certificate if I click on the lock in the bottom right corner of my browser window.
Is there a way to access the certificate's owner, expiration date and other properties via the response object? Do I have to do anything special to the request before sending it to obtain the information about the certificate?
I don't think it's possible with the standard connection adaptor that Zend_Http_Client uses. I did a bit of digging and it looks like the adaptor uses fsockopen which pretty much hide what you're looking for. The way to do it is to open the socket yourself and get the certificate first:
$url = 'mail.google.com'; // For example
$context = stream_context_create();
$res = stream_context_set_option($context, 'ssl', 'capture_peer_cert', true);
$res = stream_context_set_option($context, 'ssl', 'verify_host', true);
if ($socket = stream_socket_client("tls://$url:443/", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context)) {
if ($options = stream_context_get_options($context)) {
if (isset($options['ssl']) && isset($options['ssl']['peer_certificate'])) {
$keyinfo = openssl_x509_parse($options[$wrapper]['peer_certificate']);
var_dump($keyinfo);
}
}
fclose($fp);
}

Categories