diff --git a/lib/pcsclite.js b/lib/pcsclite.js index 9f1e0fe..ed10230 100644 --- a/lib/pcsclite.js +++ b/lib/pcsclite.js @@ -92,6 +92,21 @@ CardReader.prototype.transmit = function(data, res_len, protocol, cb) { 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_STATE_UNAWARE = 0x0000; CardReader.prototype.SCARD_STATE_IGNORE = 0x0001; CardReader.prototype.SCARD_STATE_CHANGED = 0x0002; diff --git a/src/cardreader.cpp b/src/cardreader.cpp index 863d2ed..827d5a6 100644 --- a/src/cardreader.cpp +++ b/src/cardreader.cpp @@ -27,6 +27,7 @@ void CardReader::init(Handle target) { NODE_SET_PROTOTYPE_METHOD(tpl, "_connect", Connect); NODE_SET_PROTOTYPE_METHOD(tpl, "_disconnect", Disconnect); NODE_SET_PROTOTYPE_METHOD(tpl, "_transmit", Transmit); + NODE_SET_PROTOTYPE_METHOD(tpl, "_control", Control); NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close); constructor = Persistent::New(tpl->GetFunction()); @@ -195,6 +196,61 @@ Handle CardReader::Transmit(const Arguments& args) { return scope.Close(Undefined()); } +Handle CardReader::Control(const Arguments& args) { + + HandleScope scope; + + // The first argument is the buffer to be transmitted. + if (!Buffer::HasInstance(args[0])) { + return ThrowException(Exception::TypeError( + String::New("First argument must be a Buffer"))); + } + + // The second argument is the control code to be used + if (!args[1]->IsUint32()) { + return ThrowException(Exception::TypeError( + String::New("Second argument must be an integer"))); + } + + // The third argument is output buffer + if (!Buffer::HasInstance(args[2])) { + return ThrowException(Exception::TypeError( + String::New("First argument must be a Buffer"))); + } + + // The fourth argument is the callback function + if (!args[3]->IsFunction()) { + return ThrowException(Exception::TypeError( + String::New("Fourth argument must be a callback function"))); + } + + Local in_buf = args[0]->ToObject(); + DWORD control_code = args[1]->Uint32Value(); + Local out_buf = args[2]->ToObject(); + Local cb = Local::Cast(args[3]); + + // This creates our work request, including the libuv struct. + Baton* baton = new Baton(); + baton->request.data = baton; + baton->callback = Persistent::New(cb); + baton->reader = ObjectWrap::Unwrap(args.This()); + ControlInput *ci = new ControlInput(); + ci->control_code = control_code; + ci->in_data = Buffer::Data(in_buf); + ci->in_len = Buffer::Length(in_buf); + ci->out_data = Buffer::Data(out_buf); + ci->out_len = Buffer::Length(out_buf); + baton->input = ci; + + // Schedule our work request with libuv. Here you can specify the functions + // that should be executed in the threadpool and back in the main thread + // after the threadpool function completed. + int status = uv_queue_work(uv_default_loop(), &baton->request, DoControl, AfterControl); + assert(status == 0); + + return scope.Close(Undefined()); +} + Handle CardReader::Close(const Arguments& args) { HandleScope scope; @@ -467,6 +523,72 @@ void CardReader::AfterTransmit(uv_work_t* req) { delete baton; } +void CardReader::DoControl(uv_work_t* req) { + + Baton* baton = static_cast(req->data); + ControlInput *ci = static_cast(baton->input); + CardReader* obj = baton->reader; + + ControlResult *cr = new ControlResult(); + LONG result = SCARD_E_INVALID_HANDLE; + + /* Lock mutex */ + pthread_mutex_lock(&obj->m_mutex); + /* Connected? */ + if (obj->m_card_handle) { + result = SCardControl(obj->m_card_handle, + ci->control_code, + ci->in_data, + ci->in_len, + ci->out_data, + ci->out_len, + &cr->len); + } + + /* Unlock the mutex */ + pthread_mutex_unlock(&obj->m_mutex); + + cr->result = result; + + baton->result = cr; +} + +#if NODE_VERSION_AT_LEAST(0, 9, 4) +void CardReader::AfterControl(uv_work_t* req, int status) { +#else +void CardReader::AfterControl(uv_work_t* req) { +#endif + + HandleScope scope; + Baton* baton = static_cast(req->data); + ControlInput *ci = static_cast(baton->input); + ControlResult *cr = static_cast(baton->result); + + if (cr->result) { + Local err = Exception::Error(String::New(pcsc_stringify_error(cr->result))); + + // Prepare the parameters for the callback function. + const unsigned argc = 1; + Handle argv[argc] = { err }; + PerformCallback(baton->reader->handle_, baton->callback, argc, argv); + } else { + const unsigned argc = 2; + Handle argv[argc] = { + Local::New(Null()), + Integer::New(cr->len) + }; + + PerformCallback(baton->reader->handle_, baton->callback, argc, argv); + } + + + // The callback is a permanent handle, so we have to dispose of it manually. + baton->callback.Dispose(); + delete ci; + delete cr; + delete baton; +} + void CardReader::CloseCallback(uv_handle_t *handle) { /* cleanup process */ diff --git a/src/cardreader.h b/src/cardreader.h index 3ffcf8d..8e331cf 100644 --- a/src/cardreader.h +++ b/src/cardreader.h @@ -39,6 +39,19 @@ class CardReader: public node::ObjectWrap { unsigned long len; }; + struct ControlInput { + DWORD control_code; + LPCVOID in_data; + DWORD in_len; + LPVOID out_data; + DWORD out_len; + }; + + struct ControlResult { + LONG result; + unsigned long len; + }; + struct AsyncResult { LONG result; unsigned long status; @@ -71,6 +84,7 @@ class CardReader: public node::ObjectWrap { static v8::Handle Connect(const v8::Arguments& args); static v8::Handle Disconnect(const v8::Arguments& args); static v8::Handle Transmit(const v8::Arguments& args); + static v8::Handle Control(const v8::Arguments& args); static v8::Handle Close(const v8::Arguments& args); static void HandleReaderStatusChange(uv_async_t *handle, int status); @@ -78,16 +92,19 @@ class CardReader: public node::ObjectWrap { static void DoConnect(uv_work_t* req); static void DoDisconnect(uv_work_t* req); static void DoTransmit(uv_work_t* req); + static void DoControl(uv_work_t* req); static void CloseCallback(uv_handle_t *handle); #if NODE_VERSION_AT_LEAST(0, 9, 4) static void AfterConnect(uv_work_t* req, int status); static void AfterDisconnect(uv_work_t* req, int status); static void AfterTransmit(uv_work_t* req, int status); + static void AfterControl(uv_work_t* req, int status); #else static void AfterConnect(uv_work_t* req); static void AfterDisconnect(uv_work_t* req); static void AfterTransmit(uv_work_t* req); + static void AfterControl(uv_work_t* req); #endif