smartcard 1.0.46 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,248 @@
1
+ #include "pcsc_card.h"
2
+ #include "pcsc_errors.h"
3
+ #include "async_workers.h"
4
+ #include <cstring>
5
+
6
+ Napi::FunctionReference PCSCCard::constructor;
7
+
8
+ Napi::Object PCSCCard::Init(Napi::Env env, Napi::Object exports) {
9
+ Napi::Function func = DefineClass(env, "Card", {
10
+ InstanceAccessor("protocol", &PCSCCard::GetProtocolValue, nullptr),
11
+ InstanceAccessor("connected", &PCSCCard::GetConnectedValue, nullptr),
12
+ InstanceAccessor("atr", &PCSCCard::GetAtr, nullptr),
13
+ InstanceMethod("transmit", &PCSCCard::Transmit),
14
+ InstanceMethod("control", &PCSCCard::Control),
15
+ InstanceMethod("getStatus", &PCSCCard::GetStatus),
16
+ InstanceMethod("disconnect", &PCSCCard::Disconnect),
17
+ InstanceMethod("reconnect", &PCSCCard::Reconnect),
18
+ });
19
+
20
+ constructor = Napi::Persistent(func);
21
+ constructor.SuppressDestruct();
22
+
23
+ exports.Set("Card", func);
24
+ return exports;
25
+ }
26
+
27
+ Napi::Object PCSCCard::NewInstance(Napi::Env env, SCARDHANDLE card,
28
+ DWORD protocol, const std::string& readerName) {
29
+ Napi::Object obj = constructor.New({});
30
+ PCSCCard* cardObj = Napi::ObjectWrap<PCSCCard>::Unwrap(obj);
31
+ cardObj->card_ = card;
32
+ cardObj->protocol_ = protocol;
33
+ cardObj->readerName_ = readerName;
34
+ cardObj->connected_ = true;
35
+ return obj;
36
+ }
37
+
38
+ PCSCCard::PCSCCard(const Napi::CallbackInfo& info)
39
+ : Napi::ObjectWrap<PCSCCard>(info),
40
+ card_(0),
41
+ protocol_(SCARD_PROTOCOL_UNDEFINED),
42
+ connected_(false) {
43
+ // Properties set via NewInstance
44
+ }
45
+
46
+ PCSCCard::~PCSCCard() {
47
+ if (connected_ && card_ != 0) {
48
+ SCardDisconnect(card_, SCARD_LEAVE_CARD);
49
+ connected_ = false;
50
+ card_ = 0;
51
+ }
52
+ }
53
+
54
+ Napi::Value PCSCCard::GetProtocolValue(const Napi::CallbackInfo& info) {
55
+ return Napi::Number::New(info.Env(), protocol_);
56
+ }
57
+
58
+ Napi::Value PCSCCard::GetConnectedValue(const Napi::CallbackInfo& info) {
59
+ return Napi::Boolean::New(info.Env(), connected_);
60
+ }
61
+
62
+ Napi::Value PCSCCard::GetAtr(const Napi::CallbackInfo& info) {
63
+ Napi::Env env = info.Env();
64
+
65
+ if (!connected_) {
66
+ return env.Null();
67
+ }
68
+
69
+ // Get ATR via SCardStatus
70
+ DWORD readerLen = 0;
71
+ DWORD state = 0;
72
+ DWORD protocol = 0;
73
+ BYTE atr[MAX_ATR_SIZE];
74
+ DWORD atrLen = sizeof(atr);
75
+
76
+ LONG result = SCardStatus(card_, nullptr, &readerLen, &state, &protocol, atr, &atrLen);
77
+
78
+ if (result != SCARD_S_SUCCESS) {
79
+ return env.Null();
80
+ }
81
+
82
+ return Napi::Buffer<uint8_t>::Copy(env, atr, atrLen);
83
+ }
84
+
85
+ Napi::Value PCSCCard::Transmit(const Napi::CallbackInfo& info) {
86
+ Napi::Env env = info.Env();
87
+
88
+ if (!connected_) {
89
+ Napi::Error::New(env, "Card is not connected").ThrowAsJavaScriptException();
90
+ return env.Null();
91
+ }
92
+
93
+ if (info.Length() < 1) {
94
+ Napi::TypeError::New(env, "Expected command buffer").ThrowAsJavaScriptException();
95
+ return env.Null();
96
+ }
97
+
98
+ std::vector<uint8_t> sendBuffer;
99
+
100
+ if (info[0].IsBuffer()) {
101
+ Napi::Buffer<uint8_t> buffer = info[0].As<Napi::Buffer<uint8_t>>();
102
+ sendBuffer.assign(buffer.Data(), buffer.Data() + buffer.Length());
103
+ } else if (info[0].IsArray()) {
104
+ Napi::Array arr = info[0].As<Napi::Array>();
105
+ sendBuffer.reserve(arr.Length());
106
+ for (uint32_t i = 0; i < arr.Length(); i++) {
107
+ sendBuffer.push_back(static_cast<uint8_t>(arr.Get(i).As<Napi::Number>().Uint32Value()));
108
+ }
109
+ } else {
110
+ Napi::TypeError::New(env, "Expected Buffer or Array").ThrowAsJavaScriptException();
111
+ return env.Null();
112
+ }
113
+
114
+ // Create promise for async transmit
115
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
116
+
117
+ TransmitWorker* worker = new TransmitWorker(
118
+ env, card_, protocol_, sendBuffer, deferred);
119
+ worker->Queue();
120
+
121
+ return deferred.Promise();
122
+ }
123
+
124
+ Napi::Value PCSCCard::Control(const Napi::CallbackInfo& info) {
125
+ Napi::Env env = info.Env();
126
+
127
+ if (!connected_) {
128
+ Napi::Error::New(env, "Card is not connected").ThrowAsJavaScriptException();
129
+ return env.Null();
130
+ }
131
+
132
+ if (info.Length() < 1 || !info[0].IsNumber()) {
133
+ Napi::TypeError::New(env, "Expected control code").ThrowAsJavaScriptException();
134
+ return env.Null();
135
+ }
136
+
137
+ DWORD controlCode = info[0].As<Napi::Number>().Uint32Value();
138
+
139
+ std::vector<uint8_t> sendBuffer;
140
+ if (info.Length() > 1) {
141
+ if (info[1].IsBuffer()) {
142
+ Napi::Buffer<uint8_t> buffer = info[1].As<Napi::Buffer<uint8_t>>();
143
+ sendBuffer.assign(buffer.Data(), buffer.Data() + buffer.Length());
144
+ } else if (info[1].IsArray()) {
145
+ Napi::Array arr = info[1].As<Napi::Array>();
146
+ sendBuffer.reserve(arr.Length());
147
+ for (uint32_t i = 0; i < arr.Length(); i++) {
148
+ sendBuffer.push_back(static_cast<uint8_t>(arr.Get(i).As<Napi::Number>().Uint32Value()));
149
+ }
150
+ }
151
+ }
152
+
153
+ // Create promise for async control
154
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
155
+
156
+ ControlWorker* worker = new ControlWorker(
157
+ env, card_, controlCode, sendBuffer, deferred);
158
+ worker->Queue();
159
+
160
+ return deferred.Promise();
161
+ }
162
+
163
+ Napi::Value PCSCCard::GetStatus(const Napi::CallbackInfo& info) {
164
+ Napi::Env env = info.Env();
165
+
166
+ if (!connected_) {
167
+ Napi::Error::New(env, "Card is not connected").ThrowAsJavaScriptException();
168
+ return env.Null();
169
+ }
170
+
171
+ DWORD readerLen = 0;
172
+ DWORD state = 0;
173
+ DWORD protocol = 0;
174
+ BYTE atr[MAX_ATR_SIZE];
175
+ DWORD atrLen = sizeof(atr);
176
+
177
+ // First call to get reader name length
178
+ LONG result = SCardStatus(card_, nullptr, &readerLen, &state, &protocol, atr, &atrLen);
179
+
180
+ if (result != SCARD_S_SUCCESS) {
181
+ Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
182
+ return env.Null();
183
+ }
184
+
185
+ Napi::Object status = Napi::Object::New(env);
186
+ status.Set("state", Napi::Number::New(env, state));
187
+ status.Set("protocol", Napi::Number::New(env, protocol));
188
+ status.Set("atr", Napi::Buffer<uint8_t>::Copy(env, atr, atrLen));
189
+
190
+ return status;
191
+ }
192
+
193
+ Napi::Value PCSCCard::Disconnect(const Napi::CallbackInfo& info) {
194
+ Napi::Env env = info.Env();
195
+
196
+ if (!connected_) {
197
+ return env.Undefined();
198
+ }
199
+
200
+ DWORD disposition = SCARD_LEAVE_CARD;
201
+ if (info.Length() > 0 && info[0].IsNumber()) {
202
+ disposition = info[0].As<Napi::Number>().Uint32Value();
203
+ }
204
+
205
+ LONG result = SCardDisconnect(card_, disposition);
206
+ connected_ = false;
207
+ card_ = 0;
208
+
209
+ if (result != SCARD_S_SUCCESS) {
210
+ Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
211
+ }
212
+
213
+ return env.Undefined();
214
+ }
215
+
216
+ Napi::Value PCSCCard::Reconnect(const Napi::CallbackInfo& info) {
217
+ Napi::Env env = info.Env();
218
+
219
+ if (!connected_) {
220
+ Napi::Error::New(env, "Card is not connected").ThrowAsJavaScriptException();
221
+ return env.Null();
222
+ }
223
+
224
+ DWORD shareMode = SCARD_SHARE_SHARED;
225
+ DWORD preferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
226
+ DWORD initialization = SCARD_LEAVE_CARD;
227
+
228
+ if (info.Length() > 0 && info[0].IsNumber()) {
229
+ shareMode = info[0].As<Napi::Number>().Uint32Value();
230
+ }
231
+ if (info.Length() > 1 && info[1].IsNumber()) {
232
+ preferredProtocols = info[1].As<Napi::Number>().Uint32Value();
233
+ }
234
+ if (info.Length() > 2 && info[2].IsNumber()) {
235
+ initialization = info[2].As<Napi::Number>().Uint32Value();
236
+ }
237
+
238
+ DWORD activeProtocol = 0;
239
+ LONG result = SCardReconnect(card_, shareMode, preferredProtocols, initialization, &activeProtocol);
240
+
241
+ if (result != SCARD_S_SUCCESS) {
242
+ Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
243
+ return env.Null();
244
+ }
245
+
246
+ protocol_ = activeProtocol;
247
+ return Napi::Number::New(env, activeProtocol);
248
+ }
@@ -0,0 +1,41 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <string>
5
+ #include <vector>
6
+ #include "platform/pcsc.h"
7
+
8
+ class PCSCCard : public Napi::ObjectWrap<PCSCCard> {
9
+ public:
10
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
11
+ static Napi::Object NewInstance(Napi::Env env, SCARDHANDLE card,
12
+ DWORD protocol, const std::string& readerName);
13
+
14
+ PCSCCard(const Napi::CallbackInfo& info);
15
+ ~PCSCCard();
16
+
17
+ // Accessors
18
+ SCARDHANDLE GetHandle() const { return card_; }
19
+ DWORD GetProtocol() const { return protocol_; }
20
+ bool IsConnected() const { return connected_; }
21
+
22
+ private:
23
+ static Napi::FunctionReference constructor;
24
+
25
+ SCARDHANDLE card_;
26
+ DWORD protocol_;
27
+ std::string readerName_;
28
+ bool connected_;
29
+
30
+ // JavaScript-exposed methods
31
+ Napi::Value Transmit(const Napi::CallbackInfo& info);
32
+ Napi::Value Control(const Napi::CallbackInfo& info);
33
+ Napi::Value GetStatus(const Napi::CallbackInfo& info);
34
+ Napi::Value Disconnect(const Napi::CallbackInfo& info);
35
+ Napi::Value Reconnect(const Napi::CallbackInfo& info);
36
+
37
+ // Getters
38
+ Napi::Value GetProtocolValue(const Napi::CallbackInfo& info);
39
+ Napi::Value GetConnectedValue(const Napi::CallbackInfo& info);
40
+ Napi::Value GetAtr(const Napi::CallbackInfo& info);
41
+ };
@@ -0,0 +1,224 @@
1
+ #include "pcsc_context.h"
2
+ #include "pcsc_reader.h"
3
+ #include "pcsc_errors.h"
4
+ #include "async_workers.h"
5
+ #include <cstring>
6
+
7
+ Napi::FunctionReference PCSCContext::constructor;
8
+
9
+ Napi::Object PCSCContext::Init(Napi::Env env, Napi::Object exports) {
10
+ Napi::Function func = DefineClass(env, "Context", {
11
+ InstanceMethod("listReaders", &PCSCContext::ListReaders),
12
+ InstanceMethod("waitForChange", &PCSCContext::WaitForChange),
13
+ InstanceMethod("cancel", &PCSCContext::Cancel),
14
+ InstanceMethod("close", &PCSCContext::Close),
15
+ InstanceAccessor("isValid", &PCSCContext::GetIsValid, nullptr),
16
+ });
17
+
18
+ constructor = Napi::Persistent(func);
19
+ constructor.SuppressDestruct();
20
+
21
+ exports.Set("Context", func);
22
+ return exports;
23
+ }
24
+
25
+ Napi::Object PCSCContext::NewInstance(Napi::Env env) {
26
+ return constructor.New({});
27
+ }
28
+
29
+ PCSCContext::PCSCContext(const Napi::CallbackInfo& info)
30
+ : Napi::ObjectWrap<PCSCContext>(info), context_(0), valid_(false) {
31
+
32
+ Napi::Env env = info.Env();
33
+
34
+ // Establish PC/SC context
35
+ LONG result = SCardEstablishContext(SCARD_SCOPE_SYSTEM, nullptr, nullptr, &context_);
36
+
37
+ if (result != SCARD_S_SUCCESS) {
38
+ Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
39
+ return;
40
+ }
41
+
42
+ valid_ = true;
43
+ }
44
+
45
+ PCSCContext::~PCSCContext() {
46
+ if (valid_ && context_ != 0) {
47
+ SCardReleaseContext(context_);
48
+ valid_ = false;
49
+ context_ = 0;
50
+ }
51
+ }
52
+
53
+ Napi::Value PCSCContext::ListReaders(const Napi::CallbackInfo& info) {
54
+ Napi::Env env = info.Env();
55
+
56
+ if (!valid_) {
57
+ Napi::Error::New(env, "Context is not valid").ThrowAsJavaScriptException();
58
+ return env.Null();
59
+ }
60
+
61
+ // First call to get buffer size
62
+ DWORD readersLen = 0;
63
+ LONG result = SCardListReaders(context_, nullptr, nullptr, &readersLen);
64
+
65
+ if (result == SCARD_E_NO_READERS_AVAILABLE) {
66
+ // No readers - return empty array
67
+ return Napi::Array::New(env, 0);
68
+ }
69
+
70
+ if (result != SCARD_S_SUCCESS) {
71
+ Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
72
+ return env.Null();
73
+ }
74
+
75
+ // Allocate buffer and get reader names
76
+ std::vector<char> readersBuffer(readersLen);
77
+ result = SCardListReaders(context_, nullptr, readersBuffer.data(), &readersLen);
78
+
79
+ if (result != SCARD_S_SUCCESS) {
80
+ Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
81
+ return env.Null();
82
+ }
83
+
84
+ // Parse multi-string result (null-separated, double-null terminated)
85
+ std::vector<std::string> readerNames;
86
+ const char* p = readersBuffer.data();
87
+ while (*p != '\0') {
88
+ readerNames.push_back(std::string(p));
89
+ p += strlen(p) + 1;
90
+ }
91
+
92
+ // Get initial state for each reader
93
+ std::vector<SCARD_READERSTATE> states(readerNames.size());
94
+ for (size_t i = 0; i < readerNames.size(); i++) {
95
+ states[i].szReader = readerNames[i].c_str();
96
+ states[i].dwCurrentState = SCARD_STATE_UNAWARE;
97
+ }
98
+
99
+ // Get current state (non-blocking with 0 timeout)
100
+ result = SCardGetStatusChange(context_, 0, states.data(), states.size());
101
+
102
+ // Create Reader objects
103
+ Napi::Array readers = Napi::Array::New(env, readerNames.size());
104
+ for (size_t i = 0; i < readerNames.size(); i++) {
105
+ std::vector<uint8_t> atr;
106
+ if (result == SCARD_S_SUCCESS && states[i].cbAtr > 0) {
107
+ atr.assign(states[i].rgbAtr, states[i].rgbAtr + states[i].cbAtr);
108
+ }
109
+ DWORD state = (result == SCARD_S_SUCCESS) ? states[i].dwEventState : 0;
110
+
111
+ Napi::Object reader = PCSCReader::NewInstance(env, context_, readerNames[i], state, atr);
112
+ readers.Set(static_cast<uint32_t>(i), reader);
113
+ }
114
+
115
+ return readers;
116
+ }
117
+
118
+ Napi::Value PCSCContext::WaitForChange(const Napi::CallbackInfo& info) {
119
+ Napi::Env env = info.Env();
120
+
121
+ if (!valid_) {
122
+ Napi::Error::New(env, "Context is not valid").ThrowAsJavaScriptException();
123
+ return env.Null();
124
+ }
125
+
126
+ // Parse arguments: readers array (optional), timeout (optional)
127
+ std::vector<std::string> readerNames;
128
+ std::vector<DWORD> currentStates;
129
+ DWORD timeout = INFINITE;
130
+
131
+ size_t argIndex = 0;
132
+
133
+ // First argument can be array of readers or timeout
134
+ if (info.Length() > 0 && info[0].IsArray()) {
135
+ Napi::Array readersArray = info[0].As<Napi::Array>();
136
+ for (uint32_t i = 0; i < readersArray.Length(); i++) {
137
+ Napi::Value item = readersArray.Get(i);
138
+ if (item.IsObject()) {
139
+ // It's a Reader object
140
+ Napi::Object obj = item.As<Napi::Object>();
141
+ if (obj.Has("name")) {
142
+ readerNames.push_back(obj.Get("name").As<Napi::String>().Utf8Value());
143
+ if (obj.Has("state")) {
144
+ currentStates.push_back(obj.Get("state").As<Napi::Number>().Uint32Value());
145
+ } else {
146
+ currentStates.push_back(SCARD_STATE_UNAWARE);
147
+ }
148
+ }
149
+ } else if (item.IsString()) {
150
+ readerNames.push_back(item.As<Napi::String>().Utf8Value());
151
+ currentStates.push_back(SCARD_STATE_UNAWARE);
152
+ }
153
+ }
154
+ argIndex++;
155
+ }
156
+
157
+ // Next argument is timeout
158
+ if (info.Length() > argIndex && info[argIndex].IsNumber()) {
159
+ timeout = info[argIndex].As<Napi::Number>().Uint32Value();
160
+ }
161
+
162
+ // If no readers specified, get all readers
163
+ if (readerNames.empty()) {
164
+ DWORD readersLen = 0;
165
+ LONG result = SCardListReaders(context_, nullptr, nullptr, &readersLen);
166
+
167
+ if (result == SCARD_E_NO_READERS_AVAILABLE) {
168
+ // Add PnP notification to detect when readers are attached
169
+ readerNames.push_back("\\\\?PnP?\\Notification");
170
+ currentStates.push_back(SCARD_STATE_UNAWARE);
171
+ } else if (result == SCARD_S_SUCCESS) {
172
+ std::vector<char> readersBuffer(readersLen);
173
+ result = SCardListReaders(context_, nullptr, readersBuffer.data(), &readersLen);
174
+ if (result == SCARD_S_SUCCESS) {
175
+ const char* p = readersBuffer.data();
176
+ while (*p != '\0') {
177
+ readerNames.push_back(std::string(p));
178
+ currentStates.push_back(SCARD_STATE_UNAWARE);
179
+ p += strlen(p) + 1;
180
+ }
181
+ }
182
+ }
183
+ }
184
+
185
+ // Create promise and start async worker
186
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
187
+
188
+ WaitForChangeWorker* worker = new WaitForChangeWorker(
189
+ env, context_, readerNames, currentStates, timeout, deferred);
190
+ worker->Queue();
191
+
192
+ return deferred.Promise();
193
+ }
194
+
195
+ Napi::Value PCSCContext::Cancel(const Napi::CallbackInfo& info) {
196
+ Napi::Env env = info.Env();
197
+
198
+ if (!valid_) {
199
+ return env.Undefined();
200
+ }
201
+
202
+ LONG result = SCardCancel(context_);
203
+ if (result != SCARD_S_SUCCESS && result != SCARD_E_INVALID_HANDLE) {
204
+ Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
205
+ }
206
+
207
+ return env.Undefined();
208
+ }
209
+
210
+ Napi::Value PCSCContext::Close(const Napi::CallbackInfo& info) {
211
+ Napi::Env env = info.Env();
212
+
213
+ if (valid_ && context_ != 0) {
214
+ SCardReleaseContext(context_);
215
+ valid_ = false;
216
+ context_ = 0;
217
+ }
218
+
219
+ return env.Undefined();
220
+ }
221
+
222
+ Napi::Value PCSCContext::GetIsValid(const Napi::CallbackInfo& info) {
223
+ return Napi::Boolean::New(info.Env(), valid_);
224
+ }
@@ -0,0 +1,32 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <string>
5
+ #include <vector>
6
+ #include "platform/pcsc.h"
7
+
8
+ class PCSCContext : public Napi::ObjectWrap<PCSCContext> {
9
+ public:
10
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
11
+ static Napi::Object NewInstance(Napi::Env env);
12
+
13
+ PCSCContext(const Napi::CallbackInfo& info);
14
+ ~PCSCContext();
15
+
16
+ // Get the raw context handle (for use by other classes)
17
+ SCARDCONTEXT GetContext() const { return context_; }
18
+ bool IsValid() const { return valid_; }
19
+
20
+ private:
21
+ static Napi::FunctionReference constructor;
22
+
23
+ SCARDCONTEXT context_;
24
+ bool valid_;
25
+
26
+ // JavaScript-exposed methods
27
+ Napi::Value ListReaders(const Napi::CallbackInfo& info);
28
+ Napi::Value WaitForChange(const Napi::CallbackInfo& info);
29
+ Napi::Value Cancel(const Napi::CallbackInfo& info);
30
+ Napi::Value Close(const Napi::CallbackInfo& info);
31
+ Napi::Value GetIsValid(const Napi::CallbackInfo& info);
32
+ };
@@ -0,0 +1,49 @@
1
+ #pragma once
2
+
3
+ #include "platform/pcsc.h"
4
+
5
+ // Convert PC/SC error codes to human-readable strings
6
+ // Using explicit casts to handle unsigned->signed on macOS
7
+ inline const char* GetPCSCErrorString(LONG code) {
8
+ // Cast to unsigned for comparison to avoid sign issues
9
+ DWORD ucode = static_cast<DWORD>(code);
10
+
11
+ if (ucode == SCARD_S_SUCCESS) return "Success";
12
+ if (ucode == SCARD_E_CANCELLED) return "Operation cancelled";
13
+ if (ucode == SCARD_E_CANT_DISPOSE) return "Cannot dispose handle";
14
+ if (ucode == SCARD_E_INSUFFICIENT_BUFFER) return "Insufficient buffer";
15
+ if (ucode == SCARD_E_INVALID_ATR) return "Invalid ATR";
16
+ if (ucode == SCARD_E_INVALID_HANDLE) return "Invalid handle";
17
+ if (ucode == SCARD_E_INVALID_PARAMETER) return "Invalid parameter";
18
+ if (ucode == SCARD_E_INVALID_TARGET) return "Invalid target";
19
+ if (ucode == SCARD_E_INVALID_VALUE) return "Invalid value";
20
+ if (ucode == SCARD_E_NO_MEMORY) return "Not enough memory";
21
+ if (ucode == SCARD_E_NO_SERVICE) return "PC/SC service not running";
22
+ if (ucode == SCARD_E_NO_SMARTCARD) return "No smart card present";
23
+ if (ucode == SCARD_E_NOT_READY) return "Reader not ready";
24
+ if (ucode == SCARD_E_NOT_TRANSACTED) return "Transaction failed";
25
+ if (ucode == SCARD_E_PCI_TOO_SMALL) return "PCI struct too small";
26
+ if (ucode == SCARD_E_PROTO_MISMATCH) return "Protocol mismatch";
27
+ if (ucode == SCARD_E_READER_UNAVAILABLE) return "Reader unavailable";
28
+ if (ucode == SCARD_E_SERVICE_STOPPED) return "PC/SC service stopped";
29
+ if (ucode == SCARD_E_SHARING_VIOLATION) return "Sharing violation";
30
+ if (ucode == SCARD_E_SYSTEM_CANCELLED) return "System cancelled operation";
31
+ if (ucode == SCARD_E_TIMEOUT) return "Operation timed out";
32
+ if (ucode == SCARD_E_UNKNOWN_CARD) return "Unknown card type";
33
+ if (ucode == SCARD_E_UNKNOWN_READER) return "Unknown reader";
34
+ if (ucode == SCARD_E_NO_READERS_AVAILABLE) return "No readers available";
35
+ if (ucode == SCARD_F_COMM_ERROR) return "Communication error";
36
+ if (ucode == SCARD_F_INTERNAL_ERROR) return "Internal error";
37
+ if (ucode == SCARD_W_REMOVED_CARD) return "Card was removed";
38
+ if (ucode == SCARD_W_RESET_CARD) return "Card was reset";
39
+ if (ucode == SCARD_W_UNPOWERED_CARD) return "Card is unpowered";
40
+ if (ucode == SCARD_W_UNRESPONSIVE_CARD) return "Card is unresponsive";
41
+ if (ucode == SCARD_W_UNSUPPORTED_CARD) return "Card is not supported";
42
+
43
+ return "Unknown PC/SC error";
44
+ }
45
+
46
+ // Get the error code value
47
+ inline LONG GetPCSCErrorCode(LONG code) {
48
+ return code;
49
+ }
@@ -0,0 +1,89 @@
1
+ #include "pcsc_reader.h"
2
+ #include "pcsc_card.h"
3
+ #include "pcsc_errors.h"
4
+ #include "async_workers.h"
5
+
6
+ Napi::FunctionReference PCSCReader::constructor;
7
+
8
+ Napi::Object PCSCReader::Init(Napi::Env env, Napi::Object exports) {
9
+ Napi::Function func = DefineClass(env, "Reader", {
10
+ InstanceAccessor("name", &PCSCReader::GetNameValue, nullptr),
11
+ InstanceAccessor("state", &PCSCReader::GetStateValue, nullptr),
12
+ InstanceAccessor("atr", &PCSCReader::GetAtrValue, nullptr),
13
+ InstanceMethod("connect", &PCSCReader::Connect),
14
+ });
15
+
16
+ constructor = Napi::Persistent(func);
17
+ constructor.SuppressDestruct();
18
+
19
+ exports.Set("Reader", func);
20
+ return exports;
21
+ }
22
+
23
+ Napi::Object PCSCReader::NewInstance(Napi::Env env, SCARDCONTEXT context,
24
+ const std::string& name, DWORD state,
25
+ const std::vector<uint8_t>& atr) {
26
+ Napi::Object obj = constructor.New({});
27
+ PCSCReader* reader = Napi::ObjectWrap<PCSCReader>::Unwrap(obj);
28
+ reader->context_ = context;
29
+ reader->name_ = name;
30
+ reader->state_ = state;
31
+ reader->atr_ = atr;
32
+ return obj;
33
+ }
34
+
35
+ PCSCReader::PCSCReader(const Napi::CallbackInfo& info)
36
+ : Napi::ObjectWrap<PCSCReader>(info),
37
+ context_(0),
38
+ state_(SCARD_STATE_UNAWARE) {
39
+ // Properties set via NewInstance
40
+ }
41
+
42
+ void PCSCReader::UpdateState(DWORD state, const std::vector<uint8_t>& atr) {
43
+ state_ = state;
44
+ atr_ = atr;
45
+ }
46
+
47
+ Napi::Value PCSCReader::GetNameValue(const Napi::CallbackInfo& info) {
48
+ return Napi::String::New(info.Env(), name_);
49
+ }
50
+
51
+ Napi::Value PCSCReader::GetStateValue(const Napi::CallbackInfo& info) {
52
+ return Napi::Number::New(info.Env(), state_);
53
+ }
54
+
55
+ Napi::Value PCSCReader::GetAtrValue(const Napi::CallbackInfo& info) {
56
+ Napi::Env env = info.Env();
57
+
58
+ if (atr_.empty()) {
59
+ return env.Null();
60
+ }
61
+
62
+ Napi::Buffer<uint8_t> buffer = Napi::Buffer<uint8_t>::Copy(env, atr_.data(), atr_.size());
63
+ return buffer;
64
+ }
65
+
66
+ Napi::Value PCSCReader::Connect(const Napi::CallbackInfo& info) {
67
+ Napi::Env env = info.Env();
68
+
69
+ // Parse arguments: shareMode (optional), preferredProtocols (optional)
70
+ DWORD shareMode = SCARD_SHARE_SHARED;
71
+ DWORD preferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
72
+
73
+ if (info.Length() > 0 && info[0].IsNumber()) {
74
+ shareMode = info[0].As<Napi::Number>().Uint32Value();
75
+ }
76
+
77
+ if (info.Length() > 1 && info[1].IsNumber()) {
78
+ preferredProtocols = info[1].As<Napi::Number>().Uint32Value();
79
+ }
80
+
81
+ // Create promise for async connection
82
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
83
+
84
+ ConnectWorker* worker = new ConnectWorker(
85
+ env, context_, name_, shareMode, preferredProtocols, deferred);
86
+ worker->Queue();
87
+
88
+ return deferred.Promise();
89
+ }