smartcard 0.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.
@@ -0,0 +1,22 @@
1
+ require 'mkmf'
2
+
3
+ $LDFLAGS ||= ''
4
+
5
+ pcsc_headers = []
6
+ pcsc_defines = []
7
+ if RUBY_PLATFORM =~ /darwin/
8
+ pcsc_headers += ['<PCSC/winscard.h>']
9
+ pcsc_headers += ['"pcsc_surrogate_wintypes.h"', '"pcsc_surrogate_reader.h"']
10
+ $LDFLAGS += ' -framework PCSC'
11
+ elsif RUBY_PLATFORM =~ /win/
12
+ pcsc_headers += ['<winscard.h>']
13
+ else
14
+ pcsc_headers += ['<winscard.h>']
15
+ end
16
+
17
+ File.open('pcsc_include.h', 'w') do |f|
18
+ pcsc_defines.each { |d| f.write "\#define #{d}\n" }
19
+ pcsc_headers.each { |h| f.write "\#include #{h}\n" }
20
+ end
21
+
22
+ create_makefile('smartcard_pcsc')
@@ -0,0 +1,35 @@
1
+ /* Ruby extension API. */
2
+ #include <ruby.h>
3
+ /* Generated by 'extconf.rb' to point to the PC/SC header. */
4
+ #include "pcsc_include.h"
5
+
6
+ /* Namespace structure. */
7
+ extern VALUE mSmartcard; /* Smartcard module / namespace */
8
+ extern VALUE mPcsc; /* Smartcard::PCSC module / namespace */
9
+
10
+ /* Class Smartcard::PCSC::ReaderStates */
11
+ extern VALUE cPcscReaderStates;
12
+ void Init_PCSC_ReaderStates();
13
+ int _PCSC_ReaderStates_lowlevel_get(VALUE rbReaderStates, SCARD_READERSTATE **reader_states, size_t *reader_states_count);
14
+
15
+ /* Class Smartcard::PCSC::IoRequest */
16
+ extern VALUE cPcscIoRequest;
17
+ void Init_PCSC_IoRequest();
18
+ int _PCSC_IoRequest_lowlevel_get(VALUE rbIoRequest, SCARD_IO_REQUEST **io_request);
19
+
20
+ /* Class Smartcard::PCSC::Context */
21
+ extern VALUE cPcscContext;
22
+ void Init_PCSC_Context();
23
+ int _PCSC_Context_lowlevel_get(VALUE rbContext, SCARDCONTEXT *pcsc_context);
24
+
25
+ /* Class Smartcard::PCSC::Card */
26
+ extern VALUE cPcscCard;
27
+ void Init_PCSC_Card();
28
+ int _PCSC_Card_lowlevel_get(VALUE rbCard, SCARDHANDLE *card_handle);
29
+
30
+ /* Constants in Smartcard::PCSC */
31
+ void Init_PCSC_Consts();
32
+
33
+ /* Multi-string (win32 abomination) tools. */
34
+ VALUE PCSC_Internal_multistring_to_ruby_array(char *mstr, size_t mstr_len);
35
+ int PCSC_Internal_ruby_strings_to_multistring(VALUE rbStrings, char **strings);
@@ -0,0 +1,440 @@
1
+ #include "pcsc.h"
2
+
3
+ VALUE cPcscCard;
4
+
5
+ /* Wraps a SCARDHANDLE, tracking whether it was released or not, together with the last error that occured on it. */
6
+ struct SCardHandleEx {
7
+ SCARDHANDLE card_handle;
8
+ DWORD pcsc_error;
9
+ int released;
10
+ };
11
+
12
+ /* Custom free for Smartcard::PCSC::Card. Releases the card handle via a disconnect if that was not already done. */
13
+ static void PCSC_Card_free(struct SCardHandleEx *_card) {
14
+ if(_card != NULL) {
15
+ if(!_card->released)
16
+ SCardDisconnect(_card->card_handle, SCARD_LEAVE_CARD);
17
+ xfree(_card);
18
+ }
19
+ }
20
+
21
+ /* Custom allocation for Smartcard::PCSC::Card. Wraps a SCardHandleEx. */
22
+ static VALUE PCSC_Card_alloc(VALUE klass) {
23
+ struct SCardHandleEx *card;
24
+
25
+ VALUE rbCard = Data_Make_Struct(klass, struct SCardHandleEx, NULL, PCSC_Card_free, card);
26
+ card->pcsc_error = SCARD_S_SUCCESS;
27
+ card->released = 1;
28
+ return rbCard;
29
+ }
30
+
31
+ /* :Document-method: new
32
+ * call-seq:
33
+ * new(context, reader_name, share_mode, preferred_protocols) --> card
34
+ *
35
+ * Establishes a connection to the card in the reader whose friendly name is +reader_name+.
36
+ * The first connection will power up and perform a reset on the card.
37
+ * Wraps _SCardConnect_ in PC/SC.
38
+ *
39
+ * +context+:: the Smartcard::PCSC::Context to use to connect to the PC/SC resource manager
40
+ * +reader_name+:: friendly name of the reader to connect to; get using Smartcard::PCSC::Context#list_readers
41
+ * +share_mode+:: whether a shared or exclusive lock will be requested on the reader; use one of the Smartcard::PCSC::SHARE_ constants
42
+ * +preferred_protocols+:: desired protocol; use one of the Smartcard::PCSC::PROTOCOL_ constants
43
+ */
44
+ static VALUE PCSC_Card_initialize(VALUE self, VALUE rbContext, VALUE rbReaderName, VALUE rbShareMode, VALUE rbPreferredProtocols) {
45
+ struct SCardHandleEx *card;
46
+ Data_Get_Struct(self, struct SCardHandleEx, card);
47
+
48
+ SCARDCONTEXT context;
49
+ if(_PCSC_Context_lowlevel_get(rbContext, &context) == 0) {
50
+ rb_raise(rb_eArgError, "first argument is not a Context instance");
51
+ return self;
52
+ }
53
+
54
+ VALUE rbFinalReaderName = rb_check_string_type(rbReaderName);
55
+ if(NIL_P(rbFinalReaderName)) {
56
+ rb_raise(rb_eArgError, "second argument (should be reader name) does not convert to a String");
57
+ return self;
58
+ }
59
+
60
+ DWORD share_mode = NUM2UINT(rbShareMode);
61
+ DWORD preferred_protocols = NUM2UINT(rbPreferredProtocols);
62
+
63
+ DWORD active_protocol;
64
+ card->pcsc_error = SCardConnect(context, RSTRING(rbFinalReaderName)->ptr, share_mode, preferred_protocols, &card->card_handle, &active_protocol);
65
+ if(card->pcsc_error != SCARD_S_SUCCESS)
66
+ rb_raise(rb_eRuntimeError, "SCardConnect: %s", pcsc_stringify_error(card->pcsc_error));
67
+ else
68
+ card->released = 0;
69
+ return self;
70
+ }
71
+
72
+ /* :Document-method: reconnect
73
+ * call-seq:
74
+ * card.reconnect(share_mode, preferred_protocols, initialization) --> self
75
+ *
76
+ * Reestablishes a connection to a reader that was previously connected to using Card#new.
77
+ * Wraps _SCardReconnect_ in PC/SC.
78
+ *
79
+ * +share_mode+:: whether a shared or exclusive lock will be requested on the reader; use one of the Smartcard::PCSC::SHARE_ constants
80
+ * +preferred_protocols+:: desired protocol; use one of the Smartcard::PCSC::PROTOCOL_ constants
81
+ * +initialization+:: action to be taken on the card inside the reader; use one of the Smartcard::PCSC::INITIALIZE_ constants
82
+ */
83
+ static VALUE PCSC_Card_reconnect(VALUE self, VALUE rbShareMode, VALUE rbPreferredProtocols, VALUE rbInitialization) {
84
+ struct SCardHandleEx *card;
85
+ Data_Get_Struct(self, struct SCardHandleEx, card);
86
+ if(card == NULL) return self;
87
+
88
+ DWORD share_mode = NUM2UINT(rbShareMode);
89
+ DWORD preferred_protocols = NUM2UINT(rbPreferredProtocols);
90
+ DWORD initialization = NUM2UINT(rbInitialization);
91
+
92
+ uint32_t active_protocol;
93
+ card->pcsc_error = SCardReconnect(card->card_handle, share_mode, preferred_protocols, initialization, &active_protocol);
94
+ if(card->pcsc_error != SCARD_S_SUCCESS)
95
+ rb_raise(rb_eRuntimeError, "SCardReconnect: %s", pcsc_stringify_error(card->pcsc_error));
96
+
97
+ return self;
98
+ }
99
+
100
+ /* :Document-method: disconnect
101
+ * call-seq:
102
+ * context.disconnect(disposition) --> self
103
+ *
104
+ * Terminates the connection made using Card#new. The Card object is invalid afterwards.
105
+ * Wraps _SCardDisconnect_ in PC/SC.
106
+ *
107
+ * +disposition+:: action to be taken on the card inside the reader; use one of the Smartcard::PCSC::DISPOSITION_ constants
108
+ */
109
+ static VALUE PCSC_Card_disconnect(VALUE self, VALUE rbDisposition) {
110
+ struct SCardHandleEx *card;
111
+ Data_Get_Struct(self, struct SCardHandleEx, card);
112
+ if(card == NULL) return self;
113
+
114
+ DWORD disposition = NUM2UINT(rbDisposition);
115
+ if(!card->released) {
116
+ card->pcsc_error = SCardDisconnect(card->card_handle, disposition);
117
+ card->released = 1;
118
+ if(card->pcsc_error != SCARD_S_SUCCESS)
119
+ rb_raise(rb_eRuntimeError, "SCardDisconnect: %s", pcsc_stringify_error(card->pcsc_error));
120
+ }
121
+ return self;
122
+ }
123
+
124
+ /* :Document-method: begin_transaction
125
+ * call-seq:
126
+ * card.begin_transaction() --> self
127
+ *
128
+ * Establishes a temporary exclusive access mode for doing a series of commands or transaction.
129
+ * Wraps _SCardBeginTransaction_ in PC/SC.
130
+ */
131
+ static VALUE PCSC_Card_begin_transaction(VALUE self) {
132
+ struct SCardHandleEx *card;
133
+ Data_Get_Struct(self, struct SCardHandleEx, card);
134
+ if(card == NULL) return self;
135
+
136
+ card->pcsc_error = SCardBeginTransaction(card->card_handle);
137
+ if(card->pcsc_error != SCARD_S_SUCCESS)
138
+ rb_raise(rb_eRuntimeError, "SCardBeginTransaction: %s", pcsc_stringify_error(card->pcsc_error));
139
+ return self;
140
+ }
141
+
142
+ /* :Document-method: end_transaction
143
+ * call-seq:
144
+ * context.end_transaction(disposition) --> attribute_value
145
+ *
146
+ * Ends a previously begun transaction. The calling application must be the owner of the previously begun transaction or an error will occur.
147
+ * Wraps _SCardEndTransaction_ in PC/SC.
148
+ *
149
+ * +disposition+:: action to be taken on the card inside the reader; use one of the Smartcard::PCSC::DISPOSITION_ constants
150
+ */
151
+ static VALUE PCSC_Card_end_transaction(VALUE self, VALUE rbDisposition) {
152
+ struct SCardHandleEx *card;
153
+ Data_Get_Struct(self, struct SCardHandleEx, card);
154
+ if(card == NULL) return self;
155
+
156
+ DWORD disposition = NUM2UINT(rbDisposition);
157
+ card->pcsc_error = SCardEndTransaction(card->card_handle, disposition);
158
+ if(card->pcsc_error != SCARD_S_SUCCESS)
159
+ rb_raise(rb_eRuntimeError, "SCardEndTransaction: %s", pcsc_stringify_error(card->pcsc_error));
160
+ return self;
161
+ }
162
+
163
+ /* :Document-method: get_attribute
164
+ * call-seq:
165
+ * card.get_attribute(attribute_id) --> attribute_value
166
+ *
167
+ * Reads the value of an attribute from the interface driver.
168
+ * Remember that the IFD may not implement some of the attributes specified in Smartcard::PCSC, and it may implement some attributes that
169
+ * are not included in Smartcard::PCSC.
170
+ * Wraps _SCardGetAttrib_ in PC/SC.
171
+ *
172
+ * The returned value has the bytes in the attribute, wrapped in a string. (don't complain, it's a low-level API)
173
+ *
174
+ * +attribute_id+:: identifies the attribute to be read; use one of the Smartcard::PCSC::ATTR_ constants
175
+ */
176
+ static VALUE PCSC_Card_get_attribute(VALUE self, VALUE rbAttributeId) {
177
+ struct SCardHandleEx *card;
178
+ Data_Get_Struct(self, struct SCardHandleEx, card);
179
+ if(card == NULL) return Qnil;
180
+
181
+ DWORD attribute_id = NUM2UINT(rbAttributeId);
182
+ DWORD attribute_length;
183
+ card->pcsc_error = SCardGetAttrib(card->card_handle, attribute_id, NULL, &attribute_length);
184
+ if(card->pcsc_error == SCARD_S_SUCCESS) {
185
+ char *attribute_buffer = ALLOC_N(char, attribute_length);
186
+ if(attribute_buffer != NULL) {
187
+ card->pcsc_error = SCardGetAttrib(card->card_handle, attribute_id, (LPSTR)attribute_buffer, &attribute_length);
188
+ if(card->pcsc_error == SCARD_S_SUCCESS) {
189
+ VALUE rbAttribute = rb_str_new(attribute_buffer, attribute_length);
190
+ xfree(attribute_buffer);
191
+ return rbAttribute;
192
+ }
193
+ }
194
+ }
195
+ if(card->pcsc_error != SCARD_S_SUCCESS)
196
+ rb_raise(rb_eRuntimeError, "SCardGetAttrib: %s", pcsc_stringify_error(card->pcsc_error));
197
+ return Qnil;
198
+ }
199
+
200
+ /* :Document-method: set_attribute
201
+ * call-seq:
202
+ * context.set_attribute(attribute_id, attribute_value) --> self
203
+ *
204
+ * Sets the value of an attribute in the interface driver.
205
+ * Remember that the IFD may not implement some of the attributes specified in Smartcard::PCSC, and it may implement some attributes that
206
+ * are not included in Smartcard::PCSC.
207
+ * Wraps _SCardSetAttrib_ in PC/SC.
208
+ *
209
+ * +attribute_id+:: identifies the attribute to be set; use one of the Smartcard::PCSC::ATTR_ constants
210
+ * +attribute_value+:: the value to be assigned to the attribute; wrap the bytes in a string-like object (low-level API, remember?)
211
+ */
212
+ static VALUE PCSC_Card_set_attribute(VALUE self, VALUE rbAttributeId, VALUE rbAttributeValue) {
213
+ struct SCardHandleEx *card;
214
+ Data_Get_Struct(self, struct SCardHandleEx, card);
215
+ if(card == NULL) return self;
216
+
217
+ DWORD attribute_id = NUM2UINT(rbAttributeId);
218
+
219
+ VALUE rbFinalAttributeValue = rb_check_string_type(rbAttributeValue);
220
+ if(NIL_P(rbFinalAttributeValue)) {
221
+ rb_raise(rb_eArgError, "second argument (attribute buffer) does not convert to a String");
222
+ return self;
223
+ }
224
+
225
+ card->pcsc_error = SCardSetAttrib(card->card_handle, attribute_id, (LPSTR)RSTRING(rbFinalAttributeValue)->ptr, RSTRING(rbFinalAttributeValue)->len);
226
+ if(card->pcsc_error != SCARD_S_SUCCESS)
227
+ rb_raise(rb_eRuntimeError, "SCardSetAttrib: %s", pcsc_stringify_error(card->pcsc_error));
228
+ return self;
229
+ }
230
+
231
+ /* :Document-method: transmit
232
+ * call-seq:
233
+ * card.transmit(send_data, send_io_request, recv_io_request) --> recv_data
234
+ *
235
+ * Sends an APDU to the smart card, and returns the card's response to the APDU.
236
+ * Wraps _SCardTransmit_ in PC/SC.
237
+ *
238
+ * The bytes in the card's response are returned wrapped in a string. (don't complain, it's a low-level API)
239
+ *
240
+ * +send_data+:: the APDU to be send to the card; wrap the bytes in a string-like object (low-level API, remember?)
241
+ * +send_io_request+:: Smartcard::PCSC::IoRequest instance indicating the send protocol; you can use one of the Smartcard::PCSC::PCI_ constants
242
+ * +recv_io_request+:: Smartcard::PCSC::IoRequest instance receving information about the recv protocol; you can use the result of Smartcard::PCSC::IoRequest#new
243
+ */
244
+ static VALUE PCSC_Card_transmit(VALUE self, VALUE rbSendData, VALUE rbSendIoRequest, VALUE rbRecvIoRequest) {
245
+ struct SCardHandleEx *card;
246
+ Data_Get_Struct(self, struct SCardHandleEx, card);
247
+ if(card == NULL) return Qnil;
248
+
249
+ VALUE rbFinalSendData = rb_check_string_type(rbSendData);
250
+ if(NIL_P(rbFinalSendData)) {
251
+ rb_raise(rb_eArgError, "first argument (send buffer) does not convert to a String");
252
+ return Qnil;
253
+ }
254
+
255
+ SCARD_IO_REQUEST *send_io_request;
256
+ if(_PCSC_IoRequest_lowlevel_get(rbSendIoRequest, &send_io_request) == 0) {
257
+ rb_raise(rb_eArgError, "second argument (send io request) is not an IoRequest instance");
258
+ return Qnil;
259
+ }
260
+ SCARD_IO_REQUEST *recv_io_request;
261
+ if(_PCSC_IoRequest_lowlevel_get(rbRecvIoRequest, &recv_io_request) == 0) {
262
+ rb_raise(rb_eArgError, "second argument (recv io request) is not an IoRequest instance");
263
+ return Qnil;
264
+ }
265
+
266
+ DWORD recv_length = PCSCLITE_MAX_MESSAGE_SIZE;
267
+ char *recv_buffer = ALLOC_N(char, recv_length);
268
+ if(recv_buffer == NULL) return Qnil;
269
+
270
+ card->pcsc_error = SCardTransmit(card->card_handle, send_io_request,
271
+ (LPSTR)RSTRING(rbFinalSendData)->ptr, RSTRING(rbFinalSendData)->len,
272
+ recv_io_request, (LPSTR)recv_buffer, &recv_length);
273
+ if(card->pcsc_error != SCARD_S_SUCCESS) {
274
+ xfree(recv_buffer);
275
+ rb_raise(rb_eRuntimeError, "SCardTransmit: %s", pcsc_stringify_error(card->pcsc_error));
276
+ return Qnil;
277
+ }
278
+
279
+ VALUE rbRecvData = rb_str_new(recv_buffer, recv_length);
280
+ xfree(recv_buffer);
281
+ return rbRecvData;
282
+ }
283
+
284
+ /* :Document-method: control
285
+ * call-seq:
286
+ * card.control(control_code, send_data, max_recv_bytes) --> recv_data
287
+ *
288
+ * Sends a command directly to the interface driver to be processed by the reader.
289
+ * Useful for creating client side reader drivers for functions like PIN pads, biometrics, or other smart card reader
290
+ * extensions that are not normally handled by PC/SC.
291
+ * Wraps _SCardControl_ in PC/SC.
292
+ *
293
+ * The bytes in the response are returned wrapped in a string. (don't complain, it's a low-level API)
294
+ *
295
+ * +control_code+:: control code for the operation; it's an integer, and it's IFD-specific
296
+ * +send_data+:: the data bytes to be send to the driver; wrap the bytes in a string-like object (low-level API, remember?)
297
+ * +max_recv_bytes+:: the maximum number of bytes that can be received
298
+ *
299
+ * In general, I tried to avoid having you specify receive buffer sizes. This is the only case where that is impossible to achieve,
300
+ * because there is no well-known maximum buffer size, and the _SCardControl_ call is not guaranteed to be idempotent, so it's not OK to
301
+ * re-issue it until the buffer size works out.
302
+ */
303
+ static VALUE PCSC_Card_control(VALUE self, VALUE rbControlCode, VALUE rbSendData, VALUE rbMaxRecvBytes) {
304
+ struct SCardHandleEx *card;
305
+ Data_Get_Struct(self, struct SCardHandleEx, card);
306
+ if(card == NULL) return Qnil;
307
+
308
+ VALUE rbFinalSendData = rb_check_string_type(rbSendData);
309
+ if(NIL_P(rbFinalSendData)) {
310
+ rb_raise(rb_eArgError, "second argument (send buffer) does not convert to a String");
311
+ return Qnil;
312
+ }
313
+
314
+ DWORD control_code = NUM2UINT(rbControlCode);
315
+ DWORD recv_length = NUM2UINT(rbMaxRecvBytes);
316
+ char *recv_buffer = ALLOC_N(char, recv_length);
317
+ if(recv_buffer == NULL) return Qnil;
318
+
319
+ card->pcsc_error = SCardControl(card->card_handle, control_code,
320
+ (LPSTR)RSTRING(rbFinalSendData)->ptr, RSTRING(rbFinalSendData)->len,
321
+ recv_buffer, recv_length, &recv_length);
322
+ if(card->pcsc_error != SCARD_S_SUCCESS) {
323
+ xfree(recv_buffer);
324
+ rb_raise(rb_eRuntimeError, "SCardControl: %s", pcsc_stringify_error(card->pcsc_error));
325
+ return Qnil;
326
+ }
327
+
328
+ VALUE rbRecvData = rb_str_new(recv_buffer, recv_length);
329
+ xfree(recv_buffer);
330
+ return rbRecvData;
331
+ }
332
+
333
+ static VALUE _rbStateKey, _rbProtocolKey, _rbAtrKey, _rbReaderNamesKey;
334
+
335
+ /* :Document-method: status
336
+ * call-seq:
337
+ * card.status() --> card_status
338
+ *
339
+ * Retrieves the current status of the smartcard, and packages it up in a nice hash for you.
340
+ * Wraps _SCardStatus_ in PC/SC.
341
+ *
342
+ * The response hash contains the following keys:
343
+ * <tt>:state</tt> :: reader/card status; bitfield, with bits defined as Smartcard::PCSC::STATUS_ constants
344
+ * <tt>:protocol</tt> :: the protocol established with the card; check against Smartcard::PCSC::PROTOCOL_ constants
345
+ * <tt>:atr</tt> :: the card's ATR bytes, wrapped in a string
346
+ * <tt>:reader_names</tt> :: array of strings containing all the names of the reader containing the smartcard
347
+ */
348
+ static VALUE PCSC_Card_status(VALUE self) {
349
+ struct SCardHandleEx *card;
350
+ Data_Get_Struct(self, struct SCardHandleEx, card);
351
+ if(card == NULL) return Qnil;
352
+
353
+ DWORD atr_length = MAX_ATR_SIZE;
354
+ char *atr_buffer = ALLOC_N(char, atr_length);
355
+ DWORD reader_names_length = 4096;
356
+ char *reader_names_buffer = ALLOC_N(char, reader_names_length);
357
+ if(atr_buffer == NULL || reader_names_buffer == NULL) {
358
+ if(reader_names_buffer != NULL) xfree(reader_names_buffer);
359
+ if(atr_buffer != NULL) xfree(atr_buffer);
360
+ return Qnil;
361
+ }
362
+
363
+ DWORD state, protocol;
364
+ card->pcsc_error = SCardStatus(card->card_handle, reader_names_buffer, &reader_names_length, &state, &protocol, (LPSTR)atr_buffer, &atr_length);
365
+ if(card->pcsc_error != SCARD_S_SUCCESS) {
366
+ xfree(reader_names_buffer); xfree(atr_buffer);
367
+ rb_raise(rb_eRuntimeError, "SCardStatus: %s", pcsc_stringify_error(card->pcsc_error));
368
+ return Qnil;
369
+ }
370
+
371
+ VALUE rbStateVal = UINT2NUM(state);
372
+ VALUE rbProtocolVal = UINT2NUM(protocol);
373
+ VALUE rbAtrVal = rb_str_new(atr_buffer, atr_length);
374
+ VALUE rbReaderNamesVal = PCSC_Internal_multistring_to_ruby_array(reader_names_buffer, reader_names_length);
375
+
376
+ VALUE rbReturnHash = rb_hash_new();
377
+ rb_hash_aset(rbReturnHash, _rbStateKey, rbStateVal);
378
+ rb_hash_aset(rbReturnHash, _rbProtocolKey, rbProtocolVal);
379
+ rb_hash_aset(rbReturnHash, _rbAtrKey, rbAtrVal);
380
+ rb_hash_aset(rbReturnHash, _rbReaderNamesKey, rbReaderNamesVal);
381
+ return rbReturnHash;
382
+ }
383
+
384
+ /* :Document-method: last_error
385
+ * call-seq:
386
+ * card.last_error() --> last_error
387
+ *
388
+ * The error code returned by the last PC/SC call. Useful for recovering from exceptions.
389
+ *
390
+ * The returned code is a number, and should be one of the Smartcard::PCSC::SCARD_ constants.
391
+ * The code indicating correct operation is Smartcard::PCSC::SCARD_S_SUCCESS.
392
+ */
393
+ static VALUE PCSC_Card_last_error(VALUE self) {
394
+ struct SCardHandleEx *card;
395
+ Data_Get_Struct(self, struct SCardHandleEx, card);
396
+ if(card == NULL) return Qnil;
397
+
398
+ return UINT2NUM(card->pcsc_error);
399
+ }
400
+
401
+ #ifdef MAKE_RDOC_HAPPY
402
+ mSmartcard = rb_define_module("Smartcard");
403
+ mPcsc = rb_define_module_under(mSmartcard, "PCSC");
404
+ #endif
405
+
406
+ /* :Document-class: Smartcard::PCSC::Card
407
+ * Connects a smart-card in a PC/SC reader to the Ruby world.
408
+ * Wraps a _SCARDHANDLE_ structure.
409
+ */
410
+ void Init_PCSC_Card() {
411
+ ID state_id = rb_intern("status"); _rbStateKey = ID2SYM(state_id);
412
+ ID protocol_id = rb_intern("protocol"); _rbProtocolKey = ID2SYM(protocol_id);
413
+ ID atr_id = rb_intern("atr"); _rbAtrKey = ID2SYM(atr_id);
414
+ ID reader_names_id = rb_intern("reader_names"); _rbReaderNamesKey = ID2SYM(reader_names_id);
415
+
416
+ cPcscCard = rb_define_class_under(mPcsc, "Card", rb_cObject);
417
+ rb_define_alloc_func(cPcscCard, PCSC_Card_alloc);
418
+ rb_define_method(cPcscCard, "initialize", PCSC_Card_initialize, 4);
419
+ rb_define_method(cPcscCard, "reconnect", PCSC_Card_reconnect, 3);
420
+ rb_define_method(cPcscCard, "disconnect", PCSC_Card_disconnect, 1);
421
+ rb_define_method(cPcscCard, "begin_transaction", PCSC_Card_begin_transaction, 0);
422
+ rb_define_method(cPcscCard, "end_transaction", PCSC_Card_end_transaction, 1);
423
+ rb_define_method(cPcscCard, "get_attribute", PCSC_Card_get_attribute, 1);
424
+ rb_define_method(cPcscCard, "set_attribute", PCSC_Card_set_attribute, 2);
425
+ rb_define_method(cPcscCard, "transmit", PCSC_Card_transmit, 3);
426
+ rb_define_method(cPcscCard, "control", PCSC_Card_control, 3);
427
+ rb_define_method(cPcscCard, "status", PCSC_Card_status, 0);
428
+ rb_define_method(cPcscCard, "last_error", PCSC_Card_last_error, 0);
429
+ }
430
+
431
+ /* Retrieves the SCARDHANDLE wrapped into a Smartcard::PCSC::Card instance. */
432
+ int _PCSC_Card_lowlevel_get(VALUE rbCard, SCARDHANDLE *card_handle) {
433
+ if(TYPE(rbCard) != T_DATA || RDATA(rbCard)->dfree != (void (*)(void *))PCSC_Card_free)
434
+ return 0;
435
+
436
+ struct SCardHandleEx *card;
437
+ Data_Get_Struct(rbCard, struct SCardHandleEx, card);
438
+ *card_handle = card->card_handle;
439
+ return 1;
440
+ }