Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

apn

Package Overview
Dependencies
Maintainers
1
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apn - npm Package Compare versions

Comparing version 1.1.4 to 1.1.5

441

lib/apn.js

@@ -6,13 +6,13 @@ var tls = require('tls');

var Errors = {
'noErrorsEncountered': 0
, 'processingError': 1
, 'missingDeviceToken': 2
, 'missingTopic': 3
, 'missingPayload': 4
, 'invalidTokenSize': 5
, 'invalidTopicSize': 6
, 'invalidPayloadSize': 7
, 'invalidToken': 8
, 'none': 255
}
'noErrorsEncountered': 0,
'processingError': 1,
'missingDeviceToken': 2,
'missingTopic': 3,
'missingPayload': 4,
'invalidTokenSize': 5,
'invalidTopicSize': 6,
'invalidPayloadSize': 7,
'invalidToken': 8,
'none': 255
};

@@ -24,16 +24,20 @@ var Connection = function (optionArgs) {

var self = this;
var hasKey = hasCert = false;
var hasKey = false;
var hasCert = false;
var socketOptions = {};
var openingSocket = false;
var writeBuffer = [];
var options = {
cert: 'cert.pem' /* Certificate file */,
certData: '',
key: 'key.pem' /* Key file */,
keyData: '',
gateway: 'gateway.push.apple.com' /* gateway address */,
port: 2195 /* gateway port */,
enhanced: true /* enable enhanced format */,
errorCallback: undefined /* Callback when error occurs */,
cacheLength: 5 /* Number of notifications to cache for error purposes */
};
var options = { cert: 'cert.pem' /* Certificate file */
, key: 'key.pem' /* Key file */
, gateway: 'gateway.push.apple.com' /* gateway address */
, port: 2195 /* gateway port */
, enhanced: true /* enable enhanced format */
, errorCallback: undefined /* Callback when error occurs */
, cacheLength: 5 /* Number of notifications to cache for error purposes */
};
if (optionArgs) {

@@ -46,9 +50,9 @@ var keys = Object.keys(options);

}
var readyToConnect = function () {
return (hasKey && hasCert);
}
};
var onDrain = function() {
if (writeBuffer.length) {
var onDrain = function () {
while (writeBuffer.length && self.socket.bufferSize == 0) {
writeNotificationToSocket(writeBuffer.shift());

@@ -58,26 +62,26 @@ }

var startSocket = function () {
if(!self.openingSocket) {
process.nextTick(function() {
self.socket = tls.connect(options['port'], options['gateway'], socketOptions,
callback = function() {
if(!self.socket.authorized) {
throw self.socket.authorizationError
var startSocket = function () {
if (!self.openingSocket) {
process.nextTick(function () {
self.socket = tls.connect(options['port'], options['gateway'], socketOptions,
function () {
if (!self.socket.authorized) {
throw self.socket.authorizationError;
}
onDrain();
self.openingSocket=false;
self.openingSocket = false;
});
self.socket.on('data', function(data) {
self.socket.on('data', function (data) {
handleTransmissionError(data);
});
self.socket.on('error', function(data) {
self.socket.on('error', function () {
self.socket.removeAllListeners();
self.socket = undefined;
});
self.socket.once('close', function () {
if(writeBuffer.length && readyToConnect()) {
if (writeBuffer.length && readyToConnect()) {
startSocket();

@@ -88,7 +92,7 @@ }

}
self.socket.removeAllListeners();
self.socket = undefined;
});
self.socket.on("drain", onDrain);

@@ -98,24 +102,36 @@ });

}
};
var connect = invoke_after(function () {
startSocket();
});
if (options['certData']) {
socketOptions['cert'] = options['certData'];
hasCert = true;
} else {
fs.readFile(options['cert'], connect(function (err, data) {
if (err) {
throw err;
}
socketOptions['cert'] = data.toString();
hasCert = true;
}));
}
var connect = invoke_after(function() { startSocket(); });
fs.readFile(options['cert'], connect(function(err, data) {
if(err) {
throw err;
}
socketOptions['cert'] = data.toString();
hasCert = true;
}));
fs.readFile(options['key'], connect(function(err, data) {
if(err) {
throw err;
}
socketOptions['key'] = data.toString();
if (options['keyData']) {
socketOptions['key'] = options['keyData'];
hasKey = true;
}));
} else {
fs.readFile(options['key'], connect(function (err, data) {
if (err) {
throw err;
}
socketOptions['key'] = data.toString();
hasKey = true;
}));
}
var writeNotificationToSocket = function(data) {
if (self.socket === undefined || self.socket.readyState != 'open') {
var writeNotificationToSocket = function (data) {
if (self.socket === undefined || self.socket.readyState != 'open') {
if ((self.socket === undefined || self.socket.readyState == 'closed') && readyToConnect()) {

@@ -134,9 +150,9 @@ startSocket();

var bufferDataForWrite = function(data) {
var bufferDataForWrite = function (data) {
writeBuffer.push(data);
}
};
this.sendNotification = function (note) {
var encoding = 'utf8';
if(note.encoding) {
if (note.encoding) {
encoding = note.encoding;

@@ -148,23 +164,24 @@ }

var pos = 0;
if(token === undefined) {
if (token === undefined) {
return Errors['missingDeviceToken'];
}
if(messageLength > 256) {
if (messageLength > 256) {
return Errors['invalidPayloadSize'];
}
note._uid = currentId++;
if(options.enhanced) {
var data = new Buffer(1 + 4 + 4 + 2 + token.length + 2 + messageLength);
var data;
if (options.enhanced) {
data = new Buffer(1 + 4 + 4 + 2 + token.length + 2 + messageLength);
// Command
data[pos] = 1;
pos++;
// Identifier
pos += int2buf(note._uid, data, pos, 4);
// Expiry
pos += int2buf(note.expiry, data, pos, 4);
cachedNotes.push(note);

@@ -174,7 +191,7 @@ tidyCachedNotes();

else {
var data = new Buffer(1 + 2 + token.length + 2 + messageLength);
data = new Buffer(1 + 2 + token.length + 2 + messageLength);
data[pos] = 0;
pos++;
}
pos += int2buf(token.length, data, pos, 2);

@@ -186,12 +203,12 @@ pos += token.copy(data, pos, 0);

writeNotificationToSocket(data);
}
var tidyCachedNotes = function() {
};
var tidyCachedNotes = function () {
// Maybe a timestamp should be stored for each note and kept for a duration?
if(cachedNotes.length > options.cacheLength) {
if (cachedNotes.length > options.cacheLength) {
cachedNotes.shift();
}
}
var handleTransmissionError = function(data) {
};
var handleTransmissionError = function (data) {
// Need to check message that errors

@@ -212,8 +229,8 @@ // return failed notification to owner

var errorCode = data[1];
var identifier = bytes2int(data.slice(2,6), 4);
var identifier = bytes2int(data.slice(2, 6), 4);
var note = undefined;
while(cachedNotes.length) {
while (cachedNotes.length) {
note = cachedNotes.shift();
if(note['_uid'] == identifier) {
if (note['_uid'] == identifier) {
break;

@@ -224,3 +241,3 @@ }

// Notify callback of failed notification
if(typeof options.errorCallback == 'function') {
if (typeof options.errorCallback == 'function') {
options.errorCallback(errorCode, note);

@@ -230,3 +247,3 @@ }

var count = cachedNotes.length;
for(var i=0; i<count; i++) {
for (var i = 0; i < count; i++) {
note = cachedNotes.shift();

@@ -237,3 +254,3 @@ self.sendNotification(note);

}
}
};

@@ -245,52 +262,55 @@ var Notification = function () {

this.device;
this.alert = undefined;
this.badge = undefined;
this.sound = undefined;
}
};
Notification.prototype.toJSON = function() {
if(this.payload.aps === undefined) {
Notification.prototype.toJSON = function () {
if (this.payload === undefined) {
this.payload = {};
}
if (this.payload.aps === undefined) {
this.payload.aps = {};
}
if(typeof this.badge == 'number') {
if (typeof this.badge == 'number') {
this.payload.aps.badge = this.badge;
}
if(typeof this.sound == 'string') {
if (typeof this.sound == 'string') {
this.payload.aps.sound = this.sound;
}
if(typeof this.alert == 'string' || typeof this.alert == 'object') {
if (typeof this.alert == 'string' || typeof this.alert == 'object') {
this.payload.aps.alert = this.alert;
}
return this.payload;
}
};
var Device = function (/* deviceToken, ascii=true */) {
var self = this;
self.token = undefined;
if(arguments.length > 0) {
if (arguments.length > 0) {
self.setToken.apply(self, arguments);
}
}
};
Device.prototype.parseToken = function (token) {
token = token.replace(/\s/g, "");
length = Math.ceil(token.length / 2);
hexToken = new Buffer(length);
for(var i=0; i < token.length; i+=2) {
word = token[i];
if((i + 1) >= token.length || typeof(token[i+1]) === undefined) {
var length = Math.ceil(token.length / 2);
var hexToken = new Buffer(length);
for (var i = 0; i < token.length; i += 2) {
var word = token[i];
if ((i + 1) >= token.length || typeof(token[i + 1]) === undefined) {
word += '0';
}
else {
word += token[i+1];
word += token[i + 1];
}
hexToken[i/2] = parseInt(word, 16);
hexToken[i / 2] = parseInt(word, 16);
}
return hexToken;
}
};
Device.prototype.setToken = function (newToken, ascii) {
if(ascii === undefined || ascii == true) {
if (ascii === undefined || ascii == true) {
newToken = this.parseToken(newToken);

@@ -300,7 +320,8 @@ }

return this;
}
};
Device.prototype.hexToken = function () {
var out = [],
len = this.token.length;
var out = [];
var len = this.token.length;
var n;
for (var i = 0; i < len; i++) {

@@ -312,21 +333,25 @@ n = this.token[i];

return out.join("");
}
};
var Feedback = function (optionArgs) {
var self = this;
var hasKey = hasCert = false;
var socketOptions = {}
var responsePacketLength = 38;
var hasKey = false;
var hasCert = false;
var socketOptions = {};
var responsePacketLength = 38;
var readBuffer = new Buffer(responsePacketLength);
var readLength = 0;
var options = { cert: 'cert.pem' /* Certificate file */
, key: 'key.pem' /* Key file */
, address: 'feedback.push.apple.com' /* feedback address */
, port: 2196 /* feedback port */
, feedback: false /* enable feedback service, set to callback */
, interval: 3600 /* interval in seconds to connect to feedback service */
};
var options = {
cert: 'cert.pem', /* Certificate file */
certData: '', /* Certificate data */
key: 'key.pem', /* Key file */
keyData: '', /* Key data */
address: 'feedback.push.apple.com', /* feedback address */
port: 2196, /* feedback port */
feedback: false, /* enable feedback service, set to callback */
interval: 3600, /* interval in seconds to connect to feedback service */
};
if (optionArgs) {

@@ -339,47 +364,74 @@ var keys = Object.keys(options);

}
if(typeof options['feedback'] != 'function') {
if (typeof options['feedback'] != 'function') {
return Error(-1, 'A callback function must be specified');
}
this.readyToConnect = function () {
return (hasKey && hasCert);
}
};
self.startSocket = function () {
self.socket = tls.connect(options['port'], options['address'], socketOptions);
self.socket.pair.on('secure', function () { if(!self.socket.authorized) { throw self.socket.authorizationError } });
self.socket.on('data', function(data) { processData(data); });
self.socket.once('error', function(data) {self.socket.removeAllListeners(); self.socket = undefined; });
self.socket.once('end', function () { });
self.socket.once('close', function () { self.socket.removeAllListeners(); self.socket = undefined; });
self.socket.pair.on('secure', function () {
if (!self.socket.authorized) {
throw self.socket.authorizationError;
}
});
self.socket.on('data', function (data) {
processData(data);
});
self.socket.once('error', function () {
self.socket.removeAllListeners();
self.socket = undefined;
});
self.socket.once('end', function () {
});
self.socket.once('close', function () {
self.socket.removeAllListeners();
self.socket = undefined;
});
};
var connect = invoke_after(function () {
self.startSocket();
});
if (options['certData']) {
socketOptions['cert'] = options['certData'];
hasCert = true;
} else {
fs.readFile(options['cert'], connect(function (err, data) {
if (err) {
throw err;
}
socketOptions['cert'] = data.toString();
hasCert = true;
}));
}
var connect = invoke_after(function() { self.startSocket(); });
fs.readFile(options['cert'], connect(function(err, data) {
if(err) {
throw err;
}
socketOptions['cert'] = data.toString();
hasCert = true;
}));
fs.readFile(options['key'], connect(function(err, data) {
if(err) {
throw err;
}
socketOptions['key'] = data.toString();
if (options['keyData']) {
socketOptions['key'] = options['keyData'];
hasKey = true;
}));
if(options['interval'] > 0) {
this.interval = setInterval(function() { self.request(); }, options['interval'] * 1000);
} else {
fs.readFile(options['key'], connect(function (err, data) {
if (err) {
throw err;
}
socketOptions['key'] = data.toString();
hasKey = true;
}));
}
var processData = function(data) {
if (options['interval'] > 0) {
this.interval = setInterval(function () {
self.request();
}, options['interval'] * 1000);
}
var processData = function (data) {
var pos = 0;
// If there is some buffered data, read the remainder and process this first.
if(readLength > 0) {
if(data.length < (responsePacketLength - readLength)) {
if (readLength > 0) {
if (data.length < (responsePacketLength - readLength)) {
data.copy(readBuffer, readLength, 0);

@@ -389,9 +441,9 @@ readLength += data.length;

}
data.copy(readBuffer, readLength, 0, responsePacketLength-readLength);
data.copy(readBuffer, readLength, 0, responsePacketLength - readLength);
decodeResponse(readBuffer, 0);
pos = responsePacketLength-readLength;
pos = responsePacketLength - readLength;
readLength = 0;
}
while(pos<data.length-1) {
if((data.length-pos) < responsePacketLength) {
while (pos < data.length - 1) {
if ((data.length - pos) < responsePacketLength) {
//Buffer remaining data until next time

@@ -405,45 +457,45 @@ data.copy(readBuffer, 0, pos);

}
}
};
var decodeResponse = function(data, start) {
time = bytes2int(data, 4, start);
var decodeResponse = function (data, start) {
var time = bytes2int(data, 4, start);
start += 4;
len = bytes2int(data, 2, start);
var len = bytes2int(data, 2, start);
start += 2;
tok = new Buffer(len);
data.copy(tok, 0, start, start+len);
if(typeof options['feedback'] == 'function') {
options['feedback'](time, new exports.device(tok, false));
var tok = new Buffer(len);
data.copy(tok, 0, start, start + len);
if (typeof options['feedback'] == 'function') {
options['feedback'](time, new exports.Device(tok, false));
}
}
}
};
Feedback.prototype.request = function () {
if((this.socket === undefined || this.socket.readyState == 'closed') && this.readyToConnect()) {
if ((this.socket === undefined || this.socket.readyState == 'closed') && this.readyToConnect()) {
this.startSocket();
}
}
};
Feedback.prototype.cancel = function () {
if(this.interval !== undefined) {
if (this.interval !== undefined) {
clearInterval(this.interval);
}
}
};
function int2buf(number, buffer, start, length) {
function int2buf (number, buffer, start, length) {
length -= 1;
for(var i=0; i<=length; i++) {
buffer[start+length-i] = number & 0xff;
for (var i = 0; i <= length; i++) {
buffer[start + length - i] = number & 0xff;
number = number >> 8;
}
return length+1;
return length + 1;
}
function bytes2int(bytes, length, start) {
if(start === undefined) start = 0;
function bytes2int (bytes, length, start) {
if (start === undefined) start = 0;
var num = 0;
length -= 1;
for(var i=0; i<=length; i++) {
num += (bytes[start+i] << ((length - i) * 8));
for (var i = 0; i <= length; i++) {
num += (bytes[start + i] << ((length - i) * 8));
}

@@ -453,9 +505,9 @@ return num;

function invoke_after(callback) {
function invoke_after (callback) {
var n = 0;
return function (delegate) {
n++;
return function() {
return function () {
delegate.apply(delegate, arguments);
if(--n == 0) callback();
if (--n == 0) callback();
};

@@ -465,6 +517,11 @@ };

exports.Connection = Connection;
exports.connection = Connection;
exports.Notification = Notification;
exports.notification = Notification;
exports.Device = Device;
exports.device = Device;
exports.Feedback = Feedback;
exports.feedback = Feedback;
exports.error = Errors;
exports.error = Errors;
{
"name": "apn",
"description": "An interface to the Apple Push Notification service for Node.js",
"version": "1.1.4",
"version": "1.1.5",
"author": "Andrew Naylor <argon@mkbot.net>",

@@ -6,0 +6,0 @@ "contributors": [

@@ -27,25 +27,35 @@ #node-apn

var apns = require('apn');
### Exported Objects
- Connection
- Notification
- Device
- Feedback
- errors
### Connecting
Create a new connection to the gateway server using a dictionary of options. The defaults are listed below:
options = { cert: 'cert.pem' /* Certificate file */
, key: 'key.pem' /* Key file */
, gateway: 'gateway.push.apple.com' /* gateway address */
, port: 2195 /* gateway port */
, enhanced: true /* enable enhanced format */
, errorCallback: undefined /* Callback when error occurs */
, cacheLength: 5 /* Notifications to cache for error purposes */
};
var apnsConnection = new apns.connection(options);
var options = {
cert: 'cert.pem', /* Certificate file */
certData: null, /* Optional: if supplied uses this instead of Certificate File */
key: 'key.pem', /* Key file */
keyData: null, /* Optional: if supplied uses this instead of Key file */
gateway: 'gateway.push.apple.com',/* gateway address */
port: 2195, /* gateway port */
enhanced: true, /* enable enhanced format */
errorCallback: undefined, /* Callback when error occurs */
cacheLength: 5 /* Number of notifications to cache for error purposes */
};
var apnsConnection = new apns.Connection(options);
### Sending a notification
To send a notification first create a `Device` object. Pass it the device token as either a hexadecimal string, or alternatively as a `Buffer` object containing the binary token, setting the second argument to `false`.
var myDevice = new apns.device(token /*, ascii=true*/);
var myDevice = new apns.Device(token /*, ascii=true*/);
Next create a notification object and set parameters. See the [payload documentation][pl] for more details
var note = new apns.notification();
var note = new apns.Notification();

@@ -80,11 +90,14 @@ note.badge = 3;

options = { cert: 'cert.pem' /* Certificate file */
, key: 'key.pem' /* Key file */
, address: 'feedback.push.apple.com' /* feedback address */
, port: 2196 /* feedback port */
, feedback: false /* callback function */
, interval: 3600 /* query interval in seconds */
};
var options = {
cert: 'cert.pem', /* Certificate file */
certData: null, /* Certificate file contents */
key: 'key.pem', /* Key file */
keyData: null, /* Key file contents */
address: 'feedback.push.apple.com', /* feedback address */
port: 2196, /* feedback port */
feedback: false, /* enable feedback service, set to callback */
interval: 3600 /* interval in seconds to connect to feedback service */
};
var feedback = new apns.feedback(options);
var feedback = new apns.Feedback(options);

@@ -106,3 +119,3 @@ ## Converting your APNs Certificate

Contributors: [Ian Babrou][bobrik], [dgthistle][dgthistle]
Contributors: [Ian Babrou][bobrik], [dgthistle][dgthistle], [Keith Larsen][keithnlarsen], [Mike P][mypark]

@@ -132,8 +145,18 @@ Special thanks to [Ben Noordhuis][bnoordhuis] for `invoke_after` code.

[bnoordhuis]: http://bnoordhuis.nl
[npm]: http://github.com/isaacs/npm
[npm]: https://github.com/isaacs/npm
[bobrik]: http://bobrik.name
[dgthistle]: https://github.com/dgthistle
[keithnlarsen]: https://github.com/keithnlarsen
[mypark]: https://github.com/mypark
## Changelog
1.1.5:
* Feature: Certificate and Key data can be passed directly when creating a new connection instead of providing a file name on disk. (See: `certData` and `keyData` options)
* Deliver whole write buffer if the socket is ready.
* Fixed some global memory leaks.
* Tidied up some code formatting glitches flagged by jslint
* Fixes #16, #17, #18, #19, #20
1.1.4:

@@ -182,2 +205,2 @@

* Well I created a module; Version 0.0.0 had no code, and now it does, and it works, so that's pretty neat, right?
* Well I created a module; Version 0.0.0 had no code, and now it does, and it works, so that's pretty neat, right?
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc