0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: com_brew.py 4664 2008-08-08 02:42:25Z djpham $ 0009 0010 """Implements the "Brew" filesystem protocol""" 0011 0012 import os 0013 import p_brew 0014 import time 0015 import cStringIO 0016 import com_phone 0017 import prototypes 0018 import common 0019 0020 class BrewNotSupported(Exception): 0021 """This phone not supported""" 0022 pass 0023 0024 class BrewCommandException(Exception): 0025 def __init__(self, errnum, str=None): 0026 if str is None: 0027 str="Brew Error 0x%02x" % (errnum,) 0028 Exception.__init__(self, str) 0029 self.errnum=errnum 0030 0031 class BrewNoMoreEntriesException(BrewCommandException): 0032 def __init__(self, errnum=0x1c): 0033 BrewCommandException.__init__(self, errnum, "No more directory entries") 0034 0035 class BrewNoSuchDirectoryException(BrewCommandException): 0036 def __init__(self, errnum=0x08): 0037 BrewCommandException.__init__(self, errnum, "No such directory") 0038 0039 class BrewNoSuchFileException(BrewCommandException): 0040 def __init__(self, errnum=0x06): 0041 BrewCommandException.__init__(self, errnum, "No such file") 0042 0043 class BrewBadPathnameException(BrewCommandException): 0044 def __init__(self, errnum=0x1a): 0045 BrewCommandException.__init__(self, errnum, "Bad pathname") 0046 0047 class BrewFileLockedException(BrewCommandException): 0048 def __init__(self, errnum=0x0b): 0049 BrewCommandException.__init__(self, errnum, "File is locked") 0050 0051 class BrewNameTooLongException(BrewCommandException): 0052 def __init__(self, errnum=0x0d): 0053 BrewCommandException.__init__(self, errnum, "Name is too long") 0054 0055 class BrewDirectoryExistsException(BrewCommandException): 0056 def __init__(self, errnum=0x07): 0057 BrewCommandException.__init__(self, errnum, "Directory already exists") 0058 0059 class BrewBadBrewCommandException(BrewCommandException): 0060 def __init__(self, errnum=0x100): 0061 BrewCommandException.__init__(self, errnum, "The phone did not recognise the brew command") 0062 0063 class BrewMalformedBrewCommandException(BrewCommandException): 0064 def __init__(self, errnum=0x101): 0065 BrewCommandException.__init__(self, errnum, "The parameters in the last brew command were invalid") 0066 0067 class BrewAccessDeniedException(BrewCommandException): 0068 def __init__(self, errnum=0x04, filename=None): 0069 BrewCommandException.__init__(self, errnum, "Access Denied. Access to the file/directory may be blocked on this phone") 0070 0071 class BrewFileSystemFullException(BrewCommandException): 0072 def __init__(self, errnum=0x16, filename=None): 0073 BrewCommandException.__init__(self, errnum, "The phone has run out of space to store any more files") 0074 0075 class BrewStatFileException(BrewCommandException): 0076 def __init__(self, errnum, filename): 0077 BrewCommandException.__init__(self, errnum, 0078 "Stat File %s errno %d"%(filename, errnum)) 0079 0080 0081 modeignoreerrortypes=com_phone.modeignoreerrortypes+(BrewCommandException,common.CommsDataCorruption) 0082 0083 class _DirCache: 0084 """This is a class that lets you do various filesystem manipulations and 0085 it remembers the data. Typical usage would be if you make changes to 0086 files (adding, removing, rewriting) and then have to keep checking if 0087 files exist, add sizes etc. This class saves the hassle of rereading 0088 the directory every single time. Note that it will only see changes 0089 you make via this class. If you go directly to the Brew class then 0090 those won't be seen. 0091 """ 0092 def __init__(self, target): 0093 "@param target: where operations should be done after recording them here" 0094 self.__target=target 0095 self.__cache={} 0096 0097 def rmfile(self, filename): 0098 res=self.__target.rmfile(filename) 0099 node=self._getdirectory(brewdirname(filename)) 0100 if node is None: # we didn't have it 0101 return 0102 del node[brewbasename(filename)] 0103 return res 0104 0105 def stat(self, filename): 0106 node=self._getdirectory(brewdirname(filename), ensure=True) 0107 return node.get(brewbasename(filename), None) 0108 0109 def readfile(self, filename): 0110 node=self._getdirectory(brewdirname(filename), ensure=True) 0111 file=node.get(brewbasename(filename), None) 0112 if file is None: 0113 raise BrewNoSuchFileException() 0114 # This class only populates the 'data' portion of the file obj when needed 0115 data=file.get('data', None) 0116 if data is None: 0117 data=self.__target.getfilecontents(filename) 0118 file['data']=data 0119 return data 0120 0121 def writefile(self, filename, contents): 0122 res=self.__target.writefile(filename, contents) 0123 node=self._getdirectory(brewdirname(filename), ensure=True) 0124 # we can't put the right date in since we have no idea 0125 # what the timezone (or the time for that matter) on the 0126 # phone is 0127 stat=node.get(brewbasename(filename), {'name': filename, 'type': 'file', 'date': (0, "")}) 0128 stat['size']=len(contents) 0129 stat['data']=contents 0130 node[brewbasename(filename)]=stat 0131 return res 0132 0133 def _getdirectory(self, dirname, ensure=False): 0134 if not ensure: 0135 return self.__cache.get(dirname, None) 0136 node=self.__cache.get(dirname, None) 0137 if node is not None: return node 0138 node={} 0139 fs=self.__target.getfilesystem(dirname) 0140 for filename in fs.keys(): 0141 node[brewbasename(filename)]=fs[filename] 0142 self.__cache[dirname]=node 0143 return node 0144 0145 class DebugBrewProtocol: 0146 """ Emulate a phone file system using a local file system. This is used 0147 when you may not have access to a physical phone, but have a copy of its 0148 file system. 0149 """ 0150 MODEBREW="modebrew" 0151 _fs_path='' 0152 def __init__(self): 0153 pass 0154 def getfirmwareinformation(self): 0155 self.log("Getting firmware information") 0156 def explore0c(self): 0157 self.log("Trying stuff with command 0x0c") 0158 def offlinerequest(self, reset=False, delay=0): 0159 self.log("Taking phone offline") 0160 if reset: 0161 self.log("Resetting phone") 0162 def modemmoderequest(self): 0163 self.log("Attempting to put phone in modem mode") 0164 def mkdir(self, name): 0165 self.log("Making directory '"+name+"'") 0166 os.mkdir(os.path.join(self._fs_path, name)) 0167 def mkdirs(self, directory): 0168 if len(directory)<1: 0169 return 0170 dirs=directory.split('/') 0171 for i in range(0,len(dirs)): 0172 try: 0173 self.mkdir("/".join(dirs[:i+1])) # basically mkdir -p 0174 except: 0175 pass 0176 def rmdir(self,name): 0177 self.log("Deleting directory '"+name+"'") 0178 try: 0179 os.rmdir(os.path.join(self._fs_path, name)) 0180 except: 0181 # convert to brew exception 0182 raise BrewNoSuchDirectoryException 0183 def rmfile(self,name): 0184 self.log("Deleting file '"+name+"'") 0185 try: 0186 os.remove(os.path.join(self._fs_path, name)) 0187 except: 0188 # convert to brew exception 0189 raise BrewNoSuchFileException 0190 def rmdirs(self, path): 0191 self.progress(0,1, "Listing child files and directories") 0192 all=self.getfilesystem(path, 100) 0193 keys=all.keys() 0194 keys.sort() 0195 keys.reverse() 0196 count=0 0197 for k in keys: 0198 self.progress(count, len(keys), "Deleting "+k) 0199 count+=1 0200 if all[k]['type']=='directory': 0201 self.rmdir(k) 0202 else: 0203 self.rmfile(k) 0204 self.rmdir(path) 0205 0206 def listfiles(self, dir=''): 0207 results={} 0208 self.log("Listing files in dir: '"+dir+"'") 0209 results={} 0210 _pwd=os.path.join(self._fs_path, dir) 0211 for _root,_dir,_file in os.walk(_pwd): 0212 break 0213 try: 0214 for f in _file: 0215 _stat=os.stat(os.path.join(_pwd, f)) 0216 _date=_stat[8] 0217 _name=dir+'/'+f 0218 _timestr='' 0219 try: 0220 # date is not always present in filesystem 0221 _timestr=time.strftime("%x %X", time.gmtime(_date)) 0222 except: 0223 pass 0224 results[_name]={ 'name': _name, 'type': 'file', 'size': _stat[6], 0225 'date': (_date, _timestr) } 0226 except: 0227 pass # will happen if the directory does not exist 0228 return results 0229 0230 def listsubdirs(self, dir='', recurse=0): 0231 results={} 0232 self.log("Listing subdirs in dir: '"+dir+"'") 0233 _pwd=os.path.join(self._fs_path, dir) 0234 for _root,_dir,_file in os.walk(_pwd): 0235 break 0236 for d in _dir: 0237 if len(dir): 0238 d=dir+"/"+d 0239 results[d]={ 'name': d, 'type': 'directory' } 0240 if recurse>0: 0241 results.update(self.listsubdirs(d, recurse-1)) 0242 return results 0243 0244 def getfilesystem(self, dir="", recurse=0): 0245 results={} 0246 _file=[] 0247 _dir=[] 0248 self.log("Listing dir '"+dir+"'") 0249 _pwd=os.path.join(self._fs_path, dir) 0250 for _root,_dir,_file in os.walk(_pwd): 0251 break 0252 for f in _file: 0253 _stat=os.stat(os.path.join(_pwd, f)) 0254 _date=_stat[8] 0255 _name=dir+'/'+f 0256 _timestr='' 0257 try: 0258 # date is not always present in filesystem 0259 _timestr=time.strftime("%x %X", time.gmtime(_date)) 0260 except: 0261 pass 0262 results[_name]={ 'name': _name, 'type': 'file', 'size': _stat[6], 0263 'date': (_date, _timestr) } 0264 for d in _dir: 0265 _name=dir+'/'+d 0266 results[_name]={ 'name': _name, 'type': 'directory' } 0267 if recurse>0: 0268 results.update(self.getfilesystem(_name, recurse-1)) 0269 return results 0270 0271 def statfile(self, name): 0272 try: 0273 _stat=os.stat(os.path.join(self._fs_path, name)) 0274 _date=_stat[8] 0275 results={ 'name': name, 'type': 'file', 'size': _stat[6], 'datevalue': 0x0DEB0DEB, 0276 'date': (_date, 0277 time.strftime("%x %X", time.gmtime(_date))) } 0278 return results 0279 except: 0280 # File does not exist, bail 0281 return None 0282 0283 def writefile(self, name, contents): 0284 self.log("Writing file '"+name+"' bytes "+`len(contents)`) 0285 file(os.path.join(self._fs_path, name), 'wb').write(contents) 0286 0287 def getfilecontents(self, name, use_cache=False): 0288 self.log("Getting file contents '"+name+"'") 0289 try: 0290 if name[0]=='/': 0291 return file(os.path.join(self._fs_path, name[1:]), 'rb').read() 0292 return file(os.path.join(self._fs_path, name), 'rb').read() 0293 except: 0294 raise BrewNoSuchFileException 0295 0296 def get_brew_esn(self): 0297 # fake an ESN for debug mode 0298 return "DEBUGESN" 0299 def _setmodebrew(self): 0300 self.log('_setmodebrew: in mode BREW') 0301 return True 0302 def sendbrewcommand(self, request, responseclass, callsetmode=True): 0303 return NotImplementedError 0304 def log(self, s): 0305 print s 0306 def logdata(self, s, data, klass=None): 0307 print s 0308 0309 def exists(self, path): 0310 return os.path.exists(os.path.join(self._fs_path, path)) 0311 0312 DirCache=_DirCache 0313 0314 class RealBrewProtocol: 0315 "Talk to a phone using the 'brew' protocol" 0316 0317 MODEBREW="modebrew" 0318 brewterminator="\x7e" 0319 0320 # phone uses Jan 6, 1980 as epoch. Python uses Jan 1, 1970. This is difference 0321 _brewepochtounix=315964800 0322 0323 def __init__(self): 0324 # reset default encoding 0325 p_brew.PHONE_ENCODING=p_brew.DEFAULT_PHONE_ENCODING 0326 0327 def getfirmwareinformation(self): 0328 self.log("Getting firmware information") 0329 req=p_brew.firmwarerequest() 0330 res=self.sendbrewcommand(req, p_brew.firmwareresponse) 0331 0332 def get_brew_esn(self): 0333 # return the ESN of this phone 0334 return '%0X'%self.sendbrewcommand(p_brew.ESN_req(), 0335 p_brew.ESN_resp).esn 0336 0337 def explore0c(self): 0338 self.log("Trying stuff with command 0x0c") 0339 req=p_brew.testing0crequest() 0340 res=self.sendbrewcommand(req, p_brew.testing0cresponse) 0341 0342 def offlinerequest(self, reset=False, delay=0): 0343 time.sleep(delay) 0344 req=p_brew.setmoderequest() 0345 req.request=1 0346 self.log("Taking phone offline") 0347 self.sendbrewcommand(req, p_brew.setmoderesponse) 0348 time.sleep(delay) 0349 if reset: 0350 req=p_brew.setmoderequest() 0351 req.request=2 0352 self.log("Resetting phone") 0353 self.sendbrewcommand(req, p_brew.setmoderesponse) 0354 0355 def modemmoderequest(self): 0356 # Perhaps we should modify sendbrewcommand to have an option to 0357 # not be picky about response. 0358 self.log("Attempting to put phone in modem mode") 0359 req=p_brew.setmodemmoderequest() 0360 buffer=prototypes.buffer() 0361 req.writetobuffer(buffer, logtitle="modem mode request") 0362 data=buffer.getvalue() 0363 data=common.pppescape(data+common.crcs(data))+common.pppterminator 0364 self.comm.write(data) 0365 # Response could be text or a packet 0366 self.comm.readsome(numchars=5) 0367 self.mode=self.MODENONE # Probably should add a modem mode 0368 0369 def mkdir(self, name): 0370 self.log("Making directory '"+name+"'") 0371 if self.isdir(name): 0372 raise BrewDirectoryExistsException 0373 req=p_brew.mkdirrequest() 0374 req.dirname=name 0375 try: 0376 self.sendbrewcommand(req, p_brew.mkdirresponse) 0377 except BrewDirectoryExistsException: 0378 # sometime the phone returns this, which is OK 0379 pass 0380 0381 def mkdirs(self, directory): 0382 if len(directory)<1: 0383 return 0384 dirs=directory.split('/') 0385 for i in range(0,len(dirs)): 0386 try: 0387 self.mkdir("/".join(dirs[:i+1])) # basically mkdir -p 0388 except: 0389 pass 0390 0391 0392 def rmdir(self,name): 0393 self.log("Deleting directory '"+name+"'") 0394 req=p_brew.rmdirrequest() 0395 req.dirname=name 0396 self.sendbrewcommand(req, p_brew.rmdirresponse) 0397 0398 def rmfile(self,name): 0399 self.log("Deleting file '"+name+"'") 0400 req=p_brew.rmfilerequest() 0401 req.filename=name 0402 self.sendbrewcommand(req, p_brew.rmfileresponse) 0403 file_cache.clear(name) 0404 0405 def rmdirs(self, path): 0406 self.progress(0,1, "Listing child files and directories") 0407 all=self.getfilesystem(path, 100) 0408 keys=all.keys() 0409 keys.sort() 0410 keys.reverse() 0411 count=0 0412 for k in keys: 0413 self.progress(count, len(keys), "Deleting "+k) 0414 count+=1 0415 if all[k]['type']=='directory': 0416 self.rmdir(k) 0417 else: 0418 self.rmfile(k) 0419 self.rmdir(path) 0420 0421 def exists(self, path): 0422 # Return True if this path (dir/file) exists 0423 return bool(self.statfile(path)) 0424 0425 def isdir(self, path): 0426 # Return True if path refers to an existing dir 0427 if not self.statfile(path): 0428 # if it doesn't exist, bail 0429 return False 0430 # just try a list dirs command and see if it bombs out 0431 req=p_brew.listdirectoriesrequest(dirname=path) 0432 try: 0433 self.sendbrewcommand(req, p_brew.listdirectoriesresponse) 0434 except (BrewCommandException, BrewBadPathnameException, 0435 BrewNoSuchDirectoryException, 0436 BrewAccessDeniedException): 0437 return False 0438 except: 0439 if __debug__: 0440 raise 0441 return False 0442 return True 0443 0444 def isfile(self, filename): 0445 # return True if filename is a file 0446 if not self.statfile(filename): 0447 return False 0448 # if it exists and not a dir, then it must be a file! 0449 return not self.isdir(filename) 0450 0451 def basename(self, path): 0452 # return the basename of the path, does not check on whether the path 0453 # exists. 0454 _dirs=[x for x in path.split('/') if x] 0455 if _dirs: 0456 return _dirs[-1] 0457 return '' 0458 0459 def dirname(self, filename): 0460 # return the dir name of the filename, does not check on whether 0461 # the file exists. 0462 _dirs=[x for x in filename.split('/') if x] 0463 if len(_dirs)<2: 0464 # either / or /name 0465 return '/' 0466 return '/'.join(_dirs[:-1]) 0467 0468 def join(self, *args): 0469 # join the dir/file components and return the full path name 0470 return '/'.join([x.strip('/') for x in args if x]) 0471 0472 def listsubdirs(self, dir='', recurse=0): 0473 results={} 0474 self.log("Listing subdirs in dir: '"+dir+"'") 0475 self.log("X recurse="+`recurse`) 0476 0477 req=p_brew.listdirectoryrequest() 0478 req.dirname=dir 0479 for i in xrange(10000): 0480 try: 0481 req.entrynumber=i 0482 res=self.sendbrewcommand(req,p_brew.listdirectoryresponse) 0483 # sometimes subdir can already include the parent directory 0484 f=res.subdir.rfind("/") 0485 if f>=0: 0486 subdir=res.subdir[f+1:] 0487 else: 0488 subdir=res.subdir 0489 if len(dir): 0490 subdir=dir+"/"+subdir 0491 self.log("subdir="+subdir) 0492 results[subdir]={ 'name': subdir, 'type': 'directory' } 0493 except BrewNoMoreEntriesException: 0494 break 0495 except (BrewBadPathnameException, BrewAccessDeniedException): 0496 self.log('Failed to list dir '+dir) 0497 return {} 0498 if recurse: 0499 for k,_subdir in results.items(): 0500 results.update(self.listsubdirs(_subdir['name'], recurse-1)) 0501 return results 0502 0503 def hassubdirs(self, dir=''): 0504 self.log('Checking for subdirs in dir: "'+dir+'"') 0505 req=p_brew.listdirectoryrequest() 0506 req.dirname=dir 0507 req.entrynumber=0 0508 try: 0509 res=self.sendbrewcommand(req,p_brew.listdirectoryresponse) 0510 # there's at least one subdir 0511 return True 0512 except BrewNoMoreEntriesException: 0513 return False 0514 except: 0515 if __debug__: 0516 raise 0517 return False 0518 0519 def listfiles(self, dir=''): 0520 results={} 0521 self.log("Listing files in dir: '"+dir+"'") 0522 0523 _broken_date=hasattr(self.protocolclass, 'broken_filelist_date') and \ 0524 self.protocolclass.broken_filelist_date 0525 req=p_brew.listfilerequest() 0526 req.dirname=dir 0527 # self.log("file listing 0x0b command") 0528 for i in xrange(10000): 0529 try: 0530 req.entrynumber=i 0531 res=self.sendbrewcommand(req,p_brew.listfileresponse) 0532 results[res.filename]={ 'name': res.filename, 'type': 'file', 0533 'size': res.size } 0534 if not _broken_date: 0535 if res.date<=0: 0536 results[res.filename]['date']=(0, "") 0537 else: 0538 try: 0539 date=res.date+self._brewepochtounix 0540 results[res.filename]['date']=(date, time.strftime("%x %X", time.localtime(date))) 0541 except: 0542 # invalid date - see SF bug #833517 0543 results[res.filename]['date']=(0, "") 0544 except BrewNoMoreEntriesException: 0545 break 0546 except (BrewBadPathnameException, BrewAccessDeniedException): 0547 self.log('Failed to list files in dir '+dir) 0548 return {} 0549 if _broken_date: 0550 for _key,_entry in results.items(): 0551 _stat=self.statfile(_key) 0552 if _stat: 0553 _entry['date']=_stat.get('date', (0, '')) 0554 else: 0555 _entry['date']=(0, '') 0556 return results 0557 0558 def getfilesystem(self, dir="", recurse=0): 0559 self.log("Getting file system in dir '"+dir+"'") 0560 results=self.listsubdirs(dir) 0561 subdir_list=[x['name'] for k,x in results.items()] 0562 results.update(self.listfiles(dir)) 0563 if recurse: 0564 for _subdir in subdir_list: 0565 results.update(self.getfilesystem(_subdir, recurse-1)) 0566 return results 0567 0568 def statfile(self, name): 0569 # return the status of the file 0570 try: 0571 self.log('stat file '+name) 0572 req=p_brew.statfilerequest() 0573 req.filename=name 0574 res=self.sendbrewcommand(req, p_brew.statfileresponse) 0575 results={ 'name': name, 'type': 'file', 'size': res.size, 0576 'datevalue': res.date } 0577 if res.date<=0: 0578 results['date']=(0, '') 0579 else: 0580 try: 0581 date=res.date+self._brewepochtounix 0582 results['date']=(date, time.strftime("%x %X", time.localtime(date))) 0583 except: 0584 # invalid date - see SF bug #833517 0585 results['date']=(0, '') 0586 return results 0587 except (BrewCommandException, BrewNoSuchFileException): 0588 # File does not exist, bail 0589 return None 0590 except: 0591 # something happened, we don't have any info on this file 0592 if __debug__: 0593 raise 0594 return None 0595 0596 def setfileattr(self, filename, date): 0597 # sets the date and time of the file on the phone 0598 self.log('set file date '+filename +`date`) 0599 req=p_brew.setfileattrrequest() 0600 # convert date to GPS time 0601 req.date=date-self._brewepochtounix 0602 req.filename=filename 0603 self.sendbrewcommand(req, p_brew.setfileattrresponse) 0604 0605 def writefile(self, name, contents): 0606 start=time.time() 0607 self.log("Writing file '"+name+"' bytes "+`len(contents)`) 0608 desc="Writing "+name 0609 req=p_brew.writefilerequest() 0610 req.filesize=len(contents) 0611 req.data=contents[:0x100] 0612 req.filename=name 0613 self.sendbrewcommand(req, p_brew.writefileresponse) 0614 # do remaining blocks 0615 numblocks=len(contents)/0x100 0616 count=0 0617 for offset in range(0x100, len(contents), 0x100): 0618 req=p_brew.writefileblockrequest() 0619 count+=1 0620 if count>=0x100: count=1 0621 if count % 5==0: 0622 self.progress(offset>>8,numblocks,desc) 0623 req.blockcounter=count 0624 req.thereismore=offset+0x100<len(contents) 0625 block=contents[offset:] 0626 l=min(len(block), 0x100) 0627 block=block[:l] 0628 req.data=block 0629 self.sendbrewcommand(req, p_brew.writefileblockresponse) 0630 end=time.time() 0631 if end-start>3: 0632 self.log("Wrote "+`len(contents)`+" bytes at "+`int(len(contents)/(end-start))`+" bytes/second") 0633 0634 0635 def getfilecontents(self, file, use_cache=False): 0636 if use_cache: 0637 node=self.statfile(file) 0638 if node and file_cache.hit(file, node['date'][0], node['size']): 0639 self.log('Reading from cache: '+file) 0640 _data=file_cache.data(file) 0641 if _data: 0642 return _data 0643 self.log('Cache file corrupted and discarded') 0644 0645 start=time.time() 0646 self.log("Getting file contents '"+file+"'") 0647 desc="Reading "+file 0648 0649 data=cStringIO.StringIO() 0650 0651 req=p_brew.readfilerequest() 0652 req.filename=file 0653 0654 res=self.sendbrewcommand(req, p_brew.readfileresponse) 0655 0656 filesize=res.filesize 0657 data.write(res.data) 0658 0659 counter=0 0660 while res.thereismore: 0661 counter+=1 0662 if counter>0xff: 0663 counter=0x01 0664 if counter%5==0: 0665 self.progress(data.tell(), filesize, desc) 0666 req=p_brew.readfileblockrequest() 0667 req.blockcounter=counter 0668 res=self.sendbrewcommand(req, p_brew.readfileblockresponse) 0669 data.write(res.data) 0670 0671 self.progress(1,1,desc) 0672 0673 data=data.getvalue() 0674 0675 # give the download speed if we got a non-trivial amount of data 0676 end=time.time() 0677 if end-start>3: 0678 self.log("Read "+`filesize`+" bytes at "+`int(filesize/(end-start))`+" bytes/second") 0679 0680 if filesize!=len(data): 0681 self.log("expected size "+`filesize`+" actual "+`len(data)`) 0682 self.raisecommsexception("Brew file read is incorrect size", common.CommsDataCorruption) 0683 if use_cache and node: 0684 file_cache.add(file, node.get('date', [0])[0], data) 0685 return data 0686 0687 DirCache=_DirCache 0688 0689 def _setmodebrew(self): 0690 req=p_brew.memoryconfigrequest() 0691 respc=p_brew.memoryconfigresponse 0692 try: 0693 self.sendbrewcommand(req, respc, callsetmode=False) 0694 return True 0695 except modeignoreerrortypes: 0696 pass 0697 0698 for baud in 0, 38400,115200: 0699 if baud: 0700 if not self.comm.setbaudrate(baud): 0701 continue 0702 try: 0703 self.sendbrewcommand(req, respc, callsetmode=False) 0704 return True 0705 except modeignoreerrortypes: 0706 pass 0707 0708 # send AT$CDMG at various speeds 0709 for baud in (0, 115200, 19200, 230400): 0710 if baud: 0711 if not self.comm.setbaudrate(baud): 0712 continue 0713 print "Baud="+`baud` 0714 0715 try: 0716 for line in self.comm.sendatcommand("+GMM"): 0717 if line.find("SPH-A700")>0: 0718 raise BrewNotSupported("This phone is not supported by BitPim", self.desc) 0719 except modeignoreerrortypes: 0720 self.log("No response to AT+GMM") 0721 except: 0722 print "GMM Exception" 0723 self.mode=self.MODENONE 0724 self.comm.shouldloop=True 0725 raise 0726 0727 try: 0728 self.comm.write("AT$QCDMG\r\n") 0729 except: 0730 # some issue during writing such as user pulling cable out 0731 self.mode=self.MODENONE 0732 self.comm.shouldloop=True 0733 raise 0734 try: 0735 # if we got OK back then it was success 0736 if self.comm.readsome().find("OK")>=0: 0737 break 0738 except modeignoreerrortypes: 0739 self.log("No response to setting QCDMG mode") 0740 0741 # verify if we are in DM mode 0742 for baud in 0,38400,115200: 0743 if baud: 0744 if not self.comm.setbaudrate(baud): 0745 continue 0746 try: 0747 self.sendbrewcommand(req, respc, callsetmode=False) 0748 return True 0749 except modeignoreerrortypes: 0750 pass 0751 return False 0752 0753 def sendbrewcommand(self, request, responseclass, callsetmode=True): 0754 if callsetmode: 0755 self.setmode(self.MODEBREW) 0756 buffer=prototypes.buffer() 0757 request.writetobuffer(buffer, logtitle="sendbrewcommand") 0758 data=buffer.getvalue() 0759 data=common.pppescape(data+common.crcs(data))+common.pppterminator 0760 firsttwo=data[:2] 0761 try: 0762 # we logged above, and below 0763 data=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False) 0764 except modeignoreerrortypes: 0765 self.mode=self.MODENONE 0766 self.raisecommsdnaexception("manipulating the filesystem") 0767 self.comm.success=True 0768 origdata=data 0769 0770 # sometimes there is junk at the begining, eg if the user 0771 # turned off the phone and back on again. So if there is more 0772 # than one 7e in the escaped data we should start after the 0773 # second to last one 0774 d=data.rfind(common.pppterminator,0,-1) 0775 if d>=0: 0776 self.log("Multiple packets in data - taking last one starting at "+`d+1`) 0777 self.logdata("Original data", origdata, None) 0778 data=data[d+1:] 0779 0780 # turn it back to normal 0781 data=common.pppunescape(data) 0782 0783 # take off crc and terminator 0784 crc=data[-3:-1] 0785 data=data[:-3] 0786 # check the CRC at this point to see if we might have crap at the beginning 0787 calccrc=common.crcs(data) 0788 if calccrc!=crc: 0789 # sometimes there is other crap at the begining 0790 d=data.find(firsttwo) 0791 if d>0: 0792 self.log("Junk at begining of packet, data at "+`d`) 0793 self.logdata("Original data", origdata, None) 0794 self.logdata("Working on data", data, None) 0795 data=data[d:] 0796 # recalculate CRC without the crap 0797 calccrc=common.crcs(data) 0798 # see if the crc matches now 0799 if calccrc!=crc: 0800 self.logdata("Original data", origdata, None) 0801 self.logdata("Working on data", data, None) 0802 raise common.CommsDataCorruption("Brew packet failed CRC check", self.desc) 0803 0804 # log it 0805 self.logdata("brew response", data, responseclass) 0806 0807 if firsttwo=="Y\x0c" and data==firsttwo: 0808 # we are getting an echo - the modem port has been selected 0809 # instead of diagnostics port 0810 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) 0811 0812 # look for errors 0813 if data[0]=="Y" and data[2]!="\x00": # Y is 0x59 which is brew command prefix 0814 err=ord(data[2]) 0815 if err==0x1c: 0816 raise BrewNoMoreEntriesException() 0817 if err==0x08: 0818 raise BrewNoSuchDirectoryException() 0819 if err==0x06: 0820 raise BrewNoSuchFileException() 0821 if err==0x1a: 0822 raise BrewBadPathnameException() 0823 if err==0x0b: 0824 raise BrewFileLockedException() 0825 if err==0x0d: 0826 raise BrewNameTooLongException() 0827 if err==0x07: 0828 raise BrewDirectoryExistsException() 0829 if err==0x04: 0830 raise BrewAccessDeniedException() 0831 if err==0x16: 0832 raise BrewFileSystemFullException() 0833 raise BrewCommandException(err) 0834 # Starting with the vx8100/9800 verizon started to block access to some file and directories 0835 # it reports a bad command packet as the error when it really means access denied 0836 if ord(data[0])==0x13: 0837 if firsttwo[0]=="Y" or firsttwo[0]=="\x4b": # brew command 0838 raise BrewAccessDeniedException() 0839 else: 0840 raise BrewBadBrewCommandException() 0841 if ord(data[0])==0x14: 0842 raise BrewMalformedBrewCommandException() 0843 0844 # access denied error 0845 if ord(data[0])==0x4b and ord(data[2])==0x1c: 0846 raise BrewAccessDeniedException() 0847 0848 # parse data 0849 buffer=prototypes.buffer(data) 0850 res=responseclass() 0851 try: 0852 res.readfrombuffer(buffer, autolog=False) 0853 except: 0854 # we had an exception so log the data even if protocol log 0855 # view is not available 0856 self.log(formatpacketerrorlog("Error decoding response", origdata, data, responseclass)) 0857 raise 0858 return res 0859 0860 class RealBrewProtocol2(RealBrewProtocol): 0861 """Talk to a phone using the 'brew' protocol 0862 This class uses the new filesystem commands which are supported 0863 by newer qualcomm chipsets used in phones like the LG vx8100 0864 """ 0865 0866 def exists(self, name): 0867 try: 0868 self.statfile(name) 0869 except BrewNoSuchFileException: 0870 return False 0871 return True 0872 0873 def reconfig_directory(self): 0874 # not sure how important this is or even what it really does 0875 # but the product that was reverse engineered from sent this after 0876 # rmdir and mkdir, although it seems to work without it on the 8100 0877 req=p_brew.new_reconfigfilesystemrequest() 0878 self.sendbrewcommand(req, p_brew.new_reconfigfilesystemresponse) 0879 0880 def rmfile(self,name): 0881 self.log("Deleting file '"+name+"'") 0882 if self.exists(name): 0883 req=p_brew.new_rmfilerequest() 0884 req.filename=name 0885 self.sendbrewcommand(req, p_brew.new_rmfileresponse) 0886 file_cache.clear(name) 0887 0888 def rmdir(self,name): 0889 self.log("Deleting directory '"+name+"'") 0890 if self.exists(name): 0891 req=p_brew.new_rmdirrequest() 0892 req.dirname=name 0893 self.sendbrewcommand(req, p_brew.new_rmdirresponse) 0894 self.reconfig_directory() 0895 0896 def mkdir(self, name): 0897 self.log("Making directory '"+name+"'") 0898 if self.exists(name): 0899 raise BrewDirectoryExistsException 0900 req=p_brew.new_mkdirrequest() 0901 req.dirname=name 0902 self.sendbrewcommand(req, p_brew.new_mkdirresponse) 0903 self.reconfig_directory() 0904 0905 def openfile(self, name, mode, flags=p_brew.new_fileopen_flag_existing): 0906 self.log("Open file '"+name+"'") 0907 req=p_brew.new_openfilerequest() 0908 req.filename=name 0909 req.mode=mode 0910 req.flags=flags 0911 res=self.sendbrewcommand(req, p_brew.new_openfileresponse) 0912 return res.handle 0913 0914 def closefile(self, handle): 0915 self.log("Close file") 0916 req=p_brew.new_closefilerequest() 0917 req.handle=handle 0918 self.sendbrewcommand(req, p_brew.new_closefileresponse) 0919 0920 def writefile(self, name, contents): 0921 start=time.time() 0922 self.log("Writing file '"+name+"' bytes "+`len(contents)`) 0923 desc="Writing "+name 0924 size=len(contents) 0925 exists=self.exists(name) 0926 if exists: 0927 info=self.statfile(name) 0928 current_size=info['size'] 0929 else: 0930 current_size=0 0931 try: 0932 block_size = self.protocolclass.BREW_WRITE_SIZE 0933 except AttributeError: 0934 block_size = p_brew.BREW_WRITE_SIZE 0935 # if the current file is longer than the new one we have to 0936 # delete it because the write operation does not truncate it 0937 if exists and size<current_size: 0938 self.rmfile(name) 0939 exists=False 0940 if exists: 0941 handle=self.openfile(name, p_brew.new_fileopen_mode_write, p_brew.new_fileopen_flag_existing) 0942 else: 0943 handle=self.openfile(name, p_brew.new_fileopen_mode_write, p_brew.new_fileopen_flag_create) 0944 try: 0945 remain=size 0946 pos=0 0947 count=0 0948 while remain: 0949 req=p_brew.new_writefilerequest() 0950 req.handle=handle 0951 if remain > block_size: 0952 req.bytes=block_size 0953 else: 0954 req.bytes=remain 0955 req.position=size-remain 0956 req.data=contents[req.position:(req.position+req.bytes)] 0957 count=(count&0xff)+1 0958 if count % 5==0: 0959 self.progress(req.position,size,desc) 0960 res=self.sendbrewcommand(req, p_brew.new_writefileresponse) 0961 if res.bytes!=req.bytes: 0962 self.raisecommsexception("Brew file write error", common.CommsDataCorruption) 0963 remain-=req.bytes 0964 finally: # MUST close handle to file 0965 self.closefile(handle) 0966 self.progress(1,1,desc) 0967 end=time.time() 0968 if end-start>3: 0969 self.log("Wrote "+`len(contents)`+" bytes at "+`int(len(contents)/(end-start))`+" bytes/second") 0970 0971 def getfilecontents(self, file, use_cache=False): 0972 node=self.statfile(file) 0973 if use_cache: 0974 if node and file_cache.hit(file, node['date'][0], node['size']): 0975 self.log('Reading from cache: '+file) 0976 _data=file_cache.data(file) 0977 if _data: 0978 return _data 0979 self.log('Cache file corrupted and discarded') 0980 try: 0981 block_size = self.protocolclass.BREW_READ_SIZE 0982 except AttributeError: 0983 block_size = p_brew.BREW_READ_SIZE 0984 start=time.time() 0985 self.log("Getting file contents '"+file+"'") 0986 desc="Reading "+file 0987 data=cStringIO.StringIO() 0988 handle=self.openfile(file, p_brew.new_fileopen_mode_read) 0989 try: 0990 filesize=node['size'] 0991 read=0 0992 counter=0 0993 while True: 0994 counter=(counter&0xff)+1 0995 if counter%5==0: 0996 self.progress(data.tell(), filesize, desc) 0997 req=p_brew.new_readfilerequest() 0998 req.handle=handle 0999 req.bytes=block_size 1000 req.position=read 1001 res=self.sendbrewcommand(req, p_brew.new_readfileresponse) 1002 if res.bytes: 1003 data.write(res.data) 1004 read+=res.bytes 1005 else: 1006 break 1007 if read==filesize: 1008 break 1009 finally: # MUST close handle to file 1010 self.closefile(handle) 1011 self.progress(1,1,desc) 1012 data=data.getvalue() 1013 # give the download speed if we got a non-trivial amount of data 1014 end=time.time() 1015 if end-start>3: 1016 self.log("Read "+`filesize`+" bytes at "+`int(filesize/(end-start))`+" bytes/second") 1017 if filesize!=len(data): 1018 self.log("expected size "+`filesize`+" actual "+`len(data)`) 1019 self.raisecommsexception("Brew file read is incorrect size", common.CommsDataCorruption) 1020 if use_cache and node: 1021 file_cache.add(file, node.get('date', [0])[0], data) 1022 return data 1023 1024 def getfilecontents2(self, filename, start, size): 1025 # read and return data a block of data from the specified file 1026 try: 1027 block_size = self.protocolclass.BREW_READ_SIZE 1028 except AttributeError: 1029 block_size = p_brew.BREW_READ_SIZE 1030 self.log("Getting file contents2 '"+filename+"'") 1031 desc="Reading "+filename 1032 data=cStringIO.StringIO() 1033 handle=self.openfile(filename, p_brew.new_fileopen_mode_read) 1034 _readsize=start+size 1035 try: 1036 read=start 1037 counter=0 1038 while True: 1039 counter+=1 1040 if counter%5==0: 1041 self.progress(read, _readsize, desc) 1042 req=p_brew.new_readfilerequest() 1043 req.handle=handle 1044 req.bytes=block_size 1045 req.position=read 1046 res=self.sendbrewcommand(req, p_brew.new_readfileresponse) 1047 if res.bytes: 1048 data.write(res.data) 1049 read+=res.bytes 1050 else: 1051 break 1052 if read>=_readsize: 1053 break 1054 finally: # MUST close handle to file 1055 self.closefile(handle) 1056 self.progress(1,1,desc) 1057 return data.getvalue()[:size] 1058 1059 def listfiles(self, dir=''): 1060 self.log("Listing files in dir: '"+dir+"'") 1061 return self.getfilesystem(dir, recurse=0, directories=0) 1062 1063 def getfilesystem(self, dir="", recurse=0, directories=1, files=1): 1064 results={} 1065 self.log("Listing dir '"+dir+"'") 1066 req=p_brew.new_opendirectoryrequest() 1067 if dir=="": 1068 req.dirname="/" 1069 else: 1070 req.dirname=dir 1071 res=self.sendbrewcommand(req, p_brew.new_opendirectoryresponse) 1072 handle=res.handle 1073 if handle==0: # happens if the directory does not exist 1074 raise BrewNoSuchDirectoryException 1075 dirs={} 1076 count=0 1077 try: 1078 # get all the directory entries from the phone 1079 for i in xrange(1, 10000): 1080 req=p_brew.new_listentryrequest() 1081 req.entrynumber=i 1082 req.handle=handle 1083 res=self.sendbrewcommand(req, p_brew.new_listentryresponse) 1084 if len(res.entryname) == 0: # signifies end of list 1085 break 1086 if len(dir): 1087 direntry=dir+"/"+res.entryname 1088 else: 1089 direntry=res.entryname 1090 if files and (res.type==0 or res.type == 0x0f): # file or special file 1091 if res.type == 0: 1092 results[direntry]={ 'name': direntry, 'type': 'file', 'size': res.size, 'special': False } 1093 elif res.type == 0x0f: 1094 results[direntry]={ 'name': direntry, 'type': 'file', 'size': res.size, 'special': True } 1095 try: 1096 if res.date<=0: 1097 results[direntry]['date']=(0, "") 1098 else: 1099 results[direntry]['date']=(res.date, time.strftime("%x %X", time.localtime(res.date))) 1100 except: 1101 results[direntry]['date']=(0, "") 1102 elif directories and (res.type and res.type != 0x0f): # directory 1103 results[direntry]={ 'name': direntry, 'type': 'directory' } 1104 if recurse>0: 1105 dirs[count]=direntry 1106 count+=1 1107 finally: # we MUST close the handle regardless or we wont be able to list the filesystem 1108 # reliably again without rebooting it 1109 req=p_brew.new_closedirectoryrequest() 1110 req.handle=handle 1111 res=self.sendbrewcommand(req, p_brew.new_closedirectoryresponse) 1112 # recurse the subdirectories 1113 for i in range(count): 1114 results.update(self.getfilesystem(dirs[i], recurse-1)) 1115 return results 1116 1117 def statfile(self, name): 1118 # return the status of the file 1119 self.log('stat file '+name) 1120 req=p_brew.new_statfilerequest() 1121 req.filename=name 1122 res=self.sendbrewcommand(req, p_brew.new_statfileresponse) 1123 if res.error==2: # ENOENT 1124 raise BrewNoSuchFileException 1125 elif res.error==0x13: # ENODEV 1126 # locked system file. example: /dev.null 1127 raise BrewFileLockedException 1128 elif res.error != 0: 1129 raise BrewStatFileException(res.error, name) 1130 if res.type==1 or res.type==0x86: 1131 # files on external media have type 0x86 1132 results={ 'name': name, 'type': 'file', 'size': res.size } 1133 else: 1134 results={ 'name': name, 'type': 'directory' } 1135 try: 1136 if res.created_date<=0: 1137 results['date']=(0, '') 1138 else: 1139 results['date']=(res.created_date, time.strftime("%x %X", time.localtime(res.created_date))) 1140 except: 1141 # the date value got screwed up, just ignore it. 1142 results['date']=(0, '') 1143 return results 1144 1145 phone_path=os.environ.get('PHONE_FS', None) 1146 if __debug__ and phone_path: 1147 DebugBrewProtocol._fs_path=os.path.normpath(phone_path) 1148 BrewProtocol=DebugBrewProtocol 1149 else: 1150 BrewProtocol=RealBrewProtocol 1151 del phone_path 1152 1153 def formatpacketerrorlog(str, origdata, data, klass): 1154 # copied from guiwidgets.LogWindow.logdata 1155 hd="" 1156 if data is not None: 1157 hd="Data - "+`len(data)`+" bytes\n" 1158 if klass is not None: 1159 try: 1160 hd+="<#! %s.%s !#>\n" % (klass.__module__, klass.__name__) 1161 except: 1162 klass=klass.__class__ 1163 hd+="<#! %s.%s !#>\n" % (klass.__module__, klass.__name__) 1164 hd+=common.datatohexstring(data) 1165 if origdata is not None: 1166 hd+="\nOriginal Data - "+`len(data)`+" bytes\n"+common.datatohexstring(origdata) 1167 return str+" "+hd 1168 1169 def brewbasename(str): 1170 "returns basename of str" 1171 if str.rfind("/")>0: 1172 return str[str.rfind("/")+1:] 1173 return str 1174 1175 def brewdirname(str): 1176 "returns dirname of str" 1177 if str.rfind("/")>0: 1178 return str[:str.rfind("/")] 1179 return str 1180 1181 1182 class SPURIOUSZERO(prototypes.BaseProtogenClass): 1183 """This is a special class used to consume the spurious zero in some p_brew.listfileresponse 1184 1185 The three bytes are formatted as follows: 1186 1187 - An optional 'null' byte (this class) 1188 - A byte specifying how long the directory name portion is, including trailing slash 1189 - A byte specifying the length of the whole name 1190 - The bytes of the filename (which includes the full directory name) 1191 1192 Fun and games ensue because files in the root directory have a zero length directory 1193 name, so we have some heuristics to try and distinguish if the first byte is the 1194 spurious zero or not 1195 1196 Also allow for zero length filenames. 1197 1198 """ 1199 def __init__(self, *args, **kwargs): 1200 super(SPURIOUSZERO,self).__init__(*args, **kwargs) 1201 1202 self._value=None 1203 if self._ismostderived(SPURIOUSZERO): 1204 self._update(args, kwargs) 1205 1206 def _update(self, args, kwargs): 1207 super(SPURIOUSZERO, self)._update(args, kwargs) 1208 1209 self._complainaboutunusedargs(SPURIOUSZERO, kwargs) 1210 1211 if len(args): 1212 raise TypeError("Unexpected arguments "+`args`) 1213 1214 def readfrombuffer(self, buf): 1215 self._bufferstartoffset=buf.getcurrentoffset() 1216 1217 # there are several cases this code has to deal with 1218 # 1219 # The data is ordered like this: 1220 # 1221 # optional spurious zero (sz) 1222 # dirlen 1223 # fulllen 1224 # name 1225 # 1226 # These are the various possibilities. The first two 1227 # are a file in the root directory (dirlen=0), with the other 1228 # two being a file in a subdirectory (dirlen>0). fulllen 1229 # is always >0 1230 # 1231 # A: dirlen=0 fulllen name 1232 # B: sz dirlen=0 fulllen name 1233 # C: dirlen>0 fulllen name 1234 # D: sz dirlen>0 fulllen name 1235 1236 while True: # this is just used so we can break easily 1237 1238 # CASE C 1239 if buf.peeknextbyte()!=0: 1240 self._value=-1 1241 break 1242 1243 # CASE B 1244 if buf.peeknextbyte(1)==0: 1245 # If the filename is empty, we should see two zeros 1246 if buf.howmuchmore()==2: 1247 break 1248 self._value=buf.getnextbyte() # consume sz 1249 break 1250 1251 # A & D are harder to distinguish since they both consist of a zero 1252 # followed by non-zero. Consequently we examine the data for 1253 # consistency 1254 1255 all=buf.peeknextbytes(min(max(2+buf.peeknextbyte(1), 3+buf.peeknextbyte(2)), buf.howmuchmore())) 1256 1257 # are the values consistent for D? 1258 ddirlen=ord(all[1]) 1259 dfulllen=ord(all[2]) 1260 1261 if ddirlen<dfulllen and ddirlen<len(all)-3 and all[3+ddirlen-1]=='/': 1262 self._value=buf.getnextbyte() # consume sz 1263 break 1264 1265 # case C, do nothing 1266 self._value=-2 1267 break 1268 1269 self._bufferendoffset=buf.getcurrentoffset() 1270 1271 class EXTRAZERO(prototypes.BaseProtogenClass): 1272 """This is a special class used to consume the spurious zero in some p_brew.listfileresponse or p_brew.listdirectoryresponse 1273 1274 The two bytes are formatted as follows: 1275 1276 - An optional 'null' byte (this class) 1277 - A byte specifying the length of the whole name 1278 - The bytes of the filename (which includes the full directory name) 1279 1280 Allow for zero length filenames. 1281 1282 """ 1283 def __init__(self, *args, **kwargs): 1284 super(EXTRAZERO,self).__init__(*args, **kwargs) 1285 1286 self._value=None 1287 if self._ismostderived(EXTRAZERO): 1288 self._update(args, kwargs) 1289 1290 def _update(self, args, kwargs): 1291 super(EXTRAZERO, self)._update(args, kwargs) 1292 1293 self._complainaboutunusedargs(EXTRAZERO, kwargs) 1294 1295 if len(args): 1296 raise TypeError("Unexpected arguments "+`args`) 1297 1298 def readfrombuffer(self, buf): 1299 self._bufferstartoffset=buf.getcurrentoffset() 1300 1301 # there are several cases this code has to deal with 1302 # 1303 # The data is ordered like this: 1304 # 1305 # optional spurious zero (sz) 1306 # fulllen 1307 # name 1308 # 1309 # These are the various possibilities. The first two 1310 # are a file in the root directory (dirlen=0), with the other 1311 # two being a file in a subdirectory (dirlen>0). fulllen 1312 # is always >0 1313 # 1314 # A: fulllen=0 1315 # B: ez fulllen=0 1316 # C: fulllen>0 name 1317 # D: ez fulllen>0 name 1318 1319 while True: # this is just used so we can break easily 1320 1321 # CASE C 1322 if buf.peeknextbyte()!=0: 1323 self._value=-1 1324 break 1325 1326 # CASE A 1327 if buf.howmuchmore()==1: 1328 self._value=-1 1329 break # Really a zero length file 1330 1331 # CASE B or D 1332 self._value=buf.getnextbyte() # consume sz 1333 1334 break 1335 1336 self._bufferendoffset=buf.getcurrentoffset() 1337 1338 def writetobuffer(self, buf): 1339 raise NotImplementedError() 1340 1341 def packetsize(self): 1342 raise NotImplementedError() 1343 1344 def getvalue(self): 1345 "Returns the string we are" 1346 1347 if self._value is None: 1348 raise prototypes.ValueNotSetException() 1349 return self._value 1350 1351 file_cache=None 1352 1353 class EmptyFileCache(object): 1354 def __init__(self, bitpim_path): 1355 self._path=None 1356 self._cache_file_name=None 1357 self._data={ 'file_index': 0 } 1358 self.esn=None 1359 def hit(self, file_name, datetime, data_len): 1360 return False 1361 def data(self, file_name): 1362 return None 1363 def add(self, file_name, datetime, data): 1364 pass 1365 def clear(self, file_name): 1366 pass 1367 def set_path(self, bitpim_path): 1368 try: 1369 print 'setting path to',`bitpim_path` 1370 if not bitpim_path: 1371 raise ValueError 1372 # set the paths 1373 self.__class__=FileCache 1374 self._path=os.path.join(bitpim_path, 'cache') 1375 self._cache_file_name=os.path.join(self._path, 1376 self._cache_index_file_name) 1377 self._check_path() 1378 self._read_index() 1379 self._write_index() 1380 except: 1381 self.__class__=EmptyFileCache 1382 1383 class FileCache(object): 1384 _cache_index_file_name='index.idx' 1385 current_version=1 1386 def __init__(self, bitpim_path): 1387 self._path=os.path.join(bitpim_path, 'cache') 1388 self._cache_file_name=os.path.join(self._path, 1389 self._cache_index_file_name) 1390 self._data={ 'file_index': 0 } 1391 self.esn=None 1392 try: 1393 if not bitpim_path: 1394 raise ValueError 1395 self._check_path() 1396 self._read_index() 1397 self._write_index() 1398 except: 1399 # something's wrong, disable caching 1400 self.__class__=EmptyFileCache 1401 1402 def _check_path(self): 1403 try: 1404 os.makedirs(self._path) 1405 except: 1406 pass 1407 if not os.path.isdir(self._path): 1408 raise Exception("Bad cache directory: '"+self._path+"'") 1409 1410 def _read_index(self): 1411 self._check_path() 1412 d={ 'result': {} } 1413 try: 1414 common.readversionedindexfile(self._cache_file_name, d, None, 1415 self.current_version) 1416 self._data.update(d['result']) 1417 except: 1418 print 'failed to read cache index file' 1419 1420 def _write_index(self): 1421 self._check_path() 1422 common.writeversionindexfile(self._cache_file_name, self._data, 1423 self.current_version) 1424 1425 def _entry(self, file_name): 1426 k=self._data.get(self.esn, None) 1427 if k: 1428 return k.get(file_name, None) 1429 1430 def hit(self, file_name, datetime, data_len): 1431 try: 1432 e=self._entry(file_name) 1433 if e: 1434 return e['datetime']==datetime and \ 1435 e['size']==data_len 1436 return False 1437 except: 1438 if __debug__: 1439 raise 1440 return False 1441 1442 def data(self, file_name): 1443 try: 1444 e=self._entry(file_name) 1445 if e: 1446 _data=file(os.path.join(self._path, e['cache']), 'rb').read() 1447 if len(_data)==e['size']: 1448 return _data 1449 except IOError: 1450 return None 1451 except: 1452 if __debug__: 1453 raise 1454 return None 1455 1456 def add(self, file_name, datetime, data): 1457 try: 1458 if self.esn: 1459 e=self._entry(file_name) 1460 if not e: 1461 # entry does not exist, create a new one 1462 self._data.setdefault(self.esn, {})[file_name]={} 1463 e=self._data[self.esn][file_name] 1464 e['cache']='F%05d'%self._data['file_index'] 1465 self._data['file_index']+=1 1466 # entry exists, just update the data 1467 e['datetime']=datetime 1468 e['size']=len(data) 1469 _cache_file_name=os.path.join(self._path, e['cache']) 1470 try: 1471 file(_cache_file_name, 'wb').write(data) 1472 self._write_index() 1473 except IOError: 1474 # failed to write to cache file, drop this entry 1475 self._read_index() 1476 except: 1477 if __debug__: 1478 raise 1479 1480 def clear(self, file_name): 1481 try: 1482 # clear this entry if it exists 1483 e=self._entry(file_name) 1484 if e: 1485 try: 1486 # remove the cache file 1487 os.remove(os.path.join(self._path, e['cache'])) 1488 except: 1489 pass 1490 # and remove the entry 1491 del self._data[self.esn][file_name] 1492 self._write_index() 1493 except: 1494 if __debug__: 1495 raise 1496 1497 def set_path(self, bitpim_path): 1498 try: 1499 if not bitpim_path: 1500 raise ValueError 1501 # set the paths 1502 self.__class__=FileCache 1503 self._path=os.path.join(bitpim_path, 'cache') 1504 self._cache_file_name=os.path.join(self._path, 1505 self._cache_index_file_name) 1506 self._check_path() 1507 self._read_index() 1508 self._write_index() 1509 except: 1510 self.__class__=EmptyFileCache 1511
Generated by PyXR 0.9.4