var events = require('events'); /* Make sure we choose the correct build directory */ var bindings = require('bindings')('pcsclite'); var PCSCLite = bindings.PCSCLite; var CardReader = bindings.CardReader; inherits(PCSCLite, events.EventEmitter); inherits(CardReader, events.EventEmitter); var parse_readers_string = function(readers_str) { // Borrowed from https://github.com/pokusew/node-pcsclite try { const string = readers_str.toString().slice(0, -1); // it looks like // ACS ACR122U PICC Interface\u0000ACS ACR122U PICC Interface 01\u0000\u0000 // [reader_name]\u0000[reader_name]\u0000\u0000 // ^separator ^separator^end_separator // returns readers in array // like [ 'ACS ACR122U PICC Interface', 'ACS ACR122U PICC Interface 01' ] return string.split('\u0000').slice(0, -1); } catch (e) { return []; } }; /* * It returns an array with the elements contained in a that aren't contained in b */ function diff(a, b) { return a.filter(function(i) { return b.indexOf(i) === -1; }); }; module.exports = function() { var readers = {}; var p = new PCSCLite(); p.readers = readers; process.nextTick(function() { p.start(function(err, data) { if (err) { return p.emit('error', err); } var names = parse_readers_string(data); var current_names = Object.keys(readers); var new_names = diff(names, current_names); var removed_names = diff(current_names, names); new_names.forEach(function(name) { var r = new CardReader(name); r.on('_end', function() { r.removeAllListeners('status'); r.emit('end'); delete readers[name]; }); readers[name] = r; r.get_status(function(err, state, atr) { if (err) { return r.emit('error', err); } var status = { state : state }; if (atr) { status.atr = atr; } r.emit('status', status); r.state = state; }); p.emit('reader', r); }); removed_names.forEach(function(name) { readers[name].close(); }); }); }); return p; }; CardReader.prototype.connect = function(options, cb) { if (typeof options === 'function') { cb = options; options = undefined; } options = options || {}; options.share_mode = options.share_mode || this.SCARD_SHARE_EXCLUSIVE; if (typeof options.protocol === 'undefined' || options.protocol === null) { options.protocol = this.SCARD_PROTOCOL_T0 | this.SCARD_PROTOCOL_T1; } if (!this.connected) { this._connect(options.share_mode, options.protocol, cb); } else { cb(); } }; CardReader.prototype.disconnect = function(disposition, cb) { if (typeof disposition === 'function') { cb = disposition; disposition = undefined; } if (typeof disposition !== 'number') { disposition = this.SCARD_UNPOWER_CARD; } if (this.connected) { this._disconnect(disposition, cb); } else { cb(); } }; CardReader.prototype.transmit = function(data, res_len, protocol, cb) { if (!this.connected) { return cb(new Error("Card Reader not connected")); } this._transmit(data, res_len, protocol, cb); }; CardReader.prototype.control = function(data, control_code, res_len, cb) { if (!this.connected) { return cb(new Error("Card Reader not connected")); } var output = new Buffer(res_len); this._control(data, control_code, output, function(err, len) { if (err) { return cb(err); } cb(err, output.slice(0, len)); }); }; CardReader.prototype.SCARD_CTL_CODE = function(code) { var isWin = /^win/.test(process.platform); if (isWin) { return (0x31 << 16 | (code) << 2); } else { return 0x42000000 + (code); } }; // extend prototype function inherits(target, source) { for (var k in source.prototype) { target.prototype[k] = source.prototype[k]; } }