1 """PCSC Smartcard request.
2
3 __author__ = "http://www.gemalto.com"
4
5 Copyright 2001-2011 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 import threading
26 import time
27
28 from smartcard.AbstractCardRequest import AbstractCardRequest
29 from smartcard.Exceptions import CardRequestTimeoutException, CardRequestException, ListReadersException
30 from smartcard.pcsc.PCSCReader import PCSCReader
31 from smartcard.pcsc.PCSCContext import PCSCContext
32 from smartcard import Card
33
34 from smartcard.scard import *
35
36
38 if not isInfinite:
39 evt.set()
40
41
43 """PCSC CardRequest class."""
44
45 - def __init__(self, newcardonly=False, readers=None, cardType=None, cardServiceClass=None, timeout=1):
46 """Construct new PCSCCardRequest.
47
48 @param newcardonly: if True, request a new card default is
49 False, i.e. accepts cards already inserted
50
51 @param readers: the list of readers to consider for requesting a
52 card default is to consider all readers
53
54 @param cardTypeClass: the CardType class to wait for; default is
55 AnyCardType, i.e. the request will returns with new or already
56 inserted cards
57
58 @param cardServiceClass: the specific card service class to
59 create and bind to the card default is to create and bind a
60 PassThruCardService
61
62 @param timeout: the time in seconds we are ready to wait for
63 connecting to the requested card. default is to wait one second
64 to wait forever, set timeout to None
65 """
66 AbstractCardRequest.__init__(self, newcardonly, readers, cardType, cardServiceClass, timeout)
67
68
69 self.pollinginterval = 0.1
70
71
72 if None == self.timeout:
73 self.timeout = INFINITE
74
75 else:
76 self.timeout = int(self.timeout)
77
78 self.hcontext = PCSCContext().getContext()
79
103
105 """Wait for card insertion and returns a card service."""
106 AbstractCardRequest.waitforcard(self)
107 cardfound = False
108
109
110 evt = threading.Event()
111 if INFINITE == self.timeout:
112 timertimeout = 1
113 else:
114 timertimeout = self.timeout
115 timer = threading.Timer(timertimeout, signalEvent, [evt, INFINITE == self.timeout])
116
117
118 readerstates = {}
119 readernames = self.getReaderNames()
120 for reader in readernames:
121 if not readerstates.has_key(reader):
122 readerstates[reader] = (reader, SCARD_STATE_UNAWARE)
123
124
125 for oldreader in readerstates.keys():
126 if oldreader not in readernames:
127 del readerstates[oldreader]
128
129
130 if {} != readerstates:
131 hresult, newstates = SCardGetStatusChange(self.hcontext, 0, readerstates.values())
132 else:
133 hresult = 0
134 newstates = []
135
136
137
138 if 0 != hresult and SCARD_E_TIMEOUT != hresult and SCARD_E_UNKNOWN_READER != hresult:
139 raise CardRequestException('Failed to SCardGetStatusChange ' + SCardGetErrorMessage(hresult))
140
141
142
143 if SCARD_E_TIMEOUT == hresult or SCARD_E_UNKNOWN_READER == hresult:
144 for state in newstates:
145 state[1] = state[1] & (0xFFFFFFFF ^ SCARD_STATE_CHANGED)
146
147
148 for state in newstates:
149 readername, eventstate, atr = state
150 readerstates[readername] = (readername, eventstate)
151
152
153 if not self.newcardonly:
154 for state in newstates:
155 readername, eventstate, atr = state
156 if eventstate & SCARD_STATE_PRESENT:
157 reader = PCSCReader(readername)
158 if self.cardType.matches(atr, reader):
159 if self.cardServiceClass.supports('dummy'):
160 cardfound = True
161 return self.cardServiceClass(reader.createConnection())
162
163 timerstarted = False
164 while not evt.isSet() and not cardfound:
165
166 if not timerstarted:
167 timerstarted = True
168 timer.start()
169
170 time.sleep(self.pollinginterval)
171
172
173 readernames = self.getReaderNames()
174 for reader in readernames:
175 if not readerstates.has_key(reader):
176 readerstates[reader] = (reader, SCARD_STATE_UNAWARE)
177
178
179 for oldreader in readerstates.keys():
180 if oldreader not in readernames:
181 del readerstates[oldreader]
182
183
184 if {} != readerstates:
185 hresult, newstates = SCardGetStatusChange(self.hcontext, 0, readerstates.values())
186 else:
187 hresult = SCARD_E_TIMEOUT
188 newstates = []
189
190
191 if SCARD_E_TIMEOUT == hresult:
192 if evt.isSet():
193 raise CardRequestTimeoutException()
194
195
196 elif SCARD_E_UNKNOWN_READER == hresult:
197 pass
198
199
200 elif 0 != hresult:
201 timer.cancel()
202 raise CardRequestException('Failed to get status change ' + SCardGetErrorMessage(hresult))
203
204
205 else:
206
207
208
209
210 for state in newstates:
211 readername, eventstate, atr = state
212 r, oldstate = readerstates[readername]
213
214
215
216
217
218 if self.newcardonly:
219 if oldstate & SCARD_STATE_PRESENT and eventstate & (SCARD_STATE_CHANGED | SCARD_STATE_PRESENT):
220 eventstate = eventstate & (0xFFFFFFFF ^ SCARD_STATE_CHANGED)
221
222 if (self.newcardonly and eventstate & SCARD_STATE_PRESENT and eventstate & SCARD_STATE_CHANGED) or \
223 (not self.newcardonly and eventstate & SCARD_STATE_PRESENT):
224 reader = PCSCReader(readername)
225 if self.cardType.matches(atr, reader):
226 if self.cardServiceClass.supports('dummy'):
227 cardfound = True
228 timer.cancel()
229 return self.cardServiceClass(reader.createConnection())
230
231
232 readerstates[readername] = (readername, eventstate)
233
234 if evt.isSet():
235 raise CardRequestTimeoutException()
236
238 """Wait for card insertion or removal."""
239 AbstractCardRequest.waitforcardevent(self)
240 presentcards = []
241 evt = threading.Event()
242
243
244 if INFINITE == self.timeout:
245 timertimeout = 1
246 else:
247 timertimeout = self.timeout
248 timer = threading.Timer(timertimeout, signalEvent, [evt, INFINITE == self.timeout])
249
250
251 readerstates = {}
252 timerstarted = False
253
254 while not evt.isSet():
255
256 if not timerstarted:
257 timerstarted = True
258 timer.start()
259
260 time.sleep(self.pollinginterval)
261
262
263 readernames = self.getReaderNames()
264 for reader in readernames:
265
266 if not readerstates.has_key(reader):
267 readerstates[reader] = (reader, SCARD_STATE_UNAWARE)
268
269 for oldreader in readerstates.keys():
270 if oldreader not in readernames:
271 del readerstates[oldreader]
272
273
274 if {} != readerstates:
275 hresult, newstates = SCardGetStatusChange(self.hcontext, 0, readerstates.values())
276 else:
277 hresult = 0
278 newstates = []
279
280
281 if SCARD_E_TIMEOUT == hresult:
282 if evt.isSet():
283 raise CardRequestTimeoutException()
284
285
286 elif SCARD_E_UNKNOWN_READER == hresult:
287 pass
288
289
290 elif 0 != hresult:
291 timer.cancel()
292 raise CardRequestException('Failed to get status change ' + SCardGetErrorMessage(hresult))
293
294
295 else:
296 timer.cancel()
297 for state in newstates:
298 readername, eventstate, atr = state
299 r, oldstate = readerstates[readername]
300
301
302
303
304 if oldstate & SCARD_STATE_PRESENT and eventstate & (SCARD_STATE_CHANGED | SCARD_STATE_PRESENT):
305 eventstate = eventstate & (0xFFFFFFFF ^ SCARD_STATE_CHANGED)
306
307 if eventstate & SCARD_STATE_PRESENT and eventstate & SCARD_STATE_CHANGED:
308 presentcards.append(Card.Card(readername, atr))
309 return presentcards
310
311 if evt.isSet():
312 raise CardRequestTimeoutException()
313
314 if __name__ == '__main__':
315 """Small sample illustrating the use of PCSCCardRequest.py."""
316
317 from smartcard.util import toHexString
318 print 'Insert a new card within 10 seconds'
319 cr = PCSCCardRequest(timeout=10, newcardonly=True)
320 cs = cr.waitforcard()
321 cs.connection.connect()
322 print cs.connection.getReader(), toHexString(cs.connection.getATR())
323 cs.connection.disconnect()
324