#include "cardreader.h" #include "common.h" using namespace v8; using namespace node; Persistent CardReader::constructor; void CardReader::init(Handle target) { // Prepare constructor template Local tpl = NanNew(New); tpl->SetClassName(NanNew("CardReader")); tpl->InstanceTemplate()->SetInternalFieldCount(1); // Symbol NanAssignPersistent(name_symbol, NanNew("name")); NanAssignPersistent(connected_symbol, NanNew("connected")); // Prototype NanSetPrototypeTemplate(tpl, "get_status", NanNew(GetStatus)); NanSetPrototypeTemplate(tpl, "_connect", NanNew(Connect)); NanSetPrototypeTemplate(tpl, "_disconnect", NanNew(Disconnect)); NanSetPrototypeTemplate(tpl, "_transmit", NanNew(Transmit)); NanSetPrototypeTemplate(tpl, "_control", NanNew(Control)); NanSetPrototypeTemplate(tpl, "close", NanNew(Close)); // PCSCLite constants // Share Mode NanSetPrototypeTemplate(tpl, "SCARD_SHARE_SHARED", NanNew(SCARD_SHARE_SHARED)); NanSetPrototypeTemplate(tpl, "SCARD_SHARE_EXCLUSIVE", NanNew(SCARD_SHARE_EXCLUSIVE)); NanSetPrototypeTemplate(tpl, "SCARD_SHARE_DIRECT", NanNew(SCARD_SHARE_DIRECT)); // Protocol NanSetPrototypeTemplate(tpl, "SCARD_PROTOCOL_T0", NanNew(SCARD_PROTOCOL_T0)); NanSetPrototypeTemplate(tpl, "SCARD_PROTOCOL_T1", NanNew(SCARD_PROTOCOL_T1)); NanSetPrototypeTemplate(tpl, "SCARD_PROTOCOL_RAW", NanNew(SCARD_PROTOCOL_RAW)); // State NanSetPrototypeTemplate(tpl, "SCARD_STATE_UNAWARE", NanNew(SCARD_STATE_UNAWARE)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_IGNORE", NanNew(SCARD_STATE_IGNORE)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_CHANGED", NanNew(SCARD_STATE_CHANGED)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_UNKNOWN", NanNew(SCARD_STATE_UNKNOWN)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_UNAVAILABLE", NanNew(SCARD_STATE_UNAVAILABLE)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_EMPTY", NanNew(SCARD_STATE_EMPTY)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_PRESENT", NanNew(SCARD_STATE_PRESENT)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_ATRMATCH", NanNew(SCARD_STATE_ATRMATCH)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_EXCLUSIVE", NanNew(SCARD_STATE_EXCLUSIVE)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_INUSE", NanNew(SCARD_STATE_INUSE)); NanSetPrototypeTemplate(tpl, "SCARD_STATE_MUTE", NanNew(SCARD_STATE_MUTE)); // Disconnect disposition NanSetPrototypeTemplate(tpl, "SCARD_LEAVE_CARD", NanNew(SCARD_LEAVE_CARD)); NanSetPrototypeTemplate(tpl, "SCARD_RESET_CARD", NanNew(SCARD_RESET_CARD)); NanSetPrototypeTemplate(tpl, "SCARD_UNPOWER_CARD", NanNew(SCARD_UNPOWER_CARD)); NanSetPrototypeTemplate(tpl, "SCARD_EJECT_CARD", NanNew(SCARD_EJECT_CARD)); NanAssignPersistent(constructor, tpl->GetFunction()); target->Set(NanNew("CardReader"), tpl->GetFunction()); } CardReader::CardReader(const std::string &reader_name): m_card_context(0), m_card_handle(0), m_name(reader_name), m_state(0) { assert(uv_mutex_init(&m_mutex) == 0); assert(uv_cond_init(&m_cond) == 0); } CardReader::~CardReader() { SCardCancel(m_card_context); int ret = uv_thread_join(&m_status_thread); assert(ret == 0); if (m_card_context) { SCardReleaseContext(m_card_context); } uv_mutex_destroy(&m_mutex); } NAN_METHOD(CardReader::New) { NanScope(); v8::String::Utf8Value reader_name(args[0]->ToString()); CardReader* obj = new CardReader(*reader_name); obj->Wrap(args.Holder()); NanObjectWrapHandle(obj)->Set(NanNew(name_symbol), args[0]->ToString()); NanObjectWrapHandle(obj)->Set(NanNew(connected_symbol), NanFalse()); NanReturnValue(args.Holder()); } NAN_METHOD(CardReader::GetStatus) { NanScope(); CardReader* obj = ObjectWrap::Unwrap(args.This()); Local cb = Local::Cast(args[0]); AsyncBaton *async_baton = new AsyncBaton(); async_baton->async.data = async_baton; NanAssignPersistent(async_baton->callback, cb); async_baton->reader = obj; uv_async_init(uv_default_loop(), &async_baton->async, (uv_async_cb)HandleReaderStatusChange); int ret = uv_thread_create(&obj->m_status_thread, HandlerFunction, async_baton); assert(ret == 0); NanReturnUndefined(); } NAN_METHOD(CardReader::Connect) { NanScope(); // The second argument is the length of the data to be received if (!args[0]->IsUint32()) { return NanThrowError("First argument must be an integer"); } if (!args[1]->IsUint32()) { return NanThrowError("Second argument must be an integer"); } if (!args[2]->IsFunction()) { return NanThrowError("Third argument must be a callback function"); } ConnectInput* ci = new ConnectInput(); ci->share_mode = args[0]->Uint32Value(); ci->pref_protocol = args[1]->Uint32Value(); Local cb = Local::Cast(args[2]); // This creates our work request, including the libuv struct. Baton* baton = new Baton(); baton->request.data = baton; NanAssignPersistent(baton->callback, cb); baton->reader = ObjectWrap::Unwrap(args.This()); 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, DoConnect, reinterpret_cast(AfterConnect)); assert(status == 0); NanReturnUndefined(); } NAN_METHOD(CardReader::Disconnect) { NanScope(); if (!args[0]->IsUint32()) { return NanThrowError("First argument must be an integer"); } if (!args[1]->IsFunction()) { return NanThrowError("Second argument must be a callback function"); } DWORD disposition = args[0]->Uint32Value(); Local cb = Local::Cast(args[1]); // This creates our work request, including the libuv struct. Baton* baton = new Baton(); baton->input = reinterpret_cast(new DWORD(disposition)); baton->request.data = baton; NanAssignPersistent(baton->callback, cb); baton->reader = ObjectWrap::Unwrap(args.This()); // 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, DoDisconnect, reinterpret_cast(AfterDisconnect)); assert(status == 0); NanReturnUndefined(); } NAN_METHOD(CardReader::Transmit) { NanScope(); // The first argument is the buffer to be transmitted. if (!Buffer::HasInstance(args[0])) { return NanThrowError("First argument must be a Buffer"); } // The second argument is the length of the data to be received if (!args[1]->IsUint32()) { return NanThrowError("Second argument must be an integer"); } // The third argument is the protocol to be used if (!args[2]->IsUint32()) { return NanThrowError("Third argument must be an integer"); } // The fourth argument is the callback function if (!args[3]->IsFunction()) { return NanThrowError("Fourth argument must be a callback function"); } Local buffer_data = args[0]->ToObject(); uint32_t out_len = args[1]->Uint32Value(); uint32_t protocol = args[2]->Uint32Value(); Local cb = Local::Cast(args[3]); // This creates our work request, including the libuv struct. Baton* baton = new Baton(); baton->request.data = baton; NanAssignPersistent(baton->callback, cb); baton->reader = ObjectWrap::Unwrap(args.This()); TransmitInput *ti = new TransmitInput(); ti->card_protocol = protocol; ti->in_data = new unsigned char[Buffer::Length(buffer_data)]; ti->in_len = Buffer::Length(buffer_data); memcpy(ti->in_data, Buffer::Data(buffer_data), ti->in_len); ti->out_len = out_len; baton->input = ti; // 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, DoTransmit, reinterpret_cast(AfterTransmit)); assert(status == 0); NanReturnUndefined(); } NAN_METHOD(CardReader::Control) { NanScope(); // The first argument is the buffer to be transmitted. if (!Buffer::HasInstance(args[0])) { return NanThrowError("First argument must be a Buffer"); } // The second argument is the control code to be used if (!args[1]->IsUint32()) { return NanThrowError("Second argument must be an integer"); } // The third argument is output buffer if (!Buffer::HasInstance(args[2])) { return NanThrowError("Third argument must be a Buffer"); } // The fourth argument is the callback function if (!args[3]->IsFunction()) { return NanThrowError("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; NanAssignPersistent(baton->callback, 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, reinterpret_cast(AfterControl)); assert(status == 0); NanReturnUndefined(); } NAN_METHOD(CardReader::Close) { NanScope(); LONG result = SCARD_S_SUCCESS; CardReader* obj = ObjectWrap::Unwrap(args.This()); uv_mutex_lock(&obj->m_mutex); if (obj->m_state == 0) { obj->m_state = 1; do { result = SCardCancel(obj->m_status_card_context); } while (uv_cond_timedwait(&obj->m_cond, &obj->m_mutex, 10000000) != 0); } uv_mutex_unlock(&obj->m_mutex); uv_mutex_destroy(&obj->m_mutex); uv_cond_destroy(&obj->m_cond); assert(uv_thread_join(&obj->m_status_thread) == 0); obj->m_status_thread = 0; NanReturnValue(NanNew(result)); } void CardReader::HandleReaderStatusChange(uv_async_t *handle, int status) { NanScope(); AsyncBaton* async_baton = static_cast(handle->data); AsyncResult* ar = async_baton->async_result; if (ar->do_exit) { uv_close(reinterpret_cast(&async_baton->async), CloseCallback); // necessary otherwise UV will block /* Emit end event */ Handle argv[1] = { NanNew("_end"), // event name }; NanMakeCallback(NanObjectWrapHandle(async_baton->reader), "emit", 1, argv); return; } if (ar->result == SCARD_S_SUCCESS) { const unsigned argc = 3; Handle argv[argc] = { NanUndefined(), // argument NanNew(ar->status), NanNewBufferHandle(reinterpret_cast(ar->atr), ar->atrlen) }; NanCallback(NanNew(async_baton->callback)).Call(argc, argv); } else { Local err = NanError(error_msg("SCardGetStatusChange", ar->result).c_str()); // Prepare the parameters for the callback function. const unsigned argc = 1; Handle argv[argc] = { err }; NanCallback(NanNew(async_baton->callback)).Call(argc, argv); } } void CardReader::HandlerFunction(void* arg) { AsyncBaton* async_baton = static_cast(arg); CardReader* reader = async_baton->reader; async_baton->async_result = new AsyncResult(); async_baton->async_result->do_exit = false; LONG result = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &reader->m_status_card_context); SCARD_READERSTATE card_reader_state = SCARD_READERSTATE(); card_reader_state.szReader = reader->m_name.c_str(); card_reader_state.dwCurrentState = SCARD_STATE_UNAWARE; bool keep_watching(result == SCARD_S_SUCCESS); while (keep_watching) { result = SCardGetStatusChange(reader->m_status_card_context, INFINITE, &card_reader_state, 1); keep_watching = ((result == SCARD_S_SUCCESS) && (!reader->m_state) && (!((card_reader_state.dwCurrentState & SCARD_STATE_UNKNOWN) || (card_reader_state.dwCurrentState & SCARD_STATE_UNAVAILABLE)))); uv_mutex_lock(&reader->m_mutex); if (reader->m_state == 1) { uv_cond_signal(&reader->m_cond); } if (!keep_watching) { reader->m_state = 2; } uv_mutex_unlock(&reader->m_mutex); async_baton->async_result->result = result; async_baton->async_result->status = card_reader_state.dwEventState; memcpy(async_baton->async_result->atr, card_reader_state.rgbAtr, card_reader_state.cbAtr); async_baton->async_result->atrlen = card_reader_state.cbAtr; uv_async_send(&async_baton->async); card_reader_state.dwCurrentState = card_reader_state.dwEventState; } async_baton->async_result->do_exit = true; uv_async_send(&async_baton->async); } void CardReader::DoConnect(uv_work_t* req) { Baton* baton = static_cast(req->data); ConnectInput *ci = static_cast(baton->input); DWORD card_protocol; LONG result = SCARD_S_SUCCESS; CardReader* obj = baton->reader; /* Lock mutex */ uv_mutex_lock(&obj->m_mutex); /* Is context established */ if (!obj->m_card_context) { result = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &obj->m_card_context); } /* Connect */ if (result == SCARD_S_SUCCESS) { result = SCardConnect(obj->m_card_context, obj->m_name.c_str(), ci->share_mode, ci->pref_protocol, &obj->m_card_handle, &card_protocol); } /* Unlock the mutex */ uv_mutex_unlock(&obj->m_mutex); ConnectResult *cr = new ConnectResult(); cr->result = result; if (!result) { cr->card_protocol = card_protocol; } baton->result = cr; } void CardReader::AfterConnect(uv_work_t* req, int status) { NanScope(); Baton* baton = static_cast(req->data); ConnectInput *ci = static_cast(baton->input); ConnectResult *cr = static_cast(baton->result); if (cr->result) { Local err = NanError(error_msg("SCardConnect", cr->result).c_str()); // Prepare the parameters for the callback function. const unsigned argc = 1; Handle argv[argc] = { err }; NanCallback(NanNew(baton->callback)).Call(argc, argv); } else { NanObjectWrapHandle(baton->reader)->Set(NanNew(connected_symbol), NanTrue()); const unsigned argc = 2; Handle argv[argc] = { NanNull(), NanNew(cr->card_protocol) }; NanCallback(NanNew(baton->callback)).Call(argc, argv); } // The callback is a permanent handle, so we have to dispose of it manually. NanDisposePersistent(baton->callback); delete ci; delete cr; delete baton; } void CardReader::DoDisconnect(uv_work_t* req) { Baton* baton = static_cast(req->data); DWORD* disposition = reinterpret_cast(baton->input); LONG result = SCARD_S_SUCCESS; CardReader* obj = baton->reader; /* Lock mutex */ uv_mutex_lock(&obj->m_mutex); /* Connect */ if (obj->m_card_handle) { result = SCardDisconnect(obj->m_card_handle, *disposition); if (result == SCARD_S_SUCCESS) { obj->m_card_handle = 0; } } /* Unlock the mutex */ uv_mutex_unlock(&obj->m_mutex); baton->result = reinterpret_cast(new LONG(result)); } void CardReader::AfterDisconnect(uv_work_t* req, int status) { NanScope(); Baton* baton = static_cast(req->data); LONG* result = reinterpret_cast(baton->result); if (*result) { Local err = NanError(error_msg("SCardDisconnect", *result).c_str()); // Prepare the parameters for the callback function. const unsigned argc = 1; Handle argv[argc] = { err }; NanCallback(NanNew(baton->callback)).Call(argc, argv); } else { NanObjectWrapHandle(baton->reader)->Set(NanNew(connected_symbol), NanFalse()); const unsigned argc = 1; Handle argv[argc] = { NanNull() }; NanCallback(NanNew(baton->callback)).Call(argc, argv); } // The callback is a permanent handle, so we have to dispose of it manually. NanDisposePersistent(baton->callback); DWORD* disposition = reinterpret_cast(baton->input); delete disposition; delete result; delete baton; } void CardReader::DoTransmit(uv_work_t* req) { Baton* baton = static_cast(req->data); TransmitInput *ti = static_cast(baton->input); CardReader* obj = baton->reader; SCARD_IO_REQUEST io_request; TransmitResult *tr = new TransmitResult(); tr->data = new unsigned char[ti->out_len]; tr->len = ti->out_len; LONG result = SCARD_E_INVALID_HANDLE; /* Lock mutex */ uv_mutex_lock(&obj->m_mutex); /* Connected? */ if (obj->m_card_handle) { SCARD_IO_REQUEST send_pci = { ti->card_protocol, sizeof(SCARD_IO_REQUEST) }; result = SCardTransmit(obj->m_card_handle, &send_pci, ti->in_data, ti->in_len, &io_request, tr->data, &tr->len); } /* Unlock the mutex */ uv_mutex_unlock(&obj->m_mutex); tr->result = result; baton->result = tr; } void CardReader::AfterTransmit(uv_work_t* req, int status) { NanScope(); Baton* baton = static_cast(req->data); TransmitInput *ti = static_cast(baton->input); TransmitResult *tr = static_cast(baton->result); if (tr->result) { Local err = NanError(error_msg("SCardTransmit", tr->result).c_str()); // Prepare the parameters for the callback function. const unsigned argc = 1; Handle argv[argc] = { err }; NanCallback(NanNew(baton->callback)).Call(argc, argv); } else { const unsigned argc = 2; Handle argv[argc] = { NanNull(), NanNewBufferHandle(reinterpret_cast(tr->data), tr->len) }; NanCallback(NanNew(baton->callback)).Call(argc, argv); } // The callback is a permanent handle, so we have to dispose of it manually. NanDisposePersistent(baton->callback); delete [] ti->in_data; delete ti; delete [] tr->data; delete tr; 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 */ uv_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 */ uv_mutex_unlock(&obj->m_mutex); cr->result = result; baton->result = cr; } void CardReader::AfterControl(uv_work_t* req, int status) { NanScope(); Baton* baton = static_cast(req->data); ControlInput *ci = static_cast(baton->input); ControlResult *cr = static_cast(baton->result); if (cr->result) { Local err = NanError(error_msg("SCardControl", cr->result).c_str()); // Prepare the parameters for the callback function. const unsigned argc = 1; Handle argv[argc] = { err }; NanCallback(NanNew(baton->callback)).Call(argc, argv); } else { const unsigned argc = 2; Handle argv[argc] = { NanNull(), NanNew(cr->len) }; NanCallback(NanNew(baton->callback)).Call(argc, argv); } // The callback is a permanent handle, so we have to dispose of it manually. NanDisposePersistent(baton->callback); delete ci; delete cr; delete baton; } void CardReader::CloseCallback(uv_handle_t *handle) { /* cleanup process */ AsyncBaton* async_baton = static_cast(handle->data); AsyncResult* ar = async_baton->async_result; delete ar; NanDisposePersistent(async_baton->callback); SCardReleaseContext(async_baton->reader->m_status_card_context); delete async_baton; }