Package smartcard :: Package pcsc :: Module PCSCCardRequest
[hide private]
[frames] | no frames]

Source Code for Module smartcard.pcsc.PCSCCardRequest

  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   
39 -def signalEvent(evt, isInfinite):
40 if not isInfinite: 41 evt.set()
42 43
44 -class PCSCCardRequest(AbstractCardRequest):
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 # polling interval in s for SCardGetStatusChange 73 self.pollinginterval = 0.1 74 75 # if timeout is None, translate to scard.INFINITE 76 if None == self.timeout: 77 self.timeout = INFINITE 78 # otherwise, from seconds to milliseconds 79 else: 80 self.timeout = int(self.timeout) 81 82 self.hcontext = PCSCContext().getContext()
83
84 - def getReaderNames(self):
85 """Returns the list or PCSC readers on which to wait for cards.""" 86 87 # get inserted readers 88 hresult, pcscreaders = SCardListReaders(self.hcontext, []) 89 if 0 != hresult and SCARD_E_NO_READERS_AVAILABLE != hresult: 90 raise ListReadersException(hresult) 91 92 readers = [] 93 94 # if no readers asked, use all inserted readers 95 if None == self.readersAsked: 96 readers = pcscreaders 97 98 # otherwise use only the asked readers that are inserted 99 else: 100 for reader in self.readersAsked: 101 if not isinstance(reader, type("")): 102 reader = str(reader) 103 if reader in pcscreaders: 104 readers = readers + [reader] 105 106 return readers
107
108 - def waitforcard(self):
109 """Wait for card insertion and returns a card service.""" 110 AbstractCardRequest.waitforcard(self) 111 cardfound = False 112 113 # for non infinite timeout, a timer will signal 114 # the end of the time-out by setting the evt event 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 # create a dictionary entry for new readers 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 # remove dictionary entry for readers that disappeared 131 for oldreader in list(readerstates.keys()): 132 if oldreader not in readernames: 133 del readerstates[oldreader] 134 135 # call SCardGetStatusChange only if we have some readers 136 if {} != readerstates: 137 hresult, newstates = SCardGetStatusChange( 138 self.hcontext, 0, list(readerstates.values())) 139 else: 140 hresult = 0 141 newstates = [] 142 143 # we can expect normally time-outs or reader 144 # disappearing just before the call 145 # otherwise, raise execption on error 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 # in case of timeout or reader disappearing, 154 # the content of the states is useless 155 # in which case we clear the changed bit 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 # update readerstate 161 for state in newstates: 162 readername, eventstate, atr = state 163 readerstates[readername] = (readername, eventstate) 164 165 # if a new card is not requested, just return the first available 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 # create a dictionary entry for new readers 187 readernames = self.getReaderNames() 188 for reader in readernames: 189 if not reader in readerstates: 190 readerstates[reader] = (reader, SCARD_STATE_UNAWARE) 191 192 # remove dictionary entry for readers that disappeared 193 for oldreader in list(readerstates.keys()): 194 if oldreader not in readernames: 195 del readerstates[oldreader] 196 197 # wait for card insertion 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 # time-out 206 if SCARD_E_TIMEOUT == hresult: 207 if evt.isSet(): 208 raise CardRequestTimeoutException() 209 210 # reader vanished before or during the call 211 elif SCARD_E_UNKNOWN_READER == hresult: 212 pass 213 214 # some error happened 215 elif 0 != hresult: 216 timer.cancel() 217 raise CardRequestException( 218 'Failed to get status change ' + \ 219 SCardGetErrorMessage(hresult)) 220 221 # something changed! 222 else: 223 224 # check if we have to return a match, i.e. 225 # if no new card in inserted and there is a card found 226 # or if a new card is requested, and there is a change+present 227 for state in newstates: 228 readername, eventstate, atr = state 229 r, oldstate = readerstates[readername] 230 231 # the status can change on a card already inserted, e.g. 232 # unpowered, in use, ... 233 # if a new card is requested, clear the state changed bit 234 # if the card was already inserted and is still inserted 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 # update state dictionary 256 readerstates[readername] = (readername, eventstate) 257 258 if evt.isSet(): 259 raise CardRequestTimeoutException()
260
261 - def waitforcardevent(self):
262 """Wait for card insertion or removal.""" 263 AbstractCardRequest.waitforcardevent(self) 264 presentcards = [] 265 evt = threading.Event() 266 267 # for non infinite timeout, a timer will signal the end of the time-out 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 # get status change until time-out, e.g. evt is set 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 # reinitialize at each iteration just in case a new reader appeared 288 readernames = self.getReaderNames() 289 for reader in readernames: 290 # create a dictionary entry for new readers 291 if not reader in readerstates: 292 readerstates[reader] = (reader, SCARD_STATE_UNAWARE) 293 # remove dictionary entry for readers that disappeared 294 for oldreader in list(readerstates.keys()): 295 if oldreader not in readernames: 296 del readerstates[oldreader] 297 298 # get status change 299 if {} != readerstates: 300 hresult, newstates = SCardGetStatusChange( 301 self.hcontext, 0, list(readerstates.values())) 302 else: 303 hresult = 0 304 newstates = [] 305 306 # time-out 307 if SCARD_E_TIMEOUT == hresult: 308 if evt.isSet(): 309 raise CardRequestTimeoutException() 310 311 # the reader was unplugged during the loop 312 elif SCARD_E_UNKNOWN_READER == hresult: 313 pass 314 315 # some error happened 316 elif 0 != hresult: 317 timer.cancel() 318 raise CardRequestException( 319 'Failed to get status change ' + \ 320 SCardGetErrorMessage(hresult)) 321 322 # something changed! 323 else: 324 timer.cancel() 325 for state in newstates: 326 readername, eventstate, atr = state 327 r, oldstate = readerstates[readername] 328 329 # the status can change on a card already inserted, e.g. 330 # unpowered, in use, ... Clear the state changed bit if 331 # the card was already inserted and is still inserted 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