smartcard 2.0.3 → 3.1.0
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.
- package/README.md +2 -2
- package/lib/control-codes.js +107 -0
- package/lib/errors.js +27 -0
- package/lib/index.d.ts +82 -4
- package/lib/index.js +67 -1
- package/package.json +4 -2
- package/src/async_workers.cpp +56 -2
- package/src/async_workers.h +27 -0
- package/src/pcsc_card.cpp +16 -9
package/README.md
CHANGED
|
@@ -289,11 +289,11 @@ interface Card {
|
|
|
289
289
|
readonly protocol: number;
|
|
290
290
|
readonly connected: boolean;
|
|
291
291
|
readonly atr: Buffer | null;
|
|
292
|
-
transmit(command: Buffer | number[]): Promise<Buffer>;
|
|
292
|
+
transmit(command: Buffer | number[], options?: { maxRecvLength?: number }): Promise<Buffer>;
|
|
293
293
|
control(code: number, data?: Buffer): Promise<Buffer>;
|
|
294
294
|
getStatus(): { state: number; protocol: number; atr: Buffer };
|
|
295
295
|
disconnect(disposition?: number): void;
|
|
296
|
-
reconnect(shareMode?: number, protocol?: number, init?: number): number
|
|
296
|
+
reconnect(shareMode?: number, protocol?: number, init?: number): Promise<number>;
|
|
297
297
|
}
|
|
298
298
|
```
|
|
299
299
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Control codes and feature constants for PC/SC smart card operations
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generate a control code (platform-specific)
|
|
10
|
+
* @param {number} code - The control code number
|
|
11
|
+
* @returns {number} The platform-specific control code
|
|
12
|
+
*/
|
|
13
|
+
function SCARD_CTL_CODE(code) {
|
|
14
|
+
if (process.platform === 'win32') {
|
|
15
|
+
// Windows: (FILE_DEVICE_SMARTCARD << 16) + (code << 2)
|
|
16
|
+
// FILE_DEVICE_SMARTCARD = 0x31
|
|
17
|
+
return (0x31 << 16) + (code << 2);
|
|
18
|
+
} else {
|
|
19
|
+
// macOS/Linux: 0x42000000 + code
|
|
20
|
+
return 0x42000000 + code;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Common control codes
|
|
25
|
+
const CM_IOCTL_GET_FEATURE_REQUEST = SCARD_CTL_CODE(3400);
|
|
26
|
+
|
|
27
|
+
// CCID Feature tags (from CCID spec)
|
|
28
|
+
const FEATURE_VERIFY_PIN_START = 0x01;
|
|
29
|
+
const FEATURE_VERIFY_PIN_FINISH = 0x02;
|
|
30
|
+
const FEATURE_MODIFY_PIN_START = 0x03;
|
|
31
|
+
const FEATURE_MODIFY_PIN_FINISH = 0x04;
|
|
32
|
+
const FEATURE_GET_KEY_PRESSED = 0x05;
|
|
33
|
+
const FEATURE_VERIFY_PIN_DIRECT = 0x06;
|
|
34
|
+
const FEATURE_MODIFY_PIN_DIRECT = 0x07;
|
|
35
|
+
const FEATURE_MCT_READER_DIRECT = 0x08;
|
|
36
|
+
const FEATURE_MCT_UNIVERSAL = 0x09;
|
|
37
|
+
const FEATURE_IFD_PIN_PROPERTIES = 0x0A;
|
|
38
|
+
const FEATURE_ABORT = 0x0B;
|
|
39
|
+
const FEATURE_SET_SPE_MESSAGE = 0x0C;
|
|
40
|
+
const FEATURE_VERIFY_PIN_DIRECT_APP_ID = 0x0D;
|
|
41
|
+
const FEATURE_MODIFY_PIN_DIRECT_APP_ID = 0x0E;
|
|
42
|
+
const FEATURE_WRITE_DISPLAY = 0x0F;
|
|
43
|
+
const FEATURE_GET_KEY = 0x10;
|
|
44
|
+
const FEATURE_IFD_DISPLAY_PROPERTIES = 0x11;
|
|
45
|
+
const FEATURE_GET_TLV_PROPERTIES = 0x12;
|
|
46
|
+
const FEATURE_CCID_ESC_COMMAND = 0x13;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parse feature TLV response from CM_IOCTL_GET_FEATURE_REQUEST
|
|
50
|
+
* @param {Buffer} response - The TLV response buffer
|
|
51
|
+
* @returns {Map<number, number>} Map of feature tag to control code
|
|
52
|
+
*/
|
|
53
|
+
function parseFeatures(response) {
|
|
54
|
+
const features = new Map();
|
|
55
|
+
let offset = 0;
|
|
56
|
+
|
|
57
|
+
while (offset + 4 <= response.length) {
|
|
58
|
+
const tag = response[offset];
|
|
59
|
+
const length = response[offset + 1];
|
|
60
|
+
|
|
61
|
+
if (length === 4 && offset + 2 + length <= response.length) {
|
|
62
|
+
// Big-endian control code
|
|
63
|
+
const controlCode =
|
|
64
|
+
(response[offset + 2] << 24) |
|
|
65
|
+
(response[offset + 3] << 16) |
|
|
66
|
+
(response[offset + 4] << 8) |
|
|
67
|
+
response[offset + 5];
|
|
68
|
+
features.set(tag, controlCode);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
offset += 2 + length;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return features;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
// Control code generator
|
|
79
|
+
SCARD_CTL_CODE,
|
|
80
|
+
|
|
81
|
+
// Common control codes
|
|
82
|
+
CM_IOCTL_GET_FEATURE_REQUEST,
|
|
83
|
+
|
|
84
|
+
// CCID Feature tags
|
|
85
|
+
FEATURE_VERIFY_PIN_START,
|
|
86
|
+
FEATURE_VERIFY_PIN_FINISH,
|
|
87
|
+
FEATURE_MODIFY_PIN_START,
|
|
88
|
+
FEATURE_MODIFY_PIN_FINISH,
|
|
89
|
+
FEATURE_GET_KEY_PRESSED,
|
|
90
|
+
FEATURE_VERIFY_PIN_DIRECT,
|
|
91
|
+
FEATURE_MODIFY_PIN_DIRECT,
|
|
92
|
+
FEATURE_MCT_READER_DIRECT,
|
|
93
|
+
FEATURE_MCT_UNIVERSAL,
|
|
94
|
+
FEATURE_IFD_PIN_PROPERTIES,
|
|
95
|
+
FEATURE_ABORT,
|
|
96
|
+
FEATURE_SET_SPE_MESSAGE,
|
|
97
|
+
FEATURE_VERIFY_PIN_DIRECT_APP_ID,
|
|
98
|
+
FEATURE_MODIFY_PIN_DIRECT_APP_ID,
|
|
99
|
+
FEATURE_WRITE_DISPLAY,
|
|
100
|
+
FEATURE_GET_KEY,
|
|
101
|
+
FEATURE_IFD_DISPLAY_PROPERTIES,
|
|
102
|
+
FEATURE_GET_TLV_PROPERTIES,
|
|
103
|
+
FEATURE_CCID_ESC_COMMAND,
|
|
104
|
+
|
|
105
|
+
// Helper functions
|
|
106
|
+
parseFeatures,
|
|
107
|
+
};
|
package/lib/errors.js
CHANGED
|
@@ -67,6 +67,32 @@ class SharingViolationError extends PCSCError {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* PC/SC error codes mapped to specific error classes
|
|
72
|
+
* @type {Map<number, typeof PCSCError>}
|
|
73
|
+
*/
|
|
74
|
+
const ERROR_CODE_MAP = new Map([
|
|
75
|
+
[0x80100069, CardRemovedError], // SCARD_W_REMOVED_CARD
|
|
76
|
+
[0x8010000A, TimeoutError], // SCARD_E_TIMEOUT
|
|
77
|
+
[0x8010002E, NoReadersError], // SCARD_E_NO_READERS_AVAILABLE
|
|
78
|
+
[0x8010001D, ServiceNotRunningError], // SCARD_E_NO_SERVICE
|
|
79
|
+
[0x8010000B, SharingViolationError], // SCARD_E_SHARING_VIOLATION
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Factory function to create the appropriate error class based on PC/SC error code
|
|
84
|
+
* @param {string} message - Error message
|
|
85
|
+
* @param {number} code - PC/SC error code
|
|
86
|
+
* @returns {PCSCError} The appropriate error instance
|
|
87
|
+
*/
|
|
88
|
+
function createPCSCError(message, code) {
|
|
89
|
+
const ErrorClass = ERROR_CODE_MAP.get(code);
|
|
90
|
+
if (ErrorClass) {
|
|
91
|
+
return new ErrorClass(message);
|
|
92
|
+
}
|
|
93
|
+
return new PCSCError(message, code);
|
|
94
|
+
}
|
|
95
|
+
|
|
70
96
|
module.exports = {
|
|
71
97
|
PCSCError,
|
|
72
98
|
CardRemovedError,
|
|
@@ -74,4 +100,5 @@ module.exports = {
|
|
|
74
100
|
NoReadersError,
|
|
75
101
|
ServiceNotRunningError,
|
|
76
102
|
SharingViolationError,
|
|
103
|
+
createPCSCError,
|
|
77
104
|
};
|
package/lib/index.d.ts
CHANGED
|
@@ -21,6 +21,18 @@ export interface CardStatus {
|
|
|
21
21
|
atr: Buffer;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Options for card.transmit()
|
|
26
|
+
*/
|
|
27
|
+
export interface TransmitOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Maximum receive buffer size in bytes.
|
|
30
|
+
* Default: 258 (standard APDU: 256 data + 2 status bytes)
|
|
31
|
+
* Maximum: 262144 (256KB for extended APDUs)
|
|
32
|
+
*/
|
|
33
|
+
maxRecvLength?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
/**
|
|
25
37
|
* Represents a connected smart card
|
|
26
38
|
*/
|
|
@@ -35,9 +47,10 @@ export interface Card {
|
|
|
35
47
|
/**
|
|
36
48
|
* Transmit an APDU command to the card
|
|
37
49
|
* @param command - The command buffer or byte array
|
|
50
|
+
* @param options - Optional transmit options
|
|
38
51
|
* @returns Promise resolving to the response buffer
|
|
39
52
|
*/
|
|
40
|
-
transmit(command: Buffer | number[]): Promise<Buffer>;
|
|
53
|
+
transmit(command: Buffer | number[], options?: TransmitOptions): Promise<Buffer>;
|
|
41
54
|
|
|
42
55
|
/**
|
|
43
56
|
* Send a control command to the reader
|
|
@@ -60,13 +73,13 @@ export interface Card {
|
|
|
60
73
|
disconnect(disposition?: number): void;
|
|
61
74
|
|
|
62
75
|
/**
|
|
63
|
-
* Reconnect to the card
|
|
76
|
+
* Reconnect to the card (async)
|
|
64
77
|
* @param shareMode - Share mode
|
|
65
78
|
* @param protocol - Preferred protocol(s)
|
|
66
79
|
* @param initialization - Initialization action
|
|
67
|
-
* @returns
|
|
80
|
+
* @returns Promise resolving to the new active protocol
|
|
68
81
|
*/
|
|
69
|
-
reconnect(shareMode?: number, protocol?: number, initialization?: number): number
|
|
82
|
+
reconnect(shareMode?: number, protocol?: number, initialization?: number): Promise<number>;
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
/**
|
|
@@ -216,6 +229,32 @@ export declare class TimeoutError extends PCSCError {
|
|
|
216
229
|
constructor(message?: string);
|
|
217
230
|
}
|
|
218
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Error thrown when no readers are available
|
|
234
|
+
*/
|
|
235
|
+
export declare class NoReadersError extends PCSCError {
|
|
236
|
+
constructor(message?: string);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Error thrown when PC/SC service is not running
|
|
241
|
+
*/
|
|
242
|
+
export declare class ServiceNotRunningError extends PCSCError {
|
|
243
|
+
constructor(message?: string);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Error thrown when there's a sharing violation
|
|
248
|
+
*/
|
|
249
|
+
export declare class SharingViolationError extends PCSCError {
|
|
250
|
+
constructor(message?: string);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Factory function to create the appropriate error class based on PC/SC error code
|
|
255
|
+
*/
|
|
256
|
+
export declare function createPCSCError(message: string, code: number): PCSCError;
|
|
257
|
+
|
|
219
258
|
// Share modes
|
|
220
259
|
export declare const SCARD_SHARE_EXCLUSIVE: number;
|
|
221
260
|
export declare const SCARD_SHARE_SHARED: number;
|
|
@@ -245,3 +284,42 @@ export declare const SCARD_STATE_ATRMATCH: number;
|
|
|
245
284
|
export declare const SCARD_STATE_EXCLUSIVE: number;
|
|
246
285
|
export declare const SCARD_STATE_INUSE: number;
|
|
247
286
|
export declare const SCARD_STATE_MUTE: number;
|
|
287
|
+
|
|
288
|
+
// Control code generator
|
|
289
|
+
/**
|
|
290
|
+
* Generate a platform-specific control code
|
|
291
|
+
* @param code - The control code number
|
|
292
|
+
* @returns The platform-specific control code
|
|
293
|
+
*/
|
|
294
|
+
export declare function SCARD_CTL_CODE(code: number): number;
|
|
295
|
+
|
|
296
|
+
// Common control codes
|
|
297
|
+
export declare const CM_IOCTL_GET_FEATURE_REQUEST: number;
|
|
298
|
+
|
|
299
|
+
// CCID Feature tags
|
|
300
|
+
export declare const FEATURE_VERIFY_PIN_START: number;
|
|
301
|
+
export declare const FEATURE_VERIFY_PIN_FINISH: number;
|
|
302
|
+
export declare const FEATURE_MODIFY_PIN_START: number;
|
|
303
|
+
export declare const FEATURE_MODIFY_PIN_FINISH: number;
|
|
304
|
+
export declare const FEATURE_GET_KEY_PRESSED: number;
|
|
305
|
+
export declare const FEATURE_VERIFY_PIN_DIRECT: number;
|
|
306
|
+
export declare const FEATURE_MODIFY_PIN_DIRECT: number;
|
|
307
|
+
export declare const FEATURE_MCT_READER_DIRECT: number;
|
|
308
|
+
export declare const FEATURE_MCT_UNIVERSAL: number;
|
|
309
|
+
export declare const FEATURE_IFD_PIN_PROPERTIES: number;
|
|
310
|
+
export declare const FEATURE_ABORT: number;
|
|
311
|
+
export declare const FEATURE_SET_SPE_MESSAGE: number;
|
|
312
|
+
export declare const FEATURE_VERIFY_PIN_DIRECT_APP_ID: number;
|
|
313
|
+
export declare const FEATURE_MODIFY_PIN_DIRECT_APP_ID: number;
|
|
314
|
+
export declare const FEATURE_WRITE_DISPLAY: number;
|
|
315
|
+
export declare const FEATURE_GET_KEY: number;
|
|
316
|
+
export declare const FEATURE_IFD_DISPLAY_PROPERTIES: number;
|
|
317
|
+
export declare const FEATURE_GET_TLV_PROPERTIES: number;
|
|
318
|
+
export declare const FEATURE_CCID_ESC_COMMAND: number;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Parse feature TLV response from CM_IOCTL_GET_FEATURE_REQUEST
|
|
322
|
+
* @param response - The TLV response buffer
|
|
323
|
+
* @returns Map of feature tag to control code
|
|
324
|
+
*/
|
|
325
|
+
export declare function parseFeatures(response: Buffer): Map<number, number>;
|
package/lib/index.js
CHANGED
|
@@ -38,7 +38,41 @@ const SCARD_STATE_MUTE = addon.SCARD_STATE_MUTE;
|
|
|
38
38
|
const { Devices } = require('./devices');
|
|
39
39
|
|
|
40
40
|
// Import error classes
|
|
41
|
-
const {
|
|
41
|
+
const {
|
|
42
|
+
PCSCError,
|
|
43
|
+
CardRemovedError,
|
|
44
|
+
TimeoutError,
|
|
45
|
+
NoReadersError,
|
|
46
|
+
ServiceNotRunningError,
|
|
47
|
+
SharingViolationError,
|
|
48
|
+
createPCSCError,
|
|
49
|
+
} = require('./errors');
|
|
50
|
+
|
|
51
|
+
// Import control codes
|
|
52
|
+
const {
|
|
53
|
+
SCARD_CTL_CODE,
|
|
54
|
+
CM_IOCTL_GET_FEATURE_REQUEST,
|
|
55
|
+
FEATURE_VERIFY_PIN_START,
|
|
56
|
+
FEATURE_VERIFY_PIN_FINISH,
|
|
57
|
+
FEATURE_MODIFY_PIN_START,
|
|
58
|
+
FEATURE_MODIFY_PIN_FINISH,
|
|
59
|
+
FEATURE_GET_KEY_PRESSED,
|
|
60
|
+
FEATURE_VERIFY_PIN_DIRECT,
|
|
61
|
+
FEATURE_MODIFY_PIN_DIRECT,
|
|
62
|
+
FEATURE_MCT_READER_DIRECT,
|
|
63
|
+
FEATURE_MCT_UNIVERSAL,
|
|
64
|
+
FEATURE_IFD_PIN_PROPERTIES,
|
|
65
|
+
FEATURE_ABORT,
|
|
66
|
+
FEATURE_SET_SPE_MESSAGE,
|
|
67
|
+
FEATURE_VERIFY_PIN_DIRECT_APP_ID,
|
|
68
|
+
FEATURE_MODIFY_PIN_DIRECT_APP_ID,
|
|
69
|
+
FEATURE_WRITE_DISPLAY,
|
|
70
|
+
FEATURE_GET_KEY,
|
|
71
|
+
FEATURE_IFD_DISPLAY_PROPERTIES,
|
|
72
|
+
FEATURE_GET_TLV_PROPERTIES,
|
|
73
|
+
FEATURE_CCID_ESC_COMMAND,
|
|
74
|
+
parseFeatures,
|
|
75
|
+
} = require('./control-codes');
|
|
42
76
|
|
|
43
77
|
module.exports = {
|
|
44
78
|
// Classes
|
|
@@ -52,6 +86,10 @@ module.exports = {
|
|
|
52
86
|
PCSCError,
|
|
53
87
|
CardRemovedError,
|
|
54
88
|
TimeoutError,
|
|
89
|
+
NoReadersError,
|
|
90
|
+
ServiceNotRunningError,
|
|
91
|
+
SharingViolationError,
|
|
92
|
+
createPCSCError,
|
|
55
93
|
|
|
56
94
|
// Share modes
|
|
57
95
|
SCARD_SHARE_EXCLUSIVE,
|
|
@@ -82,4 +120,32 @@ module.exports = {
|
|
|
82
120
|
SCARD_STATE_EXCLUSIVE,
|
|
83
121
|
SCARD_STATE_INUSE,
|
|
84
122
|
SCARD_STATE_MUTE,
|
|
123
|
+
|
|
124
|
+
// Control codes
|
|
125
|
+
SCARD_CTL_CODE,
|
|
126
|
+
CM_IOCTL_GET_FEATURE_REQUEST,
|
|
127
|
+
|
|
128
|
+
// CCID Feature tags
|
|
129
|
+
FEATURE_VERIFY_PIN_START,
|
|
130
|
+
FEATURE_VERIFY_PIN_FINISH,
|
|
131
|
+
FEATURE_MODIFY_PIN_START,
|
|
132
|
+
FEATURE_MODIFY_PIN_FINISH,
|
|
133
|
+
FEATURE_GET_KEY_PRESSED,
|
|
134
|
+
FEATURE_VERIFY_PIN_DIRECT,
|
|
135
|
+
FEATURE_MODIFY_PIN_DIRECT,
|
|
136
|
+
FEATURE_MCT_READER_DIRECT,
|
|
137
|
+
FEATURE_MCT_UNIVERSAL,
|
|
138
|
+
FEATURE_IFD_PIN_PROPERTIES,
|
|
139
|
+
FEATURE_ABORT,
|
|
140
|
+
FEATURE_SET_SPE_MESSAGE,
|
|
141
|
+
FEATURE_VERIFY_PIN_DIRECT_APP_ID,
|
|
142
|
+
FEATURE_MODIFY_PIN_DIRECT_APP_ID,
|
|
143
|
+
FEATURE_WRITE_DISPLAY,
|
|
144
|
+
FEATURE_GET_KEY,
|
|
145
|
+
FEATURE_IFD_DISPLAY_PROPERTIES,
|
|
146
|
+
FEATURE_GET_TLV_PROPERTIES,
|
|
147
|
+
FEATURE_CCID_ESC_COMMAND,
|
|
148
|
+
|
|
149
|
+
// Helper functions
|
|
150
|
+
parseFeatures,
|
|
85
151
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smartcard",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "PC/SC bindings for Node.js using N-API - ABI stable across Node.js versions",
|
|
5
5
|
"author": "Tom KP",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"install": "node-gyp rebuild",
|
|
20
20
|
"build": "node-gyp rebuild",
|
|
21
21
|
"rebuild": "node-gyp rebuild",
|
|
22
|
-
"test": "node test/test.js"
|
|
22
|
+
"test": "node test/test.js && node test/unit.js",
|
|
23
|
+
"test:unit": "node test/unit.js",
|
|
24
|
+
"test:integration": "node test/test.js"
|
|
23
25
|
},
|
|
24
26
|
"files": [
|
|
25
27
|
"lib/",
|
package/src/async_workers.cpp
CHANGED
|
@@ -84,6 +84,7 @@ TransmitWorker::TransmitWorker(
|
|
|
84
84
|
SCARDHANDLE card,
|
|
85
85
|
DWORD protocol,
|
|
86
86
|
std::vector<uint8_t> sendBuffer,
|
|
87
|
+
size_t maxRecvLength,
|
|
87
88
|
Napi::Promise::Deferred deferred)
|
|
88
89
|
: Napi::AsyncWorker(env),
|
|
89
90
|
card_(card),
|
|
@@ -92,8 +93,16 @@ TransmitWorker::TransmitWorker(
|
|
|
92
93
|
recvLength_(0),
|
|
93
94
|
result_(SCARD_S_SUCCESS),
|
|
94
95
|
deferred_(deferred) {
|
|
95
|
-
// Pre-allocate receive buffer
|
|
96
|
-
|
|
96
|
+
// Pre-allocate receive buffer with configurable size
|
|
97
|
+
// Default is 258 (standard APDU: 256 data + 2 status bytes)
|
|
98
|
+
// Max is 262144 (256KB) for extended APDUs
|
|
99
|
+
size_t bufferSize = maxRecvLength;
|
|
100
|
+
if (bufferSize == 0) {
|
|
101
|
+
bufferSize = 258; // Default
|
|
102
|
+
} else if (bufferSize > 262144) {
|
|
103
|
+
bufferSize = 262144; // Cap at 256KB
|
|
104
|
+
}
|
|
105
|
+
recvBuffer_.resize(bufferSize);
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
void TransmitWorker::Execute() {
|
|
@@ -232,3 +241,48 @@ void ConnectWorker::OnOK() {
|
|
|
232
241
|
void ConnectWorker::OnError(const Napi::Error& error) {
|
|
233
242
|
deferred_.Reject(error.Value());
|
|
234
243
|
}
|
|
244
|
+
|
|
245
|
+
// ============================================================================
|
|
246
|
+
// ReconnectWorker
|
|
247
|
+
// ============================================================================
|
|
248
|
+
|
|
249
|
+
ReconnectWorker::ReconnectWorker(
|
|
250
|
+
Napi::Env env,
|
|
251
|
+
SCARDHANDLE card,
|
|
252
|
+
DWORD shareMode,
|
|
253
|
+
DWORD preferredProtocols,
|
|
254
|
+
DWORD initialization,
|
|
255
|
+
Napi::Promise::Deferred deferred)
|
|
256
|
+
: Napi::AsyncWorker(env),
|
|
257
|
+
card_(card),
|
|
258
|
+
shareMode_(shareMode),
|
|
259
|
+
preferredProtocols_(preferredProtocols),
|
|
260
|
+
initialization_(initialization),
|
|
261
|
+
activeProtocol_(0),
|
|
262
|
+
result_(SCARD_S_SUCCESS),
|
|
263
|
+
deferred_(deferred) {
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
void ReconnectWorker::Execute() {
|
|
267
|
+
result_ = SCardReconnect(
|
|
268
|
+
card_,
|
|
269
|
+
shareMode_,
|
|
270
|
+
preferredProtocols_,
|
|
271
|
+
initialization_,
|
|
272
|
+
&activeProtocol_
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
void ReconnectWorker::OnOK() {
|
|
277
|
+
Napi::Env env = Env();
|
|
278
|
+
|
|
279
|
+
if (result_ == SCARD_S_SUCCESS) {
|
|
280
|
+
deferred_.Resolve(Napi::Number::New(env, activeProtocol_));
|
|
281
|
+
} else {
|
|
282
|
+
deferred_.Reject(Napi::Error::New(env, GetPCSCErrorString(result_)).Value());
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
void ReconnectWorker::OnError(const Napi::Error& error) {
|
|
287
|
+
deferred_.Reject(error.Value());
|
|
288
|
+
}
|
package/src/async_workers.h
CHANGED
|
@@ -35,6 +35,7 @@ public:
|
|
|
35
35
|
SCARDHANDLE card,
|
|
36
36
|
DWORD protocol,
|
|
37
37
|
std::vector<uint8_t> sendBuffer,
|
|
38
|
+
size_t maxRecvLength,
|
|
38
39
|
Napi::Promise::Deferred deferred);
|
|
39
40
|
|
|
40
41
|
void Execute() override;
|
|
@@ -98,3 +99,29 @@ private:
|
|
|
98
99
|
LONG result_;
|
|
99
100
|
Napi::Promise::Deferred deferred_;
|
|
100
101
|
};
|
|
102
|
+
|
|
103
|
+
// Async worker for SCardReconnect
|
|
104
|
+
class ReconnectWorker : public Napi::AsyncWorker {
|
|
105
|
+
public:
|
|
106
|
+
ReconnectWorker(Napi::Env env,
|
|
107
|
+
SCARDHANDLE card,
|
|
108
|
+
DWORD shareMode,
|
|
109
|
+
DWORD preferredProtocols,
|
|
110
|
+
DWORD initialization,
|
|
111
|
+
Napi::Promise::Deferred deferred);
|
|
112
|
+
|
|
113
|
+
void Execute() override;
|
|
114
|
+
void OnOK() override;
|
|
115
|
+
void OnError(const Napi::Error& error) override;
|
|
116
|
+
|
|
117
|
+
DWORD GetActiveProtocol() const { return activeProtocol_; }
|
|
118
|
+
|
|
119
|
+
private:
|
|
120
|
+
SCARDHANDLE card_;
|
|
121
|
+
DWORD shareMode_;
|
|
122
|
+
DWORD preferredProtocols_;
|
|
123
|
+
DWORD initialization_;
|
|
124
|
+
DWORD activeProtocol_;
|
|
125
|
+
LONG result_;
|
|
126
|
+
Napi::Promise::Deferred deferred_;
|
|
127
|
+
};
|
package/src/pcsc_card.cpp
CHANGED
|
@@ -111,11 +111,20 @@ Napi::Value PCSCCard::Transmit(const Napi::CallbackInfo& info) {
|
|
|
111
111
|
return env.Null();
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// Parse options (optional second argument)
|
|
115
|
+
size_t maxRecvLength = 0; // 0 means use default
|
|
116
|
+
if (info.Length() > 1 && info[1].IsObject()) {
|
|
117
|
+
Napi::Object options = info[1].As<Napi::Object>();
|
|
118
|
+
if (options.Has("maxRecvLength") && options.Get("maxRecvLength").IsNumber()) {
|
|
119
|
+
maxRecvLength = options.Get("maxRecvLength").As<Napi::Number>().Uint32Value();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
114
123
|
// Create promise for async transmit
|
|
115
124
|
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
|
|
116
125
|
|
|
117
126
|
TransmitWorker* worker = new TransmitWorker(
|
|
118
|
-
env, card_, protocol_, sendBuffer, deferred);
|
|
127
|
+
env, card_, protocol_, sendBuffer, maxRecvLength, deferred);
|
|
119
128
|
worker->Queue();
|
|
120
129
|
|
|
121
130
|
return deferred.Promise();
|
|
@@ -235,14 +244,12 @@ Napi::Value PCSCCard::Reconnect(const Napi::CallbackInfo& info) {
|
|
|
235
244
|
initialization = info[2].As<Napi::Number>().Uint32Value();
|
|
236
245
|
}
|
|
237
246
|
|
|
238
|
-
|
|
239
|
-
|
|
247
|
+
// Create promise for async reconnect
|
|
248
|
+
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
|
|
240
249
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
250
|
+
ReconnectWorker* worker = new ReconnectWorker(
|
|
251
|
+
env, card_, shareMode, preferredProtocols, initialization, deferred);
|
|
252
|
+
worker->Queue();
|
|
245
253
|
|
|
246
|
-
|
|
247
|
-
return Napi::Number::New(env, activeProtocol);
|
|
254
|
+
return deferred.Promise();
|
|
248
255
|
}
|