#include "pcsclite.h" #include "common.h" using namespace v8; using namespace node; Nan::Persistent PCSCLite::constructor; Nan::AsyncResource *PCSCLite::async_resource = new Nan::AsyncResource("PCSCLite_StaticAsyncResource"); void PCSCLite::init(Local target) { // Prepare constructor template Local tpl = Nan::New(New); tpl->SetClassName(Nan::New("PCSCLite").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); // Prototype Nan::SetPrototypeTemplate(tpl, "start", Nan::New(Start)); Nan::SetPrototypeTemplate(tpl, "close", Nan::New(Close)); constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked()); Nan::Set(target, Nan::New("PCSCLite").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); } PCSCLite::PCSCLite(): m_card_context(0), m_card_reader_state(), m_status_thread(0), m_state(0) { assert(uv_mutex_init(&m_mutex) == 0); assert(uv_cond_init(&m_cond) == 0); #ifdef _WIN32 HKEY hKey; DWORD startStatus, datacb = sizeof(DWORD); LONG _res; _res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\SCardSvr", 0, KEY_READ, &hKey); if (_res != ERROR_SUCCESS) { printf("Reg Open Key exited with %d\n", _res); goto postServiceCheck; } _res = RegQueryValueEx(hKey, "Start", NULL, NULL, (LPBYTE)&startStatus, &datacb); if (_res != ERROR_SUCCESS) { printf("Reg Query Value exited with %d\n", _res); goto postServiceCheck; } if (startStatus != 2) { SHELLEXECUTEINFO seInfo = {0}; seInfo.cbSize = sizeof(SHELLEXECUTEINFO); seInfo.fMask = SEE_MASK_NOCLOSEPROCESS; seInfo.hwnd = NULL; seInfo.lpVerb = "runas"; seInfo.lpFile = "sc.exe"; seInfo.lpParameters = "config SCardSvr start=auto"; seInfo.lpDirectory = NULL; seInfo.nShow = SW_SHOWNORMAL; seInfo.hInstApp = NULL; if (!ShellExecuteEx(&seInfo)) { printf("Shell Execute failed with %d\n", GetLastError()); goto postServiceCheck; } WaitForSingleObject(seInfo.hProcess, INFINITE); CloseHandle(seInfo.hProcess); } postServiceCheck: #endif // _WIN32 LONG result; do { result = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &m_card_context); } while(result == SCARD_E_NO_SERVICE || result == SCARD_E_SERVICE_STOPPED); if (result != SCARD_S_SUCCESS) { Nan::ThrowError(error_msg("SCardEstablishContext", result).c_str()); } else { m_card_reader_state.szReader = "\\\\?PnP?\\Notification"; m_card_reader_state.dwCurrentState = SCARD_STATE_UNAWARE; result = SCardGetStatusChange(m_card_context, 0, &m_card_reader_state, 1); if ((result != SCARD_S_SUCCESS) && (result != (LONG)SCARD_E_TIMEOUT)) { Nan::ThrowError(error_msg("SCardGetStatusChange", result).c_str()); } else { m_pnp = !(m_card_reader_state.dwEventState & SCARD_STATE_UNKNOWN); } } } PCSCLite::~PCSCLite() { if (m_status_thread) { SCardCancel(m_card_context); assert(uv_thread_join(&m_status_thread) == 0); } if (m_card_context) { SCardReleaseContext(m_card_context); } uv_cond_destroy(&m_cond); uv_mutex_destroy(&m_mutex); } NAN_METHOD(PCSCLite::New) { Nan::HandleScope scope; PCSCLite* obj = new PCSCLite(); obj->Wrap(info.Holder()); info.GetReturnValue().Set(info.Holder()); } NAN_METHOD(PCSCLite::Start) { Nan::HandleScope scope; PCSCLite* obj = Nan::ObjectWrap::Unwrap(info.This()); Local cb = Local::Cast(info[0]); AsyncBaton *async_baton = new AsyncBaton(); async_baton->async.data = async_baton; async_baton->callback.Reset(cb); async_baton->pcsclite = 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); } NAN_METHOD(PCSCLite::Close) { Nan::HandleScope scope; PCSCLite* obj = Nan::ObjectWrap::Unwrap(info.This()); LONG result = SCARD_S_SUCCESS; if (obj->m_pnp) { if (obj->m_status_thread) { uv_mutex_lock(&obj->m_mutex); if (obj->m_state == 0) { int ret; int times = 0; obj->m_state = 1; do { result = SCardCancel(obj->m_card_context); ret = uv_cond_timedwait(&obj->m_cond, &obj->m_mutex, 10000000); } while ((ret != 0) && (++ times < 5)); } uv_mutex_unlock(&obj->m_mutex); } } else { obj->m_state = 1; } if (obj->m_status_thread) { assert(uv_thread_join(&obj->m_status_thread) == 0); obj->m_status_thread = 0; } info.GetReturnValue().Set(Nan::New(result)); } void PCSCLite::HandleReaderStatusChange(uv_async_t *handle) { Nan::HandleScope scope; AsyncBaton* async_baton = static_cast(handle->data); AsyncResult* ar = async_baton->async_result; if (async_baton->pcsclite->m_state == 1) { // Swallow events : Listening thread was cancelled by user. } else if ((ar->result == SCARD_S_SUCCESS) || (ar->result == (LONG)SCARD_E_NO_READERS_AVAILABLE)) { const unsigned argc = 2; Local argv[argc] = { Nan::Undefined(), // argument Nan::CopyBuffer(ar->readers_name, ar->readers_name_length).ToLocalChecked() }; Nan::Callback(Nan::New(async_baton->callback)).Call(argc, argv, async_resource); } else { Local argv[1] = { Nan::Error(ar->err_msg.c_str()) }; Nan::Callback(Nan::New(async_baton->callback)).Call(1, argv, async_resource); } // Do exit, after throwing last events if (ar->do_exit) { // necessary otherwise UV will block uv_close(reinterpret_cast(&async_baton->async), CloseCallback); return; } /* reset AsyncResult */ #ifdef SCARD_AUTOALLOCATE PCSCLite* pcsclite = async_baton->pcsclite; SCardFreeMemory(pcsclite->m_card_context, ar->readers_name); #else delete [] ar->readers_name; #endif ar->readers_name = NULL; ar->readers_name_length = 0; ar->result = SCARD_S_SUCCESS; } void PCSCLite::HandlerFunction(void* arg) { LONG result = SCARD_S_SUCCESS; AsyncBaton* async_baton = static_cast(arg); PCSCLite* pcsclite = async_baton->pcsclite; async_baton->async_result = new AsyncResult(); while (!pcsclite->m_state) { /* Get card readers */ result = pcsclite->get_card_readers(pcsclite, async_baton->async_result); if (result == (LONG)SCARD_E_NO_READERS_AVAILABLE) { result = SCARD_S_SUCCESS; } /* Store the result in the baton */ async_baton->async_result->result = result; if (result != SCARD_S_SUCCESS) { async_baton->async_result->err_msg = error_msg("SCardListReaders", result); } /* Notify the nodejs thread */ uv_async_send(&async_baton->async); if (result == SCARD_S_SUCCESS) { if (pcsclite->m_pnp) { /* Set current status */ pcsclite->m_card_reader_state.dwCurrentState = pcsclite->m_card_reader_state.dwEventState; /* Start checking for status change */ result = SCardGetStatusChange(pcsclite->m_card_context, INFINITE, &pcsclite->m_card_reader_state, 1); uv_mutex_lock(&pcsclite->m_mutex); async_baton->async_result->result = result; if (pcsclite->m_state) { uv_cond_signal(&pcsclite->m_cond); } if (result != SCARD_S_SUCCESS) { pcsclite->m_state = 2; async_baton->async_result->err_msg = error_msg("SCardGetStatusChange", result); } uv_mutex_unlock(&pcsclite->m_mutex); } else { /* If PnP is not supported, just wait for 1 second */ Sleep(1000); } } else { /* Error on last card access, stop monitoring */ pcsclite->m_state = 2; } } async_baton->async_result->do_exit = true; uv_async_send(&async_baton->async); } void PCSCLite::CloseCallback(uv_handle_t *handle) { /* cleanup process */ AsyncBaton* async_baton = static_cast(handle->data); AsyncResult* ar = async_baton->async_result; #ifdef SCARD_AUTOALLOCATE PCSCLite* pcsclite = async_baton->pcsclite; SCardFreeMemory(pcsclite->m_card_context, ar->readers_name); #else delete [] ar->readers_name; #endif delete ar; async_baton->callback.Reset(); delete async_baton; } LONG PCSCLite::get_card_readers(PCSCLite* pcsclite, AsyncResult* async_result) { DWORD readers_name_length; LPTSTR readers_name; LONG result = SCARD_S_SUCCESS; /* Reset the readers_name in the baton */ async_result->readers_name = NULL; async_result->readers_name_length = 0; #ifdef SCARD_AUTOALLOCATE readers_name_length = SCARD_AUTOALLOCATE; result = SCardListReaders(pcsclite->m_card_context, NULL, (LPTSTR)&readers_name, &readers_name_length); #else /* Find out ReaderNameLength */ result = SCardListReaders(pcsclite->m_card_context, NULL, NULL, &readers_name_length); if (result != SCARD_S_SUCCESS) { return result; } /* * Allocate Memory for ReaderName and retrieve all readers in the terminal */ readers_name = new char[readers_name_length]; result = SCardListReaders(pcsclite->m_card_context, NULL, readers_name, &readers_name_length); #endif if (result != SCARD_S_SUCCESS) { #ifndef SCARD_AUTOALLOCATE delete [] readers_name; #endif readers_name = NULL; readers_name_length = 0; #ifndef SCARD_AUTOALLOCATE /* Retry in case of insufficient buffer error */ if (result == (LONG)SCARD_E_INSUFFICIENT_BUFFER) { result = get_card_readers(pcsclite, async_result); } #endif if (result == SCARD_E_NO_SERVICE || result == SCARD_E_SERVICE_STOPPED) { SCardReleaseContext(pcsclite->m_card_context); SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pcsclite->m_card_context); result = get_card_readers(pcsclite, async_result); } } else { /* Store the readers_name in the baton */ async_result->readers_name = readers_name; async_result->readers_name_length = readers_name_length; } return result; }