PyXR

c:\projects\bitpim\src \ phones \ com_brew.py



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