1 """PCSC Smartcard request.
2
3 __author__ = "http://www.gemalto.com"
4
5 Copyright 2001-2012 gemalto
6 Author: Jean-Daniel Aussel, mailto:jean-daniel.aussel@gemalto.com
7
8 This file is part of pyscard.
9
10 pyscard is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14
15 pyscard is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public License
21 along with pyscard; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 """
24
25 from __future__ import print_function
26 import threading
27 import time
28
29 from smartcard.AbstractCardRequest import AbstractCardRequest
30 from smartcard.Exceptions import CardRequestTimeoutException
31 from smartcard.Exceptions import CardRequestException, ListReadersException
32 from smartcard.pcsc.PCSCReader import PCSCReader
33 from smartcard.pcsc.PCSCContext import PCSCContext
34 from smartcard import Card
35
36 from smartcard.scard import *
37
38
40 if not isInfinite:
41 evt.set()
42
43
45 """PCSC CardRequest class."""
46
47 - def __init__(self, newcardonly=False, readers=None,
48 cardType=None, cardServiceClass=None, timeout=1):
49 """Construct new PCSCCardRequest.
50
51 @param newcardonly: if True, request a new card default is
52 False, i.e. accepts cards already inserted
53
54 @param readers: the list of readers to consider for requesting a
55 card default is to consider all readers
56
57 @param cardType: the CardType class to wait for; default is
58 AnyCardType, i.e. the request will returns with new or already
59 inserted cards
60
61 @param cardServiceClass: the specific card service class to
62 create and bind to the card default is to create and bind a
63 PassThruCardService
64
65 @param timeout: the time in seconds we are ready to wait for
66 connecting to the requested card. default is to wait one second
67 to wait forever, set timeout to None
68 """
69 AbstractCardRequest.__init__(
70 self, newcardonly, readers, cardType, cardServiceClass, timeout)
71
72
73 self.pollinginterval = 0.1
74
75
76 if None == self.timeout:
77 self.timeout = INFINITE
78
79 else:
80 self.timeout = int(self.timeout)
81
82 self.hcontext = PCSCContext().getContext()
83
107
109 """Wait for card insertion and returns a card service."""
110 AbstractCardRequest.waitforcard(self)
111 cardfound = False
112
113
114
115 evt = threading.Event()
116 if INFINITE == self.timeout:
117 timertimeout = 1
118 else:
119 timertimeout = self.timeout
120 timer = threading.Timer(
121 timertimeout, signalEvent, [evt, INFINITE == self.timeout])
122
123
124 readerstates = {}
125 readernames = self.getReaderNames()
126 for reader in readernames:
127 if not reader in readerstates:
128 readerstates[reader] = (reader, SCARD_STATE_UNAWARE)
129
130
131 for oldreader in list(readerstates.keys()):
132 if oldreader not in readernames:
133 del readerstates[oldreader]
134
135
136 if {} != readerstates:
137 hresult, newstates = SCardGetStatusChange(
138 self.hcontext, 0, list(readerstates.values()))
139 else:
140 hresult = 0
141 newstates = []
142
143
144
145
146 if 0 != hresult and \
147 SCARD_E_TIMEOUT != hresult and \
148 SCARD_E_UNKNOWN_READER != hresult:
149 raise CardRequestException(
150 'Failed to SCardGetStatusChange ' + \
151 SCardGetErrorMessage(hresult))
152
153
154
155
156 if SCARD_E_TIMEOUT == hresult or SCARD_E_UNKNOWN_READER == hresult:
157 for state in newstates:
158 state[1] = state[1] & (0xFFFFFFFF ^ SCARD_STATE_CHANGED)
159
160
161 for state in newstates:
162 readername, eventstate, atr = state
163 readerstates[readername] = (readername, eventstate)
164
165
166 if not self.newcardonly:
167 for state in newstates:
168 readername, eventstate, atr = state
169 if eventstate & SCARD_STATE_PRESENT:
170 reader = PCSCReader(readername)
171 if self.cardType.matches(atr, reader):
172 if self.cardServiceClass.supports('dummy'):
173 cardfound = True
174 return self.cardServiceClass(
175 reader.createConnection())
176
177 timerstarted = False
178 while not evt.isSet() and not cardfound:
179
180 if not timerstarted:
181 timerstarted = True
182 timer.start()
183
184 time.sleep(self.pollinginterval)
185
186
187 readernames = self.getReaderNames()
188 for reader in readernames:
189 if not reader in readerstates:
190 readerstates[reader] = (reader, SCARD_STATE_UNAWARE)
191
192
193 for oldreader in list(readerstates.keys()):
194 if oldreader not in readernames:
195 del readerstates[oldreader]
196
197
198 if {} != readerstates:
199 hresult, newstates = SCardGetStatusChange(
200 self.hcontext, 0, list(readerstates.values()))
201 else:
202 hresult = SCARD_E_TIMEOUT
203 newstates = []
204
205
206 if SCARD_E_TIMEOUT == hresult:
207 if evt.isSet():
208 raise CardRequestTimeoutException()
209
210
211 elif SCARD_E_UNKNOWN_READER == hresult:
212 pass
213
214
215 elif 0 != hresult:
216 timer.cancel()
217 raise CardRequestException(
218 'Failed to get status change ' + \
219 SCardGetErrorMessage(hresult))
220
221
222 else:
223
224
225
226
227 for state in newstates:
228 readername, eventstate, atr = state
229 r, oldstate = readerstates[readername]
230
231
232
233
234
235 if self.newcardonly:
236 if oldstate & SCARD_STATE_PRESENT and \
237 eventstate & \
238 (SCARD_STATE_CHANGED | SCARD_STATE_PRESENT):
239 eventstate = eventstate & \
240 (0xFFFFFFFF ^ SCARD_STATE_CHANGED)
241
242 if (self.newcardonly and \
243 eventstate & SCARD_STATE_PRESENT and \
244 eventstate & SCARD_STATE_CHANGED) or \
245 (not self.newcardonly and \
246 eventstate & SCARD_STATE_PRESENT):
247 reader = PCSCReader(readername)
248 if self.cardType.matches(atr, reader):
249 if self.cardServiceClass.supports('dummy'):
250 cardfound = True
251 timer.cancel()
252 return self.cardServiceClass(
253 reader.createConnection())
254
255
256 readerstates[readername] = (readername, eventstate)
257
258 if evt.isSet():
259 raise CardRequestTimeoutException()
260
262 """Wait for card insertion or removal."""
263 AbstractCardRequest.waitforcardevent(self)
264 presentcards = []
265 evt = threading.Event()
266
267
268 if INFINITE == self.timeout:
269 timertimeout = 1
270 else:
271 timertimeout = self.timeout
272 timer = threading.Timer(
273 timertimeout, signalEvent, [evt, INFINITE == self.timeout])
274
275
276 readerstates = {}
277 timerstarted = False
278
279 while not evt.isSet():
280
281 if not timerstarted:
282 timerstarted = True
283 timer.start()
284
285 time.sleep(self.pollinginterval)
286
287
288 readernames = self.getReaderNames()
289 for reader in readernames:
290
291 if not reader in readerstates:
292 readerstates[reader] = (reader, SCARD_STATE_UNAWARE)
293
294 for oldreader in list(readerstates.keys()):
295 if oldreader not in readernames:
296 del readerstates[oldreader]
297
298
299 if {} != readerstates:
300 hresult, newstates = SCardGetStatusChange(
301 self.hcontext, 0, list(readerstates.values()))
302 else:
303 hresult = 0
304 newstates = []
305
306
307 if SCARD_E_TIMEOUT == hresult:
308 if evt.isSet():
309 raise CardRequestTimeoutException()
310
311
312 elif SCARD_E_UNKNOWN_READER == hresult:
313 pass
314
315
316 elif 0 != hresult:
317 timer.cancel()
318 raise CardRequestException(
319 'Failed to get status change ' + \
320 SCardGetErrorMessage(hresult))
321
322
323 else:
324 timer.cancel()
325 for state in newstates:
326 readername, eventstate, atr = state
327 r, oldstate = readerstates[readername]
328
329
330
331
332 if oldstate & SCARD_STATE_PRESENT and \
333 eventstate & \
334 (SCARD_STATE_CHANGED | SCARD_STATE_PRESENT):
335 eventstate = eventstate & \
336 (0xFFFFFFFF ^ SCARD_STATE_CHANGED)
337
338 if eventstate & SCARD_STATE_PRESENT and \
339 eventstate & SCARD_STATE_CHANGED:
340 presentcards.append(Card.Card(readername, atr))
341 return presentcards
342
343 if evt.isSet():
344 raise CardRequestTimeoutException()
345
346 if __name__ == '__main__':
347 """Small sample illustrating the use of PCSCCardRequest.py."""
348
349 from smartcard.util import toHexString
350 print('Insert a new card within 10 seconds')
351 cr = PCSCCardRequest(timeout=10, newcardonly=True)
352 cs = cr.waitforcard()
353 cs.connection.connect()
354 print(cs.connection.getReader() + ' ' + toHexString(cs.connection.getATR()))
355 cs.connection.disconnect()
356