Package phones :: Module com_brew
[hide private]
[frames] | no frames]

Source Code for Module phones.com_brew

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 
   4  ### 
   5  ### This program is free software; you can redistribute it and/or modify 
   6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   7  ### 
   8  ### $Id: com_brew.py 4783 2010-01-14 00:09:41Z djpham $ 
   9   
  10  """Implements the "Brew" filesystem protocol""" 
  11   
  12  import os 
  13  import p_brew 
  14  import time 
  15  import cStringIO 
  16  import com_phone 
  17  import prototypes 
  18  import common 
  19   
20 -class BrewNotSupported(Exception):
21 """This phone not supported""" 22 pass
23
24 -class BrewCommandException(Exception):
25 - def __init__(self, errnum, str=None):
26 if str is None: 27 str="Brew Error 0x%02x" % (errnum,) 28 Exception.__init__(self, str) 29 self.errnum=errnum
30
31 -class BrewNoMoreEntriesException(BrewCommandException):
32 - def __init__(self, errnum=0x1c):
33 BrewCommandException.__init__(self, errnum, "No more directory entries")
34
35 -class BrewNoSuchDirectoryException(BrewCommandException):
36 - def __init__(self, errnum=0x08):
37 BrewCommandException.__init__(self, errnum, "No such directory")
38
39 -class BrewNoSuchFileException(BrewCommandException):
40 - def __init__(self, errnum=0x06):
41 BrewCommandException.__init__(self, errnum, "No such file")
42
43 -class BrewBadPathnameException(BrewCommandException):
44 - def __init__(self, errnum=0x1a):
45 BrewCommandException.__init__(self, errnum, "Bad pathname")
46
47 -class BrewFileLockedException(BrewCommandException):
48 - def __init__(self, errnum=0x0b):
49 BrewCommandException.__init__(self, errnum, "File is locked")
50
51 -class BrewNameTooLongException(BrewCommandException):
52 - def __init__(self, errnum=0x0d):
53 BrewCommandException.__init__(self, errnum, "Name is too long")
54
55 -class BrewDirectoryExistsException(BrewCommandException):
56 - def __init__(self, errnum=0x07):
57 BrewCommandException.__init__(self, errnum, "Directory already exists")
58
59 -class BrewBadBrewCommandException(BrewCommandException):
60 - def __init__(self, errnum=0x100):
61 BrewCommandException.__init__(self, errnum, "The phone did not recognise the brew command")
62
63 -class BrewMalformedBrewCommandException(BrewCommandException):
64 - def __init__(self, errnum=0x101):
65 BrewCommandException.__init__(self, errnum, "The parameters in the last brew command were invalid")
66
67 -class BrewAccessDeniedException(BrewCommandException):
68 - def __init__(self, errnum=0x04, filename=None):
69 BrewCommandException.__init__(self, errnum, "Access Denied. Access to the file/directory may be blocked on this phone")
70
71 -class BrewFileSystemFullException(BrewCommandException):
72 - def __init__(self, errnum=0x16, filename=None):
73 BrewCommandException.__init__(self, errnum, "The phone has run out of space to store any more files")
74
75 -class BrewStatFileException(BrewCommandException):
76 - def __init__(self, errnum, filename):
77 BrewCommandException.__init__(self, errnum, 78 "Stat File %s errno %d"%(filename, errnum))
79 80 81 modeignoreerrortypes=com_phone.modeignoreerrortypes+(BrewCommandException,common.CommsDataCorruption) 82
83 -class _DirCache:
84 """This is a class that lets you do various filesystem manipulations and 85 it remembers the data. Typical usage would be if you make changes to 86 files (adding, removing, rewriting) and then have to keep checking if 87 files exist, add sizes etc. This class saves the hassle of rereading 88 the directory every single time. Note that it will only see changes 89 you make via this class. If you go directly to the Brew class then 90 those won't be seen. 91 """
92 - def __init__(self, target):
93 "@param target: where operations should be done after recording them here" 94 self.__target=target 95 self.__cache={}
96
97 - def rmfile(self, filename):
98 res=self.__target.rmfile(filename) 99 node=self._getdirectory(brewdirname(filename)) 100 if node is None: # we didn't have it 101 return 102 del node[brewbasename(filename)] 103 return res
104
105 - def stat(self, filename):
106 node=self._getdirectory(brewdirname(filename), ensure=True) 107 return node.get(brewbasename(filename), None)
108
109 - def readfile(self, filename):
110 node=self._getdirectory(brewdirname(filename), ensure=True) 111 file=node.get(brewbasename(filename), None) 112 if file is None: 113 raise BrewNoSuchFileException() 114 # This class only populates the 'data' portion of the file obj when needed 115 data=file.get('data', None) 116 if data is None: 117 data=self.__target.getfilecontents(filename) 118 file['data']=data 119 return data
120
121 - def writefile(self, filename, contents):
122 res=self.__target.writefile(filename, contents) 123 node=self._getdirectory(brewdirname(filename), ensure=True) 124 # we can't put the right date in since we have no idea 125 # what the timezone (or the time for that matter) on the 126 # phone is 127 stat=node.get(brewbasename(filename), {'name': filename, 'type': 'file', 'date': (0, "")}) 128 stat['size']=len(contents) 129 stat['data']=contents 130 node[brewbasename(filename)]=stat 131 return res
132
133 - def _getdirectory(self, dirname, ensure=False):
134 if not ensure: 135 return self.__cache.get(dirname, None) 136 node=self.__cache.get(dirname, None) 137 if node is not None: return node 138 node={} 139 fs=self.__target.getfilesystem(dirname) 140 for filename in fs.keys(): 141 node[brewbasename(filename)]=fs[filename] 142 self.__cache[dirname]=node 143 return node
144
145 -class DebugBrewProtocol:
146 """ Emulate a phone file system using a local file system. This is used 147 when you may not have access to a physical phone, but have a copy of its 148 file system. 149 """ 150 MODEBREW="modebrew" 151 _fs_path=''
152 - def __init__(self):
153 pass
154 - def getfirmwareinformation(self):
155 self.log("Getting firmware information")
156 - def explore0c(self):
157 self.log("Trying stuff with command 0x0c")
158 - def offlinerequest(self, reset=False, delay=0):
159 self.log("Taking phone offline") 160 if reset: 161 self.log("Resetting phone")
162 - def modemmoderequest(self):
163 self.log("Attempting to put phone in modem mode")
164 - def mkdir(self, name):
165 self.log("Making directory '"+name+"'") 166 os.mkdir(os.path.join(self._fs_path, name))
167 - def mkdirs(self, directory):
168 if len(directory)<1: 169 return 170 dirs=directory.split('/') 171 for i in range(0,len(dirs)): 172 try: 173 self.mkdir("/".join(dirs[:i+1])) # basically mkdir -p 174 except: 175 pass
176 - def rmdir(self,name):
177 self.log("Deleting directory '"+name+"'") 178 try: 179 os.rmdir(os.path.join(self._fs_path, name)) 180 except: 181 # convert to brew exception 182 raise BrewNoSuchDirectoryException
183 - def rmfile(self,name):
184 self.log("Deleting file '"+name+"'") 185 try: 186 os.remove(os.path.join(self._fs_path, name)) 187 except: 188 # convert to brew exception 189 raise BrewNoSuchFileException
190 - def rmdirs(self, path):
191 self.progress(0,1, "Listing child files and directories") 192 all=self.getfilesystem(path, 100) 193 keys=all.keys() 194 keys.sort() 195 keys.reverse() 196 count=0 197 for k in keys: 198 self.progress(count, len(keys), "Deleting "+k) 199 count+=1 200 if all[k]['type']=='directory': 201 self.rmdir(k) 202 else: 203 self.rmfile(k) 204 self.rmdir(path)
205
206 - def listfiles(self, dir=''):
207 results={} 208 self.log("Listing files in dir: '"+dir+"'") 209 results={} 210 _pwd=os.path.join(self._fs_path, dir) 211 for _root,_dir,_file in os.walk(_pwd): 212 break 213 try: 214 for f in _file: 215 _stat=os.stat(os.path.join(_pwd, f)) 216 _date=_stat[8] 217 _name=dir+'/'+f 218 _timestr='' 219 try: 220 # date is not always present in filesystem 221 _timestr=time.strftime("%x %X", time.gmtime(_date)) 222 except: 223 pass 224 results[_name]={ 'name': _name, 'type': 'file', 'size': _stat[6], 225 'date': (_date, _timestr) } 226 except: 227 pass # will happen if the directory does not exist 228 return results
229
230 - def listsubdirs(self, dir='', recurse=0):
231 results={} 232 self.log("Listing subdirs in dir: '"+dir+"'") 233 _pwd=os.path.join(self._fs_path, dir) 234 for _root,_dir,_file in os.walk(_pwd): 235 break 236 for d in _dir: 237 if len(dir): 238 d=dir+"/"+d 239 results[d]={ 'name': d, 'type': 'directory' } 240 if recurse>0: 241 results.update(self.listsubdirs(d, recurse-1)) 242 return results
243
244 - def getfilesystem(self, dir="", recurse=0):
245 results={} 246 _file=[] 247 _dir=[] 248 self.log("Listing dir '"+dir+"'") 249 _pwd=os.path.join(self._fs_path, dir) 250 for _root,_dir,_file in os.walk(_pwd): 251 break 252 for f in _file: 253 _stat=os.stat(os.path.join(_pwd, f)) 254 _date=_stat[8] 255 _name=dir+'/'+f 256 _timestr='' 257 try: 258 # date is not always present in filesystem 259 _timestr=time.strftime("%x %X", time.gmtime(_date)) 260 except: 261 pass 262 results[_name]={ 'name': _name, 'type': 'file', 'size': _stat[6], 263 'date': (_date, _timestr) } 264 for d in _dir: 265 _name=dir+'/'+d 266 results[_name]={ 'name': _name, 'type': 'directory' } 267 if recurse>0: 268 results.update(self.getfilesystem(_name, recurse-1)) 269 return results
270
271 - def statfile(self, name):
272 try: 273 _stat=os.stat(os.path.join(self._fs_path, name)) 274 _date=_stat[8] 275 results={ 'name': name, 'type': 'file', 'size': _stat[6], 'datevalue': 0x0DEB0DEB, 276 'date': (_date, 277 time.strftime("%x %X", time.gmtime(_date))) } 278 return results 279 except: 280 # File does not exist, bail 281 return None
282
283 - def writefile(self, name, contents):
284 self.log("Writing file '"+name+"' bytes "+`len(contents)`) 285 file(os.path.join(self._fs_path, name), 'wb').write(contents)
286
287 - def getfilecontents(self, name, use_cache=False):
288 self.log("Getting file contents '"+name+"'") 289 try: 290 if name[0]=='/': 291 return file(os.path.join(self._fs_path, name[1:]), 'rb').read() 292 return file(os.path.join(self._fs_path, name), 'rb').read() 293 except: 294 raise BrewNoSuchFileException
295
296 - def get_brew_esn(self):
297 # fake an ESN for debug mode 298 return "DEBUGESN"
299 - def _setmodebrew(self):
300 self.log('_setmodebrew: in mode BREW') 301 return True
302 - def sendbrewcommand(self, request, responseclass, callsetmode=True):
303 return NotImplementedError
304 - def log(self, s):
305 print s
306 - def logdata(self, s, data, klass=None):
307 print s
308
309 - def exists(self, path):
310 return os.path.exists(os.path.join(self._fs_path, path))
311 312 DirCache=_DirCache
313
314 -class RealBrewProtocol:
315 "Talk to a phone using the 'brew' protocol" 316 317 MODEBREW="modebrew" 318 brewterminator="\x7e" 319 320 # phone uses Jan 6, 1980 as epoch. Python uses Jan 1, 1970. This is difference 321 _brewepochtounix=315964800 322
323 - def __init__(self):
324 # reset default encoding 325 p_brew.PHONE_ENCODING=p_brew.DEFAULT_PHONE_ENCODING
326
327 - def getfirmwareinformation(self):
328 self.log("Getting firmware information") 329 req=p_brew.firmwarerequest() 330 res=self.sendbrewcommand(req, p_brew.firmwareresponse)
331
332 - def get_brew_esn(self):
333 # return the ESN of this phone 334 return '%0X'%self.sendbrewcommand(p_brew.ESN_req(), 335 p_brew.ESN_resp).esn
336
337 - def explore0c(self):
338 self.log("Trying stuff with command 0x0c") 339 req=p_brew.testing0crequest() 340 res=self.sendbrewcommand(req, p_brew.testing0cresponse)
341
342 - def offlinerequest(self, reset=False, delay=0):
343 time.sleep(delay) 344 req=p_brew.setmoderequest() 345 req.request=1 346 self.log("Taking phone offline") 347 self.sendbrewcommand(req, p_brew.setmoderesponse) 348 time.sleep(delay) 349 if reset: 350 req=p_brew.setmoderequest() 351 req.request=2 352 self.log("Resetting phone") 353 self.sendbrewcommand(req, p_brew.setmoderesponse)
354
355 - def modemmoderequest(self):
356 # Perhaps we should modify sendbrewcommand to have an option to 357 # not be picky about response. 358 self.log("Attempting to put phone in modem mode") 359 req=p_brew.setmodemmoderequest() 360 buffer=prototypes.buffer() 361 req.writetobuffer(buffer, logtitle="modem mode request") 362 data=buffer.getvalue() 363 data=common.pppescape(data+common.crcs(data))+common.pppterminator 364 self.comm.write(data) 365 # Response could be text or a packet 366 self.comm.readsome(numchars=5) 367 self.mode=self.MODENONE # Probably should add a modem mode
368
369 - def mkdir(self, name):
370 self.log("Making directory '"+name+"'") 371 if self.isdir(name): 372 raise BrewDirectoryExistsException 373 req=p_brew.mkdirrequest() 374 req.dirname=name 375 try: 376 self.sendbrewcommand(req, p_brew.mkdirresponse) 377 except BrewDirectoryExistsException: 378 # sometime the phone returns this, which is OK 379 pass
380
381 - def mkdirs(self, directory):
382 if len(directory)<1: 383 return 384 dirs=directory.split('/') 385 for i in range(0,len(dirs)): 386 try: 387 self.mkdir("/".join(dirs[:i+1])) # basically mkdir -p 388 except: 389 pass
390 391
392 - def rmdir(self,name):
393 self.log("Deleting directory '"+name+"'") 394 req=p_brew.rmdirrequest() 395 req.dirname=name 396 self.sendbrewcommand(req, p_brew.rmdirresponse)
397
398 - def rmfile(self,name):
399 self.log("Deleting file '"+name+"'") 400 req=p_brew.rmfilerequest() 401 req.filename=name 402 self.sendbrewcommand(req, p_brew.rmfileresponse) 403 file_cache.clear(name)
404
405 - def rmdirs(self, path):
406 self.progress(0,1, "Listing child files and directories") 407 all=self.getfilesystem(path, 100) 408 keys=all.keys() 409 keys.sort() 410 keys.reverse() 411 count=0 412 for k in keys: 413 self.progress(count, len(keys), "Deleting "+k) 414 count+=1 415 if all[k]['type']=='directory': 416 self.rmdir(k) 417 else: 418 self.rmfile(k) 419 self.rmdir(path)
420
421 - def exists(self, path):
422 # Return True if this path (dir/file) exists 423 return bool(self.statfile(path))
424
425 - def isdir(self, path):
426 # Return True if path refers to an existing dir 427 if not self.statfile(path): 428 # if it doesn't exist, bail 429 return False 430 # just try a list dirs command and see if it bombs out 431 req=p_brew.listdirectoriesrequest(dirname=path) 432 try: 433 self.sendbrewcommand(req, p_brew.listdirectoriesresponse) 434 except (BrewCommandException, BrewBadPathnameException, 435 BrewNoSuchDirectoryException, 436 BrewAccessDeniedException): 437 return False 438 except: 439 if __debug__: 440 raise 441 return False 442 return True
443
444 - def isfile(self, filename):
445 # return True if filename is a file 446 if not self.statfile(filename): 447 return False 448 # if it exists and not a dir, then it must be a file! 449 return not self.isdir(filename)
450
451 - def basename(self, path):
452 # return the basename of the path, does not check on whether the path 453 # exists. 454 _dirs=[x for x in path.split('/') if x] 455 if _dirs: 456 return _dirs[-1] 457 return ''
458
459 - def dirname(self, filename):
460 # return the dir name of the filename, does not check on whether 461 # the file exists. 462 _dirs=[x for x in filename.split('/') if x] 463 if len(_dirs)<2: 464 # either / or /name 465 return '/' 466 return '/'.join(_dirs[:-1])
467
468 - def join(self, *args):
469 # join the dir/file components and return the full path name 470 return '/'.join([x.strip('/') for x in args if x])
471
472 - def listsubdirs(self, dir='', recurse=0):
473 results={} 474 self.log("Listing subdirs in dir: '"+dir+"'") 475 self.log("X recurse="+`recurse`) 476 477 req=p_brew.listdirectoryrequest() 478 req.dirname=dir 479 for i in xrange(10000): 480 try: 481 req.entrynumber=i 482 res=self.sendbrewcommand(req,p_brew.listdirectoryresponse) 483 # sometimes subdir can already include the parent directory 484 f=res.subdir.rfind("/") 485 if f>=0: 486 subdir=res.subdir[f+1:] 487 else: 488 subdir=res.subdir 489 if len(dir): 490 subdir=dir+"/"+subdir 491 self.log("subdir="+subdir) 492 results[subdir]={ 'name': subdir, 'type': 'directory' } 493 except BrewNoMoreEntriesException: 494 break 495 except (BrewBadPathnameException, BrewAccessDeniedException): 496 self.log('Failed to list dir '+dir) 497 return {} 498 if recurse: 499 for k,_subdir in results.items(): 500 results.update(self.listsubdirs(_subdir['name'], recurse-1)) 501 return results
502
503 - def hassubdirs(self, dir=''):
504 self.log('Checking for subdirs in dir: "'+dir+'"') 505 req=p_brew.listdirectoryrequest() 506 req.dirname=dir 507 req.entrynumber=0 508 try: 509 res=self.sendbrewcommand(req,p_brew.listdirectoryresponse) 510 # there's at least one subdir 511 return True 512 except BrewNoMoreEntriesException: 513 return False 514 except: 515 if __debug__: 516 raise 517 return False
518
519 - def listfiles(self, dir=''):
520 results={} 521 self.log("Listing files in dir: '"+dir+"'") 522 523 _broken_date=hasattr(self.protocolclass, 'broken_filelist_date') and \ 524 self.protocolclass.broken_filelist_date 525 req=p_brew.listfilerequest() 526 req.dirname=dir 527 # self.log("file listing 0x0b command") 528 for i in xrange(10000): 529 try: 530 req.entrynumber=i 531 res=self.sendbrewcommand(req,p_brew.listfileresponse) 532 results[res.filename]={ 'name': res.filename, 'type': 'file', 533 'size': res.size } 534 if not _broken_date: 535 if res.date<=0: 536 results[res.filename]['date']=(0, "") 537 else: 538 try: 539 date=res.date+self._brewepochtounix 540 results[res.filename]['date']=(date, time.strftime("%x %X", time.localtime(date))) 541 except: 542 # invalid date - see SF bug #833517 543 results[res.filename]['date']=(0, "") 544 except BrewNoMoreEntriesException: 545 break 546 except (BrewBadPathnameException, BrewAccessDeniedException): 547 self.log('Failed to list files in dir '+dir) 548 return {} 549 if _broken_date: 550 for _key,_entry in results.items(): 551 _stat=self.statfile(_key) 552 if _stat: 553 _entry['date']=_stat.get('date', (0, '')) 554 else: 555 _entry['date']=(0, '') 556 return results
557
558 - def getfilesystem(self, dir="", recurse=0):
559 self.log("Getting file system in dir '"+dir+"'") 560 results=self.listsubdirs(dir) 561 subdir_list=[x['name'] for k,x in results.items()] 562 results.update(self.listfiles(dir)) 563 if recurse: 564 for _subdir in subdir_list: 565 results.update(self.getfilesystem(_subdir, recurse-1)) 566 return results
567
568 - def statfile(self, name):
569 # return the status of the file 570 try: 571 self.log('stat file '+name) 572 req=p_brew.statfilerequest() 573 req.filename=name 574 res=self.sendbrewcommand(req, p_brew.statfileresponse) 575 results={ 'name': name, 'type': 'file', 'size': res.size, 576 'datevalue': res.date } 577 if res.date<=0: 578 results['date']=(0, '') 579 else: 580 try: 581 date=res.date+self._brewepochtounix 582 results['date']=(date, time.strftime("%x %X", time.localtime(date))) 583 except: 584 # invalid date - see SF bug #833517 585 results['date']=(0, '') 586 return results 587 except (BrewCommandException, BrewNoSuchFileException): 588 # File does not exist, bail 589 return None 590 except: 591 # something happened, we don't have any info on this file 592 if __debug__: 593 raise 594 return None
595
596 - def setfileattr(self, filename, date):
597 # sets the date and time of the file on the phone 598 self.log('set file date '+filename +`date`) 599 req=p_brew.setfileattrrequest() 600 # convert date to GPS time 601 req.date=date-self._brewepochtounix 602 req.filename=filename 603 self.sendbrewcommand(req, p_brew.setfileattrresponse)
604
605 - def writefile(self, name, contents):
606 start=time.time() 607 self.log("Writing file '"+name+"' bytes "+`len(contents)`) 608 desc="Writing "+name 609 req=p_brew.writefilerequest() 610 req.filesize=len(contents) 611 req.data=contents[:0x100] 612 req.filename=name 613 self.sendbrewcommand(req, p_brew.writefileresponse) 614 # do remaining blocks 615 numblocks=len(contents)/0x100 616 count=0 617 for offset in range(0x100, len(contents), 0x100): 618 req=p_brew.writefileblockrequest() 619 count+=1 620 if count>=0x100: count=1 621 if count % 5==0: 622 self.progress(offset>>8,numblocks,desc) 623 req.blockcounter=count 624 req.thereismore=offset+0x100<len(contents) 625 block=contents[offset:] 626 l=min(len(block), 0x100) 627 block=block[:l] 628 req.data=block 629 self.sendbrewcommand(req, p_brew.writefileblockresponse) 630 end=time.time() 631 if end-start>3: 632 self.log("Wrote "+`len(contents)`+" bytes at "+`int(len(contents)/(end-start))`+" bytes/second")
633 634
635 - def getfilecontents(self, file, use_cache=False):
636 if use_cache: 637 node=self.statfile(file) 638 if node and file_cache.hit(file, node['date'][0], node['size']): 639 self.log('Reading from cache: '+file) 640 _data=file_cache.data(file) 641 if _data: 642 return _data 643 self.log('Cache file corrupted and discarded') 644 645 start=time.time() 646 self.log("Getting file contents '"+file+"'") 647 desc="Reading "+file 648 649 data=cStringIO.StringIO() 650 651 req=p_brew.readfilerequest() 652 req.filename=file 653 654 res=self.sendbrewcommand(req, p_brew.readfileresponse) 655 656 filesize=res.filesize 657 data.write(res.data) 658 659 counter=0 660 while res.thereismore: 661 counter+=1 662 if counter>0xff: 663 counter=0x01 664 if counter%5==0: 665 self.progress(data.tell(), filesize, desc) 666 req=p_brew.readfileblockrequest() 667 req.blockcounter=counter 668 res=self.sendbrewcommand(req, p_brew.readfileblockresponse) 669 data.write(res.data) 670 671 self.progress(1,1,desc) 672 673 data=data.getvalue() 674 675 # give the download speed if we got a non-trivial amount of data 676 end=time.time() 677 if end-start>3: 678 self.log("Read "+`filesize`+" bytes at "+`int(filesize/(end-start))`+" bytes/second") 679 680 if filesize!=len(data): 681 self.log("expected size "+`filesize`+" actual "+`len(data)`) 682 self.raisecommsexception("Brew file read is incorrect size", common.CommsDataCorruption) 683 if use_cache and node: 684 file_cache.add(file, node.get('date', [0])[0], data) 685 return data
686 687 DirCache=_DirCache 688
689 - def _setmodebrew(self):
690 req=p_brew.memoryconfigrequest() 691 respc=p_brew.memoryconfigresponse 692 try: 693 self.sendbrewcommand(req, respc, callsetmode=False) 694 return True 695 except modeignoreerrortypes: 696 pass 697 698 for baud in 0, 38400,115200: 699 if baud: 700 if not self.comm.setbaudrate(baud): 701 continue 702 try: 703 self.sendbrewcommand(req, respc, callsetmode=False) 704 return True 705 except modeignoreerrortypes: 706 pass 707 708 # send AT$CDMG at various speeds 709 for baud in (0, 115200, 19200, 230400): 710 if baud: 711 if not self.comm.setbaudrate(baud): 712 continue 713 print "Baud="+`baud` 714 715 try: 716 for line in self.comm.sendatcommand("+GMM"): 717 if line.find("SPH-A700")>0: 718 raise BrewNotSupported("This phone is not supported by BitPim", self.desc) 719 except modeignoreerrortypes: 720 self.log("No response to AT+GMM") 721 except: 722 print "GMM Exception" 723 self.mode=self.MODENONE 724 self.comm.shouldloop=True 725 raise 726 727 try: 728 self.comm.write("AT$QCDMG\r\n") 729 except: 730 # some issue during writing such as user pulling cable out 731 self.mode=self.MODENONE 732 self.comm.shouldloop=True 733 raise 734 try: 735 # if we got OK back then it was success 736 if self.comm.readsome().find("OK")>=0: 737 break 738 except modeignoreerrortypes: 739 self.log("No response to setting QCDMG mode") 740 741 # verify if we are in DM mode 742 for baud in 0,38400,115200: 743 if baud: 744 if not self.comm.setbaudrate(baud): 745 continue 746 try: 747 self.sendbrewcommand(req, respc, callsetmode=False) 748 return True 749 except modeignoreerrortypes: 750 pass 751 return False
752
753 - def sendbrewcommand(self, request, responseclass, callsetmode=True):
754 if callsetmode: 755 self.setmode(self.MODEBREW) 756 buffer=prototypes.buffer() 757 request.writetobuffer(buffer, logtitle="sendbrewcommand") 758 data=buffer.getvalue() 759 data=common.pppescape(data+common.crcs(data))+common.pppterminator 760 firsttwo=data[:2] 761 try: 762 # we logged above, and below 763 data=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False) 764 except modeignoreerrortypes: 765 self.mode=self.MODENONE 766 self.raisecommsdnaexception("manipulating the filesystem") 767 self.comm.success=True 768 origdata=data 769 770 # sometimes there is junk at the begining, eg if the user 771 # turned off the phone and back on again. So if there is more 772 # than one 7e in the escaped data we should start after the 773 # second to last one 774 d=data.rfind(common.pppterminator,0,-1) 775 if d>=0: 776 self.log("Multiple packets in data - taking last one starting at "+`d+1`) 777 self.logdata("Original data", origdata, None) 778 data=data[d+1:] 779 780 # turn it back to normal 781 data=common.pppunescape(data) 782 783 # take off crc and terminator 784 crc=data[-3:-1] 785 data=data[:-3] 786 # check the CRC at this point to see if we might have crap at the beginning 787 calccrc=common.crcs(data) 788 if calccrc!=crc: 789 # sometimes there is other crap at the begining 790 d=data.find(firsttwo) 791 if d>0: 792 self.log("Junk at begining of packet, data at "+`d`) 793 self.logdata("Original data", origdata, None) 794 self.logdata("Working on data", data, None) 795 data=data[d:] 796 # recalculate CRC without the crap 797 calccrc=common.crcs(data) 798 # see if the crc matches now 799 if calccrc!=crc: 800 self.logdata("Original data", origdata, None) 801 self.logdata("Working on data", data, None) 802 raise common.CommsDataCorruption("Brew packet failed CRC check", self.desc) 803 804 # log it 805 self.logdata("brew response", data, responseclass) 806 807 if firsttwo=="Y\x0c" and data==firsttwo: 808 # we are getting an echo - the modem port has been selected 809 # instead of diagnostics port 810 raise common.CommsWrongPort("The port you are using is echoing data back, and is not valid for Brew data. Most likely you have selected the modem interface when you should be using the diagnostic interface.", self.desc) 811 812 # look for errors 813 if data[0]=="Y" and data[2]!="\x00": # Y is 0x59 which is brew command prefix 814 err=ord(data[2]) 815 if err==0x1c: 816 raise BrewNoMoreEntriesException() 817 if err==0x08: 818 raise BrewNoSuchDirectoryException() 819 if err==0x06: 820 raise BrewNoSuchFileException() 821 if err==0x1a: 822 raise BrewBadPathnameException() 823 if err==0x0b: 824 raise BrewFileLockedException() 825 if err==0x0d: 826 raise BrewNameTooLongException() 827 if err==0x07: 828 raise BrewDirectoryExistsException() 829 if err==0x04: 830 raise BrewAccessDeniedException() 831 if err==0x16: 832 raise BrewFileSystemFullException() 833 raise BrewCommandException(err) 834 # Starting with the vx8100/9800 verizon started to block access to some file and directories 835 # it reports a bad command packet as the error when it really means access denied 836 if ord(data[0])==0x13: 837 if firsttwo[0]=="Y" or firsttwo[0]=="\x4b": # brew command 838 raise BrewAccessDeniedException() 839 else: 840 raise BrewBadBrewCommandException() 841 if ord(data[0])==0x14: 842 raise BrewMalformedBrewCommandException() 843 844 # access denied error 845 if ord(data[0])==0x4b and ord(data[2])==0x1c: 846 raise BrewAccessDeniedException() 847 848 # parse data 849 buffer=prototypes.buffer(data) 850 res=responseclass() 851 try: 852 res.readfrombuffer(buffer, autolog=False) 853 except: 854 # we had an exception so log the data even if protocol log 855 # view is not available 856 self.log(formatpacketerrorlog("Error decoding response", origdata, data, responseclass)) 857 raise 858 return res
859
860 -class RealBrewProtocol2(RealBrewProtocol):
861 """Talk to a phone using the 'brew' protocol 862 This class uses the new filesystem commands which are supported 863 by newer qualcomm chipsets used in phones like the LG vx8100 864 """ 865
866 - def exists(self, name):
867 try: 868 self.statfile(name) 869 except BrewNoSuchFileException: 870 return False 871 return True
872
873 - def reconfig_directory(self):
874 # not sure how important this is or even what it really does 875 # but the product that was reverse engineered from sent this after 876 # rmdir and mkdir, although it seems to work without it on the 8100 877 req=p_brew.new_reconfigfilesystemrequest() 878 self.sendbrewcommand(req, p_brew.new_reconfigfilesystemresponse)
879
880 - def rmfile(self,name):
881 self.log("Deleting file '"+name+"'") 882 if self.exists(name): 883 req=p_brew.new_rmfilerequest() 884 req.filename=name 885 self.sendbrewcommand(req, p_brew.new_rmfileresponse) 886 file_cache.clear(name)
887
888 - def rmdir(self,name):
889 self.log("Deleting directory '"+name+"'") 890 if self.exists(name): 891 req=p_brew.new_rmdirrequest() 892 req.dirname=name 893 self.sendbrewcommand(req, p_brew.new_rmdirresponse) 894 self.reconfig_directory()
895
896 - def mkdir(self, name):
897 self.log("Making directory '"+name+"'") 898 if self.exists(name): 899 raise BrewDirectoryExistsException 900 req=p_brew.new_mkdirrequest() 901 req.dirname=name 902 self.sendbrewcommand(req, p_brew.new_mkdirresponse) 903 self.reconfig_directory()
904
905 - def openfile(self, name, mode, flags=p_brew.new_fileopen_flag_existing):
906 self.log("Open file '"+name+"'") 907 req=p_brew.new_openfilerequest(filename=name, mode=mode, 908 flags=flags) 909 res=self.sendbrewcommand(req, p_brew.new_openfileresponse) 910 return res.handle
911
912 - def closefile(self, handle):
913 self.log("Close file") 914 req=p_brew.new_closefilerequest(handle=handle) 915 self.sendbrewcommand(req, p_brew.new_closefileresponse)
916
917 - def writefile(self, name, contents):
918 start=time.time() 919 self.log("Writing file '"+name+"' bytes "+`len(contents)`) 920 desc="Writing "+name 921 size=len(contents) 922 exists=self.exists(name) 923 if exists: 924 info=self.statfile(name) 925 current_size=info['size'] 926 else: 927 current_size=0 928 try: 929 block_size = self.protocolclass.BREW_WRITE_SIZE 930 except AttributeError: 931 block_size = p_brew.BREW_WRITE_SIZE 932 # if the current file is longer than the new one we have to 933 # delete it because the write operation does not truncate it 934 if exists and size<current_size: 935 self.rmfile(name) 936 exists=False 937 if exists: 938 handle=self.openfile(name, p_brew.new_fileopen_mode_write, p_brew.new_fileopen_flag_existing) 939 else: 940 handle=self.openfile(name, p_brew.new_fileopen_mode_write, p_brew.new_fileopen_flag_create) 941 try: 942 remain=size 943 pos=0 944 count=0 945 while remain: 946 req=p_brew.new_writefilerequest() 947 req.handle=handle 948 if remain > block_size: 949 req.bytes=block_size 950 else: 951 req.bytes=remain 952 req.position=size-remain 953 req.data=contents[req.position:(req.position+req.bytes)] 954 count=(count&0xff)+1 955 if count % 5==0: 956 self.progress(req.position,size,desc) 957 res=self.sendbrewcommand(req, p_brew.new_writefileresponse) 958 if res.bytes!=req.bytes: 959 self.raisecommsexception("Brew file write error", common.CommsDataCorruption) 960 remain-=req.bytes 961 finally: # MUST close handle to file 962 self.closefile(handle) 963 self.progress(1,1,desc) 964 end=time.time() 965 if end-start>3: 966 self.log("Wrote "+`len(contents)`+" bytes at "+`int(len(contents)/(end-start))`+" bytes/second")
967
968 - def getfilecontents(self, file, use_cache=False):
969 node=self.statfile(file) 970 if use_cache: 971 if node and file_cache.hit(file, node['date'][0], node['size']): 972 self.log('Reading from cache: '+file) 973 _data=file_cache.data(file) 974 if _data: 975 return _data 976 self.log('Cache file corrupted and discarded') 977 try: 978 block_size = self.protocolclass.BREW_READ_SIZE 979 except AttributeError: 980 block_size = p_brew.BREW_READ_SIZE 981 start=time.time() 982 self.log("Getting file contents '"+file+"'") 983 desc="Reading "+file 984 data=cStringIO.StringIO() 985 handle=self.openfile(file, p_brew.new_fileopen_mode_read) 986 try: 987 filesize=node['size'] 988 read=0 989 counter=0 990 req=p_brew.new_readfilerequest(handle=handle, bytes=block_size) 991 while True: 992 counter=(counter&0xff)+1 993 if counter%5==0: 994 self.progress(data.tell(), filesize, desc) 995 req.position=read 996 res=self.sendbrewcommand(req, p_brew.new_readfileresponse) 997 if res.bytes: 998 data.write(res.data) 999 read+=res.bytes 1000 else: 1001 break 1002 if read==filesize: 1003 break 1004 finally: # MUST close handle to file 1005 self.closefile(handle) 1006 self.progress(1,1,desc) 1007 data=data.getvalue() 1008 # give the download speed if we got a non-trivial amount of data 1009 end=time.time() 1010 if end-start>3: 1011 self.log("Read "+`filesize`+" bytes at "+`int(filesize/(end-start))`+" bytes/second") 1012 if filesize!=len(data): 1013 self.log("expected size "+`filesize`+" actual "+`len(data)`) 1014 self.raisecommsexception("Brew file read is incorrect size", common.CommsDataCorruption) 1015 if use_cache and node: 1016 file_cache.add(file, node.get('date', [0])[0], data) 1017 return data
1018
1019 - def getfilecontents2(self, filename, start, size):
1020 # read and return data a block of data from the specified file 1021 try: 1022 block_size = self.protocolclass.BREW_READ_SIZE 1023 except AttributeError: 1024 block_size = p_brew.BREW_READ_SIZE 1025 self.log("Getting file contents2 '"+filename+"'") 1026 desc="Reading "+filename 1027 data=cStringIO.StringIO() 1028 handle=self.openfile(filename, p_brew.new_fileopen_mode_read) 1029 _readsize=start+size 1030 try: 1031 read=start 1032 counter=0 1033 while True: 1034 counter+=1 1035 if counter%5==0: 1036 self.progress(read, _readsize, desc) 1037 req=p_brew.new_readfilerequest() 1038 req.handle=handle 1039 req.bytes=block_size 1040 req.position=read 1041 res=self.sendbrewcommand(req, p_brew.new_readfileresponse) 1042 if res.bytes: 1043 data.write(res.data) 1044 read+=res.bytes 1045 else: 1046 break 1047 if read>=_readsize: 1048 break 1049 finally: # MUST close handle to file 1050 self.closefile(handle) 1051 self.progress(1,1,desc) 1052 return data.getvalue()[:size]
1053
1054 - def _get_dir_handle(self, dirname):
1055 # return the handle to the specified dir 1056 req=p_brew.new_opendirectoryrequest(dirname=dirname if dirname else "/") 1057 res=self.sendbrewcommand(req, p_brew.new_opendirectoryresponse) 1058 if res.handle: 1059 return res.handle 1060 # dir does not exist 1061 raise BrewNoSuchDirectoryException
1062
1063 - def _close_dir(self, handle):
1064 req=p_brew.new_closedirectoryrequest(handle=handle) 1065 res=self.sendbrewcommand(req, p_brew.new_closedirectoryresponse)
1066
1067 - def listsubdirs(self, dir='', recurse=0):
1068 self.log("Listing subdirs in dir: '"+dir+"'") 1069 self.log("X recurse="+`recurse`) 1070 return self.getfilesystem(dir, recurse, files=0)
1071
1072 - def listfiles(self, dir=''):
1073 self.log("Listing files in dir: '"+dir+"'") 1074 return self.getfilesystem(dir, recurse=0, directories=0)
1075
1076 - def getfilesystem(self, dir="", recurse=0, directories=1, files=1):
1077 results={} 1078 self.log("Listing dir '"+dir+"'") 1079 handle=self._get_dir_handle(dir) 1080 dirs={} 1081 count=0 1082 try: 1083 # get all the directory entries from the phone 1084 req=p_brew.new_listentryrequest(handle=handle) 1085 for i in xrange(1, 10000): 1086 req.entrynumber=i 1087 res=self.sendbrewcommand(req, p_brew.new_listentryresponse) 1088 if len(res.entryname) == 0: # signifies end of list 1089 break 1090 if len(dir): 1091 direntry=dir+"/"+res.entryname 1092 else: 1093 direntry=res.entryname 1094 if files and (res.type==0 or res.type == 0x0f): # file or special file 1095 results[direntry]={ 'name': direntry, 'type': 'file', 'size': res.size, 'special': res.type==0xf } 1096 try: 1097 if res.date<=0: 1098 results[direntry]['date']=(0, "") 1099 else: 1100 results[direntry]['date']=(res.date, time.strftime("%x %X", time.localtime(res.date))) 1101 except: 1102 results[direntry]['date']=(0, "") 1103 elif directories and (res.type and res.type != 0x0f): # directory 1104 results[direntry]={ 'name': direntry, 'type': 'directory' } 1105 if recurse>0: 1106 dirs[count]=direntry 1107 count+=1 1108 finally: # we MUST close the handle regardless or we wont be able to list the filesystem 1109 # reliably again without rebooting it 1110 self._close_dir(handle) 1111 # recurse the subdirectories 1112 for i in range(count): 1113 results.update(self.getfilesystem(dirs[i], recurse-1)) 1114 return results
1115
1116 - def statfile(self, name):
1117 # return the status of the file 1118 self.log('stat file '+name) 1119 req=p_brew.new_statfilerequest() 1120 req.filename=name 1121 res=self.sendbrewcommand(req, p_brew.new_statfileresponse) 1122 if res.error==2: # ENOENT 1123 raise BrewNoSuchFileException 1124 elif res.error==0x13: # ENODEV 1125 # locked system file. example: /dev.null 1126 raise BrewFileLockedException 1127 elif res.error != 0: 1128 raise BrewStatFileException(res.error, name) 1129 ## if res.error==2 or res.error==0x13 or res.error!=0: 1130 ## return None 1131 if res.type==1 or res.type==0x86: 1132 # files on external media have type 0x86 1133 results={ 'name': name, 'type': 'file', 'size': res.size } 1134 else: 1135 results={ 'name': name, 'type': 'directory' } 1136 try: 1137 if res.created_date<=0: 1138 results['date']=(0, '') 1139 else: 1140 results['date']=(res.created_date, time.strftime("%x %X", time.localtime(res.created_date))) 1141 except: 1142 # the date value got screwed up, just ignore it. 1143 results['date']=(0, '') 1144 return results
1145 1146 phone_path=os.environ.get('PHONE_FS', None) 1147 if __debug__ and phone_path: 1148 DebugBrewProtocol._fs_path=os.path.normpath(phone_path) 1149 BrewProtocol=DebugBrewProtocol 1150 else: 1151 BrewProtocol=RealBrewProtocol 1152 del phone_path 1153
1154 -def formatpacketerrorlog(str, origdata, data, klass):
1155 # copied from guiwidgets.LogWindow.logdata 1156 hd="" 1157 if data is not None: 1158 hd="Data - "+`len(data)`+" bytes\n" 1159 if klass is not None: 1160 try: 1161 hd+="<#! %s.%s !#>\n" % (klass.__module__, klass.__name__) 1162 except: 1163 klass=klass.__class__ 1164 hd+="<#! %s.%s !#>\n" % (klass.__module__, klass.__name__) 1165 hd+=common.datatohexstring(data) 1166 if origdata is not None: 1167 hd+="\nOriginal Data - "+`len(data)`+" bytes\n"+common.datatohexstring(origdata) 1168 return str+" "+hd
1169
1170 -def brewbasename(str):
1171 "returns basename of str" 1172 if str.rfind("/")>0: 1173 return str[str.rfind("/")+1:] 1174 return str
1175
1176 -def brewdirname(str):
1177 "returns dirname of str" 1178 if str.rfind("/")>0: 1179 return str[:str.rfind("/")] 1180 return str
1181 1182
1183 -class SPURIOUSZERO(prototypes.BaseProtogenClass):
1184 """This is a special class used to consume the spurious zero in some p_brew.listfileresponse 1185 1186 The three bytes are formatted as follows: 1187 1188 - An optional 'null' byte (this class) 1189 - A byte specifying how long the directory name portion is, including trailing slash 1190 - A byte specifying the length of the whole name 1191 - The bytes of the filename (which includes the full directory name) 1192 1193 Fun and games ensue because files in the root directory have a zero length directory 1194 name, so we have some heuristics to try and distinguish if the first byte is the 1195 spurious zero or not 1196 1197 Also allow for zero length filenames. 1198 1199 """
1200 - def __init__(self, *args, **kwargs):
1201 super(SPURIOUSZERO,self).__init__(*args, **kwargs) 1202 1203 self._value=None 1204 if self._ismostderived(SPURIOUSZERO): 1205 self._update(args, kwargs)
1206
1207 - def _update(self, args, kwargs):
1208 super(SPURIOUSZERO, self)._update(args, kwargs) 1209 1210 self._complainaboutunusedargs(SPURIOUSZERO, kwargs) 1211 1212 if len(args): 1213 raise TypeError("Unexpected arguments "+`args`)
1214
1215 - def readfrombuffer(self, buf):
1216 self._bufferstartoffset=buf.getcurrentoffset() 1217 1218 # there are several cases this code has to deal with 1219 # 1220 # The data is ordered like this: 1221 # 1222 # optional spurious zero (sz) 1223 # dirlen 1224 # fulllen 1225 # name 1226 # 1227 # These are the various possibilities. The first two 1228 # are a file in the root directory (dirlen=0), with the other 1229 # two being a file in a subdirectory (dirlen>0). fulllen 1230 # is always >0 1231 # 1232 # A: dirlen=0 fulllen name 1233 # B: sz dirlen=0 fulllen name 1234 # C: dirlen>0 fulllen name 1235 # D: sz dirlen>0 fulllen name 1236 1237 while True: # this is just used so we can break easily 1238 1239 # CASE C 1240 if buf.peeknextbyte()!=0: 1241 self._value=-1 1242 break 1243 1244 # CASE B 1245 if buf.peeknextbyte(1)==0: 1246 # If the filename is empty, we should see two zeros 1247 if buf.howmuchmore()==2: 1248 break 1249 self._value=buf.getnextbyte() # consume sz 1250 break 1251 1252 # A & D are harder to distinguish since they both consist of a zero 1253 # followed by non-zero. Consequently we examine the data for 1254 # consistency 1255 1256 all=buf.peeknextbytes(min(max(2+buf.peeknextbyte(1), 3+buf.peeknextbyte(2)), buf.howmuchmore())) 1257 1258 # are the values consistent for D? 1259 ddirlen=ord(all[1]) 1260 dfulllen=ord(all[2]) 1261 1262 if ddirlen<dfulllen and ddirlen<len(all)-3 and all[3+ddirlen-1]=='/': 1263 self._value=buf.getnextbyte() # consume sz 1264 break 1265 1266 # case C, do nothing 1267 self._value=-2 1268 break 1269 1270 self._bufferendoffset=buf.getcurrentoffset()
1271
1272 -class EXTRAZERO(prototypes.BaseProtogenClass):
1273 """This is a special class used to consume the spurious zero in some p_brew.listfileresponse or p_brew.listdirectoryresponse 1274 1275 The two bytes are formatted as follows: 1276 1277 - An optional 'null' byte (this class) 1278 - A byte specifying the length of the whole name 1279 - The bytes of the filename (which includes the full directory name) 1280 1281 Allow for zero length filenames. 1282 1283 """
1284 - def __init__(self, *args, **kwargs):
1285 super(EXTRAZERO,self).__init__(*args, **kwargs) 1286 1287 self._value=None 1288 if self._ismostderived(EXTRAZERO): 1289 self._update(args, kwargs)
1290
1291 - def _update(self, args, kwargs):
1292 super(EXTRAZERO, self)._update(args, kwargs) 1293 1294 self._complainaboutunusedargs(EXTRAZERO, kwargs) 1295 1296 if len(args): 1297 raise TypeError("Unexpected arguments "+`args`)
1298
1299 - def readfrombuffer(self, buf):
1300 self._bufferstartoffset=buf.getcurrentoffset() 1301 1302 # there are several cases this code has to deal with 1303 # 1304 # The data is ordered like this: 1305 # 1306 # optional spurious zero (sz) 1307 # fulllen 1308 # name 1309 # 1310 # These are the various possibilities. The first two 1311 # are a file in the root directory (dirlen=0), with the other 1312 # two being a file in a subdirectory (dirlen>0). fulllen 1313 # is always >0 1314 # 1315 # A: fulllen=0 1316 # B: ez fulllen=0 1317 # C: fulllen>0 name 1318 # D: ez fulllen>0 name 1319 1320 while True: # this is just used so we can break easily 1321 1322 # CASE C 1323 if buf.peeknextbyte()!=0: 1324 self._value=-1 1325 break 1326 1327 # CASE A 1328 if buf.howmuchmore()==1: 1329 self._value=-1 1330 break # Really a zero length file 1331 1332 # CASE B or D 1333 self._value=buf.getnextbyte() # consume sz 1334 1335 break 1336 1337 self._bufferendoffset=buf.getcurrentoffset()
1338
1339 - def writetobuffer(self, buf):
1340 raise NotImplementedError()
1341
1342 - def packetsize(self):
1343 raise NotImplementedError()
1344
1345 - def getvalue(self):
1346 "Returns the string we are" 1347 1348 if self._value is None: 1349 raise prototypes.ValueNotSetException() 1350 return self._value
1351 1352 file_cache=None 1353
1354 -class EmptyFileCache(object):
1355 - def __init__(self, bitpim_path):
1356 self._path=None 1357 self._cache_file_name=None 1358 self._data={ 'file_index': 0 } 1359 self.esn=None
1360 - def hit(self, file_name, datetime, data_len):
1361 return False
1362 - def data(self, file_name):
1363 return None
1364 - def add(self, file_name, datetime, data):
1365 pass
1366 - def clear(self, file_name):
1367 pass
1368 - def set_path(self, bitpim_path):
1369 try: 1370 print 'setting path to',`bitpim_path` 1371 if not bitpim_path: 1372 raise ValueError 1373 # set the paths 1374 self.__class__=FileCache 1375 self._path=os.path.join(bitpim_path, 'cache') 1376 self._cache_file_name=os.path.join(self._path, 1377 self._cache_index_file_name) 1378 self._check_path() 1379 self._read_index() 1380 self._write_index() 1381 except: 1382 self.__class__=EmptyFileCache
1383
1384 -class FileCache(object):
1385 _cache_index_file_name='index.idx' 1386 current_version=1
1387 - def __init__(self, bitpim_path):
1388 self._path=os.path.join(bitpim_path, 'cache') 1389 self._cache_file_name=os.path.join(self._path, 1390 self._cache_index_file_name) 1391 self._data={ 'file_index': 0 } 1392 self.esn=None 1393 try: 1394 if not bitpim_path: 1395 raise ValueError 1396 self._check_path() 1397 self._read_index() 1398 self._write_index() 1399 except: 1400 # something's wrong, disable caching 1401 self.__class__=EmptyFileCache
1402
1403 - def _check_path(self):
1404 try: 1405 os.makedirs(self._path) 1406 except: 1407 pass 1408 if not os.path.isdir(self._path): 1409 raise Exception("Bad cache directory: '"+self._path+"'")
1410
1411 - def _read_index(self):
1412 self._check_path() 1413 d={ 'result': {} } 1414 try: 1415 common.readversionedindexfile(self._cache_file_name, d, None, 1416 self.current_version) 1417 self._data.update(d['result']) 1418 except: 1419 print 'failed to read cache index file'
1420
1421 - def _write_index(self):
1422 self._check_path() 1423 common.writeversionindexfile(self._cache_file_name, self._data, 1424 self.current_version)
1425
1426 - def _entry(self, file_name):
1427 k=self._data.get(self.esn, None) 1428 if k: 1429 return k.get(file_name, None)
1430
1431 - def hit(self, file_name, datetime, data_len):
1432 try: 1433 e=self._entry(file_name) 1434 if e: 1435 return e['datetime']==datetime and \ 1436 e['size']==data_len 1437 return False 1438 except: 1439 if __debug__: 1440 raise 1441 return False
1442
1443 - def data(self, file_name):
1444 try: 1445 e=self._entry(file_name) 1446 if e: 1447 _data=file(os.path.join(self._path, e['cache']), 'rb').read() 1448 if len(_data)==e['size']: 1449 return _data 1450 except IOError: 1451 return None 1452 except: 1453 if __debug__: 1454 raise 1455 return None
1456
1457 - def add(self, file_name, datetime, data):
1458 try: 1459 if self.esn: 1460 e=self._entry(file_name) 1461 if not e: 1462 # entry does not exist, create a new one 1463 self._data.setdefault(self.esn, {})[file_name]={} 1464 e=self._data[self.esn][file_name] 1465 e['cache']='F%05d'%self._data['file_index'] 1466 self._data['file_index']+=1 1467 # entry exists, just update the data 1468 e['datetime']=datetime 1469 e['size']=len(data) 1470 _cache_file_name=os.path.join(self._path, e['cache']) 1471 try: 1472 file(_cache_file_name, 'wb').write(data) 1473 self._write_index() 1474 except IOError: 1475 # failed to write to cache file, drop this entry 1476 self._read_index() 1477 except: 1478 if __debug__: 1479 raise
1480
1481 - def clear(self, file_name):
1482 try: 1483 # clear this entry if it exists 1484 e=self._entry(file_name) 1485 if e: 1486 try: 1487 # remove the cache file 1488 os.remove(os.path.join(self._path, e['cache'])) 1489 except: 1490 pass 1491 # and remove the entry 1492 del self._data[self.esn][file_name] 1493 self._write_index() 1494 except: 1495 if __debug__: 1496 raise
1497
1498 - def set_path(self, bitpim_path):
1499 try: 1500 if not bitpim_path: 1501 raise ValueError 1502 # set the paths 1503 self.__class__=FileCache 1504 self._path=os.path.join(bitpim_path, 'cache') 1505 self._cache_file_name=os.path.join(self._path, 1506 self._cache_index_file_name) 1507 self._check_path() 1508 self._read_index() 1509 self._write_index() 1510 except: 1511 self.__class__=EmptyFileCache
1512