-
-
Save anonymous/0f3c17f6fcc079090256 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ------------------------------------------------------------------------------------------- | |
// PNS Class for Push Notification | |
// | |
// Note: this class also acts as a Node.js module to be imported and used in server.js file | |
// ------------------------------------------------------------------------------------------- | |
var eventEmitter = require('events').EventEmitter; | |
var util = require('util'); | |
var instance; | |
/** | |
* Note: dev_cert_path param is a path to the single file pem file that | |
* embeds the ios PNS certificate and private key | |
*/ | |
var PNS = function(dev_cert_path, prod_cert_path) { | |
// dev_cert param is a | |
this.dev_cert_path = dev_cert_path; | |
this.prod_cert_path = prod_cert_path; | |
this.dev_host = 'gateway.sandbox.push.apple.com'; | |
this.prod_host = 'gateway.push.apple.com'; | |
// port to Apple push notification server | |
this.port = 2195; | |
// push notification socket stream variable | |
this.stream = null; | |
this.crypto = require('crypto'); | |
this.tls = require('tls'); | |
this.fs = require('fs'); | |
this.path = require('path'); | |
this.querystring = require('querystring'); | |
this.http = require('http'); | |
instance = this; | |
} | |
// allowing this class to emit events | |
util.inherits(PNS, eventEmitter); | |
// ------------------------------------------------------------------------------------------- | |
// Class methods | |
// ------------------------------------------------------------------------------------------- | |
/** | |
* Helper constructor method | |
*/ | |
var init = function(dev_cert_path, prod_cert_path) { | |
return new PNS(dev_cert_path, prod_cert_path); | |
}; | |
/** | |
* Method to connect to the push notification server | |
*/ | |
var connect = function() { | |
// read certificate and key file | |
// synchronously | |
var privateKey = this.fs.readFileSync(this.dev_cert_path, encoding='ascii'); | |
var certificate = this.fs.readFileSync(this.dev_cert_path, encoding='ascii'); | |
var options = { key: privateKey, cert: certificate }; | |
this.stream = this.tls.connect(this.port, this.dev_host, options, function() { | |
// connected | |
console.log('Apple Push Notification socket stream connected'); | |
instance.emit('pns_connected'); | |
}); | |
}; | |
/** | |
* Makes a POST request to Symfony web server to get the necessary Notification objects | |
* then start parsing the notification objects, sending any notification out | |
*/ | |
var getNotifications = function() { | |
console.log('getting push notifications now'); | |
var options = { | |
host: 'www.symfony.local', | |
port: 80, | |
path: '/app_dev.php/api/pnsqueue', | |
method: 'POST' | |
}; | |
var data = ''; | |
// make a HTTP Post request to the Symfony web server | |
var request = this.http.request(options, function(response) { | |
response.on('data', function(chunk) { | |
data += chunk; | |
//console.log('data = ' + data + '\n'); | |
}); | |
response.on('end', function() { | |
parseNotifications(data); | |
}); | |
}); | |
// end of request | |
request.end(); | |
}; | |
/** | |
* Builds the final array needed to send push notifications | |
* and calls the push notification method for each notifications | |
*/ | |
function parseNotifications(rawData) { | |
var obj = JSON.parse(rawData); | |
//var strJSON = JSON.stringify(rawData); | |
//console.log(obj); | |
// array of notification objects | |
var dataObj = obj.data; | |
//console.log('dataObj'); | |
//console.log(dataObj); | |
var arrNotifications = dataObj.notification; | |
//console.log('arrNotifications'); | |
//console.log(arrNotifications); | |
// --------------------------------------------------- | |
// go through each notification and | |
// parse get the message and array of tokens | |
// --------------------------------------------------- | |
for(var i = 0; i < arrNotifications.length; i++) | |
{ | |
//console.log('parsing notification'); | |
var notificationObj = arrNotifications[i]; | |
//console.log(notificationObj); | |
//console.log('message = ' + notificationObj.message); | |
var arrTokens = new Array(); | |
var arrNotifObjTokens = notificationObj.tokens; | |
if(arrNotifObjTokens != null) | |
{ | |
// add all raw tokens | |
for(var j = 0; j < arrNotifObjTokens.length; j++) | |
{ | |
var tokenObj = notificationObj.tokens[j]; | |
if(tokenObj.active) | |
{ | |
// add the token string from the token object | |
// to our final array | |
arrTokens.push(tokenObj.token); | |
//console.log('raw token added'); | |
} | |
} | |
} | |
var arrNotifObjTokenGroups = notificationObj.tokengroups; | |
if(arrNotifObjTokenGroups != null) | |
{ | |
// add all tokens from all token groups | |
//for(var tokengroup in notificationObj.tokengroups) | |
for(var k = 0; k < arrNotifObjTokenGroups.length; k++) | |
{ | |
var tokengroupObj = arrNotifObjTokenGroups[k]; | |
var arrTGTokens = tokengroupObj.tokens; | |
if(arrTGTokens != null) | |
{ | |
//for(var token in tokengroupObj.tokens) | |
for(var l = 0; l < arrTGTokens.length; l++) | |
{ | |
var tokenObj = arrTGTokens[l]; | |
arrTokens.push(tokenObj.token); | |
//console.log('token group token added'); | |
} | |
} | |
} | |
} | |
// clean up final array, removing all duplicates | |
arrTokens = arrTokens.filter(function(elem, pos) { | |
return arrTokens.indexOf(elem) == pos; | |
}); | |
//console.log('arrTokens = ' + arrTokens + '\n\n'); | |
// send the final push notification | |
pushNotification(notificationObj.message, arrTokens); | |
} | |
// start downloading notification again | |
//instance.emit('pns_queue_end'); | |
}; | |
/** | |
* sends a push notification for all tokens attached to the notification | |
*/ | |
function pushNotification(message, arrTokens) | |
{ | |
console.log('sending push notification...'); | |
// for each token, send the notification message to that token | |
var pushObj = { aps: { alert:message, sound:'default' } }; | |
var payload = JSON.stringify(pushObj); | |
var tokenlen = 32; | |
var payloadlen = Buffer.byteLength(payload, 'utf-8'); | |
var buffer = new Buffer(1 + 4 + 4 + 2 + tokenlen + 2 + payloadlen); | |
var i = 0; | |
var msgid = 0xbeefcace; // message identifier, can be left at 0 | |
//console.log('arrTokens = ' + arrTokens); | |
console.log('arrTokens.length = ' + arrTokens.length); | |
for(var i = 0; i < arrTokens.length; i++) | |
{ | |
var strToken = arrTokens[i]; | |
console.log('strToken = ' + strToken + '\n'); | |
var hextoken = strToken; | |
buffer[i++] = 1; // command | |
buffer[i++] = msgid >> 24 & 0xFF; | |
buffer[i++] = msgid >> 16 & 0xFF; | |
buffer[i++] = msgid >> 8 & 0xFF; | |
buffer[i++] = msgid & 0xFF; | |
// expiry in epoch seconds (1 hour) | |
var seconds = Math.round(new Date().getTime() / 1000) + 1*60*60; // expire in epoch seconds (1 hour) | |
buffer[i++] = seconds >> 24 & 0xFF; | |
buffer[i++] = seconds >> 16 & 0xFF; | |
buffer[i++] = seconds >> 8 & 0xFF; | |
buffer[i++] = seconds & 0xFF; | |
buffer[i++] = tokenlen >> 8 & 0xFF; // token length | |
buffer[i++] = tokenlen & 0xFF; | |
var token = hextobin(hextoken); | |
token.copy(buffer, i, 0, tokenlen); | |
i += tokenlen; | |
buffer[i++] = payloadlen >> 8 & 0xFF; // payload length | |
buffer[i++] = payloadlen & 0xFF; | |
payload = Buffer(payload); | |
payload.copy(buffer, i, 0, payloadlen); | |
var writable = instance.stream.write(buffer); | |
if(!writable) | |
{ | |
console.log('unable to write to stream'); | |
} | |
// reset buffer | |
//console.log('a push notification was sent for token ' + strToken); | |
} | |
} | |
/** | |
* Helper function to convert hexidecimal to binary | |
* Apple push notification expects a binary stream | |
*/ | |
function hextobin(hexstr) | |
{ | |
buf = new Buffer(hexstr.length / 2); | |
for(var i = 0; i < hexstr.length/2; i++) | |
{ | |
buf[i] = (parseInt(hexstr[i * 2], 16) << 4) + (parseInt(hexstr[i * 2 + 1], 16)); | |
} | |
return buf; | |
} | |
// ------------------------------------------------------------------------------------------- | |
// Attaching methods to this class | |
// ------------------------------------------------------------------------------------------- | |
PNS.prototype.init = init; | |
PNS.prototype.connect = connect; | |
PNS.prototype.getNotifications = getNotifications; | |
// ------------------------------------------------------------------------------------------- | |
// Exporting the class | |
// ------------------------------------------------------------------------------------------- | |
module.exports.PNS = PNS; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment