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-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   
37 -def signalEvent(evt, isInfinite):
38 if not isInfinite: 39 evt.set()
40 41
42 -class PCSCCardRequest(AbstractCardRequest):
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 # polling interval in s for SCardGetStatusChange 69 self.pollinginterval = 0.1 70 71 # if timeout is None, translate to scard.INFINITE 72 if None == self.timeout: 73 self.timeout = INFINITE 74 # otherwise, from seconds to milliseconds 75 else: 76 self.timeout = int(self.timeout) 77 78 self.hcontext = PCSCContext().getContext()
79
80 - def getReaderNames(self):
81 """Returns the list or PCSC readers on which to wait for cards.""" 82 83 # get inserted readers 84 hresult, pcscreaders = SCardListReaders(self.hcontext, []) 85 if 0 != hresult and SCARD_E_NO_READERS_AVAILABLE != hresult: 86 raise ListReadersException(hresult) 87 88 readers = [] 89 90 # if no readers asked, use all inserted readers 91 if None == self.readersAsked: 92 readers = pcscreaders 93 94 # otherwise use only the asked readers that are inserted 95 else: 96 for reader in self.readersAsked: 97 if not isinstance(reader, type("")): 98 reader = str(reader) 99 if reader in pcscreaders: 100 readers = readers + [reader] 101 102 return readers
103
104 - def waitforcard(self):
105 """Wait for card insertion and returns a card service.""" 106 AbstractCardRequest.waitforcard(self) 107 cardfound = False 108 109 # for non infinite timeout, a timer will signal the end of the time-out by setting the evt event 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 # create a dictionary entry for new readers 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 # remove dictionary entry for readers that disappeared 125 for oldreader in readerstates.keys(): 126 if oldreader not in readernames: 127 del readerstates[oldreader] 128 129 # call SCardGetStatusChange only if we have some readers 130 if {} != readerstates: 131 hresult, newstates = SCardGetStatusChange(self.hcontext, 0, readerstates.values()) 132 else: 133 hresult = 0 134 newstates = [] 135 136 # we can expect normally time-outs or reader disappearing just before the call 137 # otherwise, raise execption on error 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 # in case of timeout or reader disappearing, the content of the states is useless 142 # in which case we clear the changed bit 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 # update readerstate 148 for state in newstates: 149 readername, eventstate, atr = state 150 readerstates[readername] = (readername, eventstate) 151 152 # if a new card is not requested, just return the first available 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 # create a dictionary entry for new readers 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 # remove dictionary entry for readers that disappeared 179 for oldreader in readerstates.keys(): 180 if oldreader not in readernames: 181 del readerstates[oldreader] 182 183 # wait for card insertion 184 if {} != readerstates: 185 hresult, newstates = SCardGetStatusChange(self.hcontext, 0, readerstates.values()) 186 else: 187 hresult = SCARD_E_TIMEOUT 188 newstates = [] 189 190 # time-out 191 if SCARD_E_TIMEOUT == hresult: 192 if evt.isSet(): 193 raise CardRequestTimeoutException() 194 195 # reader vanished before or during the call 196 elif SCARD_E_UNKNOWN_READER == hresult: 197 pass 198 199 # some error happened 200 elif 0 != hresult: 201 timer.cancel() 202 raise CardRequestException('Failed to get status change ' + SCardGetErrorMessage(hresult)) 203 204 # something changed! 205 else: 206 207 # check if we have to return a match, i.e. 208 # if no new card in inserted and there is a card found 209 # or if a new card is requested, and there is a change+present 210 for state in newstates: 211 readername, eventstate, atr = state 212 r, oldstate = readerstates[readername] 213 214 # the status can change on a card already inserted, e.g. 215 # unpowered, in use, ... 216 # if a new card is requested, clear the state changed bit if 217 # the card was already inserted and is still inserted 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 # update state dictionary 232 readerstates[readername] = (readername, eventstate) 233 234 if evt.isSet(): 235 raise CardRequestTimeoutException()
236
237 - def waitforcardevent(self):
238 """Wait for card insertion or removal.""" 239 AbstractCardRequest.waitforcardevent(self) 240 presentcards = [] 241 evt = threading.Event() 242 243 # for non infinite timeout, a timer will signal the end of the time-out 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 # get status change until time-out, e.g. evt is set 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 # reinitialize at each iteration just in case a new reader appeared 263 readernames = self.getReaderNames() 264 for reader in readernames: 265 # create a dictionary entry for new readers 266 if not readerstates.has_key(reader): 267 readerstates[reader] = (reader, SCARD_STATE_UNAWARE) 268 # remove dictionary entry for readers that disappeared 269 for oldreader in readerstates.keys(): 270 if oldreader not in readernames: 271 del readerstates[oldreader] 272 273 # get status change 274 if {} != readerstates: 275 hresult, newstates = SCardGetStatusChange(self.hcontext, 0, readerstates.values()) 276 else: 277 hresult = 0 278 newstates = [] 279 280 # time-out 281 if SCARD_E_TIMEOUT == hresult: 282 if evt.isSet(): 283 raise CardRequestTimeoutException() 284 285 # the reader was unplugged during the loop 286 elif SCARD_E_UNKNOWN_READER == hresult: 287 pass 288 289 # some error happened 290 elif 0 != hresult: 291 timer.cancel() 292 raise CardRequestException('Failed to get status change ' + SCardGetErrorMessage(hresult)) 293 294 # something changed! 295 else: 296 timer.cancel() 297 for state in newstates: 298 readername, eventstate, atr = state 299 r, oldstate = readerstates[readername] 300 301 # the status can change on a card already inserted, e.g. 302 # unpowered, in use, ... Clear the state changed bit if 303 # the card was already inserted and is still inserted 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