'use strict'; var log = require('winston'), merge = require('utils-merge'), net = require('net'); EventEmitter = require('events').EventEmitter; function Session(options) { var self = this; // Makes this available even in lower level function scopes // Also make this Session an eventEmitter EventEmitter.call(this); // Set default options options = merge({ 'host': 'localhost', 'port': 2775 }, options || {}); // Initiate sequence at 0 // This will increment on each command sent over this session this.sequence = 0; if (options.socket) { this.socket = options.socket; } else { this.port = options.port; this.host = options.host; this.socket = net.connect(this.port, this.host); this.socket.on('connect', function() { self.emit('connect'); }); } this.socket.on('data', function(chunk) { self._buffer = Buffer.concat([self._buffer, chunk]); self._extractPDUs(); }); this.socket.on('close', function() { self.emit('close'); }); this.socket.on('error', function(e) { self.emit('error', e); }); } util.inherits(Session, EventEmitter); Session.prototype.connect = function() { this.sequence = 0; this._callbacks = []; this._buffer = new Buffer(0); this.socket.connect(this.port, this.host); }; Session.prototype._extractPDUs = function() { var pdu; while (!this.paused && (pdu = PDU.fromBuffer(this._buffer))) { this._buffer = this._buffer.slice(pdu.command_length); this.emit('pdu', pdu); this.emit(pdu.command, pdu); if (pdu.isResponse() && this._callbacks[pdu.sequence_number]) { this._callbacks[pdu.sequence_number](pdu); delete this._callbacks[pdu.sequence_number]; } } }; Session.prototype.send = function(pdu, callback) { if (!this.socket.writable) { return false; } if (!pdu.isResponse()) { // when server/session pair is used to proxy smpp // traffic, the sequence_number will be provided by // client otherwise we generate it automatically if (!pdu.sequence_number) { pdu.sequence_number = ++this.sequence; } if (callback) { this._callbacks[pdu.sequence_number] = callback; } } this.socket.write(pdu.toBuffer(), function() { this.emit('send', pdu); }.bind(this)); return true; }; Session.prototype.pause = function() { this.paused = true; this.socket.pause(); }; Session.prototype.resume = function() { this.paused = false; this.socket.resume(); this._extractPDUs(); }; Session.prototype.close = function() { this.socket.end(); }; var createShortcut = function(command) { return function(options, callback) { return this.send(new PDU(command, options), callback); }; }; for (var command in defs.commands) { Session.prototype[command] = createShortcut(command); } function Server() { var options, self = this; this.sessions = []; if (typeof arguments[0] == 'function') { options = {}; this.on('session', arguments[0]); } else { options = arguments[0] || {}; if (typeof arguments[1] == 'function') { this.on('session', arguments[1]); } } net.Server.call(this, options, function(socket) { var session = new Session({socket: socket}); session.server = self; self.sessions.push(session); socket.on('close', function() { self.sessions.splice(self.sessions.indexOf(socket), 1); }); self.emit('session', session); }); } util.inherits(Server, net.Server); Server.prototype.listen = function() { var args = [2775]; if (typeof arguments[0] == 'function') { args[1] = arguments[0]; } else if (arguments.length > 0) { args = arguments; } return net.Server.prototype.listen.apply(this, args); }; exports.createServer = function() { return new Server(arguments[0], arguments[1]); }; exports.connect = exports.createSession = function(host, port) { return new Session({ host: host || 'localhost', port: port || 2775 // Default SMPP port is 2775 }); }; exports.addCommand = function(command, options) { options.command = command; defs.commands[command] = options; defs.commandsById[options.id] = options; Session.prototype[command] = createShortcut(command); }; exports.addTLV = function(tag, options) { options.tag = tag; defs.tlvs[tlv] = options; defs.tlvsById[options.id] = options; }; exports.Session = Session; exports.Server = Server; exports.PDU = PDU; for (var key in defs) { exports[key] = defs[key]; } for (var error in defs.errors) { exports[error] = defs.errors[error]; } for (var key in defs.consts) { exports[key] = defs.consts[key]; }