def _setmodemodem(self): self.log("_setmodemodem") req=p_brew.memoryconfigrequest() respc=p_brew.memoryconfigresponse try: self.comm.sendatcommand("Z") self.comm.sendatcommand('E0V1') return True except: pass for baud in 0, 38400,115200: if baud: if not self.comm.setbaudrate(baud): continue try: self.sendbrewcommand(req, respc, callsetmode=False) self.log('In BREW mode, trying to switch to Modem mode') if self._setmodebrewtomodem(): break return False except com_brew.modeignoreerrortypes: pass for baud in (0, 115200, 19200, 230400): self.log("Baud="+`baud`) if baud: if not self.comm.setbaudrate(baud): continue try: self.comm.sendatcommand("Z") self.comm.sendatcommand('E0V1') return True except: pass return False
def is_mode_brew(self): req=p_brew.memoryconfigrequest() respc=p_brew.memoryconfigresponse for baud in 0, 38400, 115200: if baud: if not self.comm.setbaudrate(baud): continue try: self.sendbrewcommand(req, respc, callsetmode=False) return True except com_phone.modeignoreerrortypes: pass return False
def _setmodebrew(self): req=p_brew.memoryconfigrequest() respc=p_brew.memoryconfigresponse for baud in 0, 38400,115200: if baud: if not self.comm.setbaudrate(baud): continue try: self.sendbrewcommand(req, respc, callsetmode=False) return True except modeignoreerrortypes: pass for baud in (0, 115200, 19200, 230400): if baud: if not self.comm.setbaudrate(baud): continue print "Baud="+`baud` try: for line in self.comm.sendatcommand("+GMM"): if line.find("SPH-A700")>0: raise BrewNotSupported("This phone is not supported by BitPim", self.desc) except modeignoreerrortypes: self.log("No response to AT+GMM") except: print "GMM Exception" self.mode=self.MODENONE self.comm.shouldloop=True raise try: self.comm.write("AT$QCDMG\r\n") except: self.mode=self.MODENONE self.comm.shouldloop=True raise try: if self.comm.readsome().find("OK")>=0: break except modeignoreerrortypes: self.log("No response to setting QCDMG mode") for baud in 0,38400,115200: if baud: if not self.comm.setbaudrate(baud): continue try: self.sendbrewcommand(req, respc, callsetmode=False) return True except modeignoreerrortypes: pass return False
def _setmodemodem(self): self.log("_setmodemodem") req=p_brew.memoryconfigrequest() respc=p_brew.memoryconfigresponse # Just try waking phone up first try: self.comm.sendatcommand("Z") self.comm.sendatcommand('E0V1') return True except: pass # Now check to see if in diagnostic mode for baud in 0, 38400,115200: if baud: if not self.comm.setbaudrate(baud): continue try: self.sendbrewcommand(req, respc, callsetmode=False) self.log('In BREW mode, trying to switch to Modem mode') # Infinite loop if self._setmodebrewtomodem(): break return False except com_brew.modeignoreerrortypes: pass # Should be in modem mode. Wake up the interface for baud in (0, 115200, 19200, 230400): self.log("Baud="+`baud`) if baud: if not self.comm.setbaudrate(baud): continue try: self.comm.sendatcommand("Z") self.comm.sendatcommand('E0V1') return True except: pass return False
def writefile(self, name, contents): start=time.time() self.log("Writing file '"+name+"' bytes "+`len(contents)`) desc="Writing "+name req=p_brew.writefilerequest() req.filesize=len(contents) req.data=contents[:0x100] req.filename=name self.sendbrewcommand(req, p_brew.writefileresponse) numblocks=len(contents)/0x100 count=0 for offset in range(0x100, len(contents), 0x100): req=p_brew.writefileblockrequest() count+=1 if count>=0x100: count=1 if count % 5==0: self.progress(offset>>8,numblocks,desc) req.blockcounter=count req.thereismore=offset+0x100<len(contents) block=contents[offset:] l=min(len(block), 0x100) block=block[:l] req.data=block self.sendbrewcommand(req, p_brew.writefileblockresponse) end=time.time() if end-start>3: self.log("Wrote "+`len(contents)`+" bytes at "+`int(len(contents)/(end-start))`+" bytes/second") def getfilecontents(self, file, use_cache=False): if use_cache: node=self.statfile(file) if node and file_cache.hit(file, node['date'][0], node['size']): self.log('Reading from cache: '+file) _data=file_cache.data(file) if _data: return _data self.log('Cache file corrupted and discarded') start=time.time() self.log("Getting file contents '"+file+"'") desc="Reading "+file data=cStringIO.StringIO() req=p_brew.readfilerequest() req.filename=file res=self.sendbrewcommand(req, p_brew.readfileresponse) filesize=res.filesize data.write(res.data) counter=0 while res.thereismore: counter+=1 if counter>0xff: counter=0x01 if counter%5==0: self.progress(data.tell(), filesize, desc) req=p_brew.readfileblockrequest() req.blockcounter=counter res=self.sendbrewcommand(req, p_brew.readfileblockresponse) data.write(res.data) self.progress(1,1,desc) data=data.getvalue() end=time.time() if end-start>3: self.log("Read "+`filesize`+" bytes at "+`int(filesize/(end-start))`+" bytes/second") if filesize!=len(data): self.log("expected size "+`filesize`+" actual "+`len(data)`) self.raisecommsexception("Brew file read is incorrect size", common.CommsDataCorruption) if use_cache and node: file_cache.add(file, node.get('date', [0])[0], data) return data DirCache=_DirCache def _setmodebrew(self): req=p_brew.memoryconfigrequest() respc=p_brew.memoryconfigresponse for baud in 0, 38400,115200: if baud: if not self.comm.setbaudrate(baud): continue try: self.sendbrewcommand(req, respc, callsetmode=False) return True except modeignoreerrortypes: pass for baud in (0, 115200, 19200, 230400): if baud: if not self.comm.setbaudrate(baud): continue print "Baud="+`baud` try: for line in self.comm.sendatcommand("+GMM"): if line.find("SPH-A700")>0: raise BrewNotSupported("This phone is not supported by BitPim", self.desc) except modeignoreerrortypes: self.log("No response to AT+GMM") except: print "GMM Exception" self.mode=self.MODENONE self.comm.shouldloop=True raise try: self.comm.write("AT$QCDMG\r\n") except: self.mode=self.MODENONE self.comm.shouldloop=True raise try: if self.comm.readsome().find("OK")>=0: break except modeignoreerrortypes: self.log("No response to setting QCDMG mode") for baud in 0,38400,115200: if baud: if not self.comm.setbaudrate(baud): continue try: self.sendbrewcommand(req, respc, callsetmode=False) return True except modeignoreerrortypes: pass return False def sendbrewcommand(self, request, responseclass, callsetmode=True): if callsetmode: self.setmode(self.MODEBREW) buffer=prototypes.buffer() request.writetobuffer(buffer) data=buffer.getvalue() self.logdata("brew request", data, request) data=common.pppescape(data+common.crcs(data))+common.pppterminator firsttwo=data[:2] try: data=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False) except modeignoreerrortypes: self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the filesystem") self.comm.success=True origdata=data d=data.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple packets in data - taking last one starting at "+`d+1`) self.logdata("Original data", origdata, None) data=data[d+1:] data=common.pppunescape(data) d=data.find(firsttwo) if d>0: self.log("Junk at begining of packet, data at "+`d`) self.logdata("Original data", origdata, None) self.logdata("Working on data", data, None) data=data[d:] crc=data[-3:-1] data=data[:-3] calccrc=common.crcs(data) if calccrc!=crc: self.logdata("Original data", origdata, None) self.logdata("Working on data", data, None) raise common.CommsDataCorruption("Brew packet failed CRC check", self.desc) self.logdata("brew response", data, responseclass) if firsttwo=="Y\x0c" and data==firsttwo: 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) if data[0]=="Y" and data[2]!="\x00": err=ord(data[2]) if err==0x1c: raise BrewNoMoreEntriesException() if err==0x08: raise BrewNoSuchDirectoryException() if err==0x06: raise BrewNoSuchFileException() if err==0x1a: raise BrewBadPathnameException() if err==0x0b: raise BrewFileLockedException() if err==0x0d: raise BrewNameTooLongException() if err==0x07: raise BrewDirectoryExistsException() raise BrewCommandException(err) buffer=prototypes.buffer(data) res=responseclass() try: res.readfrombuffer(buffer) except: self.log(formatpacketerrorlog("Error decoding response", origdata, data, responseclass)) raise return res "Talk to a phone using the 'brew' protocol" class RealBrewProtocol2 : """Talk to a phone using the 'brew' protocol This class uses the new filesystem commands which are supported by newer qualcomm chipsets used in phones like the LG vx8100 """ def exists(self, name): try: self.statfile(name) except BrewNoSuchFileException: return False return True def reconfig_directory(self): req=p_brew.new_reconfigfilesystemrequest() self.sendbrewcommand(req, p_brew.new_reconfigfilesystemresponse) def rmfile(self,name): self.log("Deleting file '"+name+"'") if self.exists(name): req=p_brew.new_rmfilerequest() req.filename=name self.sendbrewcommand(req, p_brew.new_rmfileresponse) file_cache.clear(name) def rmdir(self,name): self.log("Deleting directory '"+name+"'") if self.exists(name): req=p_brew.new_rmdirrequest() req.dirname=name self.sendbrewcommand(req, p_brew.new_rmdirresponse) self.reconfig_directory() def mkdir(self, name): self.log("Making directory '"+name+"'") if self.exists(name): raise BrewDirectoryExistsException req=p_brew.new_mkdirrequest() req.dirname=name self.sendbrewcommand(req, p_brew.new_mkdirresponse) self.reconfig_directory() def openfile(self, name, mode, flags=p_brew.new_fileopen_flag_existing): self.log("Open file '"+name+"'") req=p_brew.new_openfilerequest() req.filename=name req.mode=mode req.flags=flags res=self.sendbrewcommand(req, p_brew.new_openfileresponse) return res.handle def closefile(self, handle): self.log("Close file") req=p_brew.new_closefilerequest() req.handle=handle self.sendbrewcommand(req, p_brew.new_closefileresponse) def writefile(self, name, contents): start=time.time() self.log("Writing file '"+name+"' bytes "+`len(contents)`) desc="Writing "+name size=len(contents) exists=self.exists(name) if exists: info=self.statfile(name) current_size=info['size'] else: current_size=0 if exists and size<current_size: self.rmfile(name) exists=False if exists: handle=self.openfile(name, p_brew.new_fileopen_mode_write, p_brew.new_fileopen_flag_existing) else: handle=self.openfile(name, p_brew.new_fileopen_mode_write, p_brew.new_fileopen_flag_create) if not handle: raise BrewNoSuchFileException try: remain=size pos=0 count=0 while remain: req=p_brew.new_writefilerequest() req.handle=handle if remain>243: req.bytes=243 else: req.bytes=remain req.position=size-remain req.data=contents[req.position:(req.position+req.bytes)] count=(count&0xff)+1 if count % 5==0: self.progress(req.position,size,desc) res=self.sendbrewcommand(req, p_brew.new_writefileresponse) if res.bytes!=req.bytes: self.raisecommsexception("Brew file write error", common.CommsDataCorruption) remain-=req.bytes except Exception, e: self.closefile(handle) raise Exception, e self.closefile(handle) self.progress(1,1,desc) end=time.time() if end-start>3: self.log("Wrote "+`len(contents)`+" bytes at "+`int(len(contents)/(end-start))`+" bytes/second") def getfilecontents(self, file, use_cache=False): node=self.statfile(file) if use_cache: if node and file_cache.hit(file, node['date'][0], node['size']): self.log('Reading from cache: '+file) _data=file_cache.data(file) if _data: return _data self.log('Cache file corrupted and discarded') start=time.time() self.log("Getting file contents '"+file+"'") desc="Reading "+file data=cStringIO.StringIO() handle=self.openfile(file, p_brew.new_fileopen_mode_read) if not handle: raise BrewNoSuchFileException try: filesize=node['size'] read=0 counter=0 while True: counter=(counter&0xff)+1 if counter%5==0: self.progress(data.tell(), filesize, desc) req=p_brew.new_readfilerequest() req.handle=handle req.bytes=0xEB req.position=read res=self.sendbrewcommand(req, p_brew.new_readfileresponse) if res.bytes: data.write(res.data) read+=res.bytes else: break if read==filesize: break except Exception, e: self.closefile(handle) raise Exception, e self.closefile(handle) self.progress(1,1,desc) data=data.getvalue() end=time.time() if end-start>3: self.log("Read "+`filesize`+" bytes at "+`int(filesize/(end-start))`+" bytes/second") if filesize!=len(data): self.log("expected size "+`filesize`+" actual "+`len(data)`) self.raisecommsexception("Brew file read is incorrect size", common.CommsDataCorruption) if use_cache and node: file_cache.add(file, node.get('date', [0])[0], data) return data def listfiles(self, dir=''): self.log("Listing files in dir: '"+dir+"'") return self.getfilesystem(dir, recurse=0, directories=0) def getfilesystem(self, dir="", recurse=0, directories=1, files=1): results={} self.log("Listing dir '"+dir+"'") req=p_brew.new_opendirectoryrequest() if dir=="": req.dirname="/" else: req.dirname=dir res=self.sendbrewcommand(req, p_brew.new_opendirectoryresponse) handle=res.handle if handle==0: raise BrewNoSuchDirectoryException dirs={} count=0 try: for i in xrange(1, 10000): req=p_brew.new_listentryrequest() req.entrynumber=i req.handle=handle res=self.sendbrewcommand(req, p_brew.new_listentryresponse) if len(res.entryname) == 0: break if len(dir): direntry=dir+"/"+res.entryname else: direntry=res.entryname if files and res.type==0: results[direntry]={ 'name': direntry, 'type': 'file', 'size': res.size } if res.date==0: results[direntry]['date']=(0, "") else: results[direntry]['date']=(res.date, time.strftime("%x %X", time.localtime(res.date))) elif directories and res.type: results[direntry]={ 'name': direntry, 'type': 'directory' } if recurse>0: dirs[count]=direntry count+=1 except Exception, e: req=p_brew.new_closedirectoryrequest() req.handle=handle res=self.sendbrewcommand(req, p_brew.new_closedirectoryresponse) raise Exception, e req=p_brew.new_closedirectoryrequest() req.handle=handle res=self.sendbrewcommand(req, p_brew.new_closedirectoryresponse) for i in range(count): results.update(self.getfilesystem(dirs[i], recurse-1)) return results def statfile(self, name): self.log('stat file '+name) req=p_brew.new_statfilerequest() req.filename=name res=self.sendbrewcommand(req, p_brew.new_statfileresponse) if res.flags==2: raise BrewNoSuchFileException if res.type==1: results={ 'name': name, 'type': 'file', 'size': res.size } if res.created_date==0: results['date']=(0, '') else: results['date']=(res.created_date, time.strftime("%x %X", time.localtime(res.created_date))) else: results={ 'name': name, 'type': 'directory' } return results """Talk to a phone using the 'brew' protocol This class uses the new filesystem commands which are supported by newer qualcomm chipsets used in phones like the LG vx8100 """ class BrewProtocol (RealBrewProtocol) : """This is just a wrapper class that allows the manipulation between RealBrewProtocol and DebugBrewProtocol classes. """ def __init__(self): phone_path=os.environ.get('PHONE_FS', None) if __debug__ and phone_path: print 'Debug Phone File System:',phone_path DebugBrewProtocol._fs_path=os.path.normpath(phone_path) self._update_base_class(self.__class__) elif __debug__ and getattr(self, "protocolclass", 0) and \ getattr(self.protocolclass, "BREW_FILE_SYSTEM", 0) == 2: print '_set_new_brew', self.protocolclass def _update_base_class(self, klass): _bases=[] found=False for e in klass.__bases__: if e==RealBrewProtocol: _bases.append(DebugBrewProtocol) found=True else: _bases.append(e) if found: klass.__bases__=tuple(_bases) else: for e in _bases: self._update_base_class(e) def _set_new_brew(self, klass): _bases=[] found=False for e in klass.__bases__: if e==RealBrewProtocol: _bases.append(RealBrewProtocol2) found=True _bases.append(e) if found: klass.__bases__=tuple(_bases) else: for e in _bases: self._set_new_brew(e) """This is just a wrapper class that allows the manipulation between RealBrewProtocol and DebugBrewProtocol classes. """ def formatpacketerrorlog(str, origdata, data, klass): hd="" if data is not None: hd="Data - "+`len(data)`+" bytes\n" if klass is not None: try: hd+="<#! %s.%s !#>\n" % (klass.__module__, klass.__name__) except: klass=klass.__class__ hd+="<#! %s.%s !#>\n" % (klass.__module__, klass.__name__) hd+=common.datatohexstring(data) if origdata is not None: hd+="\nOriginal Data - "+`len(data)`+" bytes\n"+common.datatohexstring(origdata) return str+" "+hd def brewbasename(str): "returns basename of str" if str.rfind("/")>0: return str[str.rfind("/")+1:] return str def brewdirname(str): "returns dirname of str" if str.rfind("/")>0: return str[:str.rfind("/")] return str class SPURIOUSZERO (prototypes.BaseProtogenClass) : """This is a special class used to consume the spurious zero in some p_brew.listfileresponse The three bytes are formatted as follows: - An optional 'null' byte (this class) - A byte specifying how long the directory name portion is, including trailing slash - A byte specifying the length of the whole name - The bytes of the filename (which includes the full directory name) Fun and games ensue because files in the root directory have a zero length directory name, so we have some heuristics to try and distinguish if the first byte is the spurious zero or not Also allow for zero length filenames. """ def __init__(self, *args, **kwargs): super(SPURIOUSZERO,self).__init__(*args, **kwargs) self._value=None if self._ismostderived(SPURIOUSZERO): self._update(args, kwargs) def _update(self, args, kwargs): super(SPURIOUSZERO, self)._update(args, kwargs) self._complainaboutunusedargs(SPURIOUSZERO, kwargs) if len(args): raise TypeError("Unexpected arguments "+`args`) def readfrombuffer(self, buf): self._bufferstartoffset=buf.getcurrentoffset() while True: if buf.peeknextbyte()!=0: self._value=-1 break if buf.peeknextbyte(1)==0: if buf.howmuchmore()==2: break self._value=buf.getnextbyte() break all=buf.peeknextbytes(min(max(2+buf.peeknextbyte(1), 3+buf.peeknextbyte(2)), buf.howmuchmore())) ddirlen=ord(all[1]) dfulllen=ord(all[2]) if ddirlen<dfulllen and ddirlen<len(all)-3 and all[3+ddirlen-1]=='/': self._value=buf.getnextbyte() break self._value=-2 break self._bufferendoffset=buf.getcurrentoffset() def writetobuffer(self, buf): raise NotImplementedError() def packetsize(self): raise NotImplementedError() def getvalue(self): "Returns the string we are" if self._value is None: raise prototypes.ValueNotSetException() return self._value """This is a special class used to consume the spurious zero in some p_brew.listfileresponse The three bytes are formatted as follows: - An optional 'null' byte (this class) - A byte specifying how long the directory name portion is, including trailing slash - A byte specifying the length of the whole name - The bytes of the filename (which includes the full directory name) Fun and games ensue because files in the root directory have a zero length directory name, so we have some heuristics to try and distinguish if the first byte is the spurious zero or not Also allow for zero length filenames. """ file_cache=None class EmptyFileCache (object) : def __init__(self, bitpim_path): self._path=None self._cache_file_name=None self._data={ 'file_index': 0 } self.esn=None def hit(self, file_name, datetime, data_len): return False def data(self, file_name): return None def add(self, file_name, datetime, data): pass def clear(self, file_name): pass def set_path(self, bitpim_path): try: print 'setting path to',`bitpim_path` if not bitpim_path: raise ValueError self.__class__=FileCache self._path=os.path.join(bitpim_path, 'cache') self._cache_file_name=os.path.join(self._path, self._cache_index_file_name) self._check_path() self._read_index() self._write_index() except: self.__class__=EmptyFileCache class FileCache (object) : _cache_index_file_name='index.idx' current_version=1 def __init__(self, bitpim_path): self._path=os.path.join(bitpim_path, 'cache') self._cache_file_name=os.path.join(self._path, self._cache_index_file_name) self._data={ 'file_index': 0 } self.esn=None try: if not bitpim_path: raise ValueError self._check_path() self._read_index() self._write_index() except: self.__class__=EmptyFileCache def _check_path(self): try: os.makedirs(self._path) except: pass if not os.path.isdir(self._path): raise Exception("Bad cache directory: '"+self._path+"'") def _read_index(self): self._check_path() d={ 'result': {} } try: common.readversionedindexfile(self._cache_file_name, d, None, self.current_version) self._data.update(d['result']) except: print 'failed to read cache index file' def _write_index(self): self._check_path() common.writeversionindexfile(self._cache_file_name, self._data, self.current_version) def _entry(self, file_name): k=self._data.get(self.esn, None) if k: return k.get(file_name, None) def hit(self, file_name, datetime, data_len): try: e=self._entry(file_name) if e: return e['datetime'] and e['datetime']==datetime and \ e['size']==data_len return False except: if __debug__: raise return False def data(self, file_name): try: e=self._entry(file_name) if e: _data=file(os.path.join(self._path, e['cache']), 'rb').read() if len(_data)==e['size']: return _data except IOError: return None except: if __debug__: raise return None def add(self, file_name, datetime, data): try: if self.esn: e=self._entry(file_name) if not e: self._data.setdefault(self.esn, {})[file_name]={} e=self._data[self.esn][file_name] e['cache']='F%05d'%self._data['file_index'] self._data['file_index']+=1 e['datetime']=datetime e['size']=len(data) _cache_file_name=os.path.join(self._path, e['cache']) try: file(_cache_file_name, 'wb').write(data) self._write_index() except IOError: self._read_index() except: if __debug__: raise def clear(self, file_name): try: e=self._entry(file_name) if e: try: os.remove(os.path.join(self._path, e['cache'])) except: pass del self._data[self.esn][file_name] self._write_index() except: if __debug__: raise def set_path(self, bitpim_path): try: print 'setting path to',`bitpim_path` if not bitpim_path: raise ValueError self.__class__=FileCache self._path=os.path.join(bitpim_path, 'cache') self._cache_file_name=os.path.join(self._path, self._cache_index_file_name) self._check_path() self._read_index() self._write_index() except: raise self.__class__=EmptyFileCache
def _setmodemodem(self): self.log("_setmodemodem") req=p_brew.memoryconfigrequest() respc=p_brew.memoryconfigresponse try: self.comm.sendatcommand("Z") self.comm.sendatcommand('E0V1') return True except: pass for baud in 0, 38400,115200: if baud: if not self.comm.setbaudrate(baud): continue try: self.sendbrewcommand(req, respc, callsetmode=False) self.log('In BREW mode, trying to switch to Modem mode') if self._setmodebrewtomodem(): break return False except com_brew.modeignoreerrortypes: pass for baud in (0, 115200, 19200, 230400): self.log("Baud="+`baud`) if baud: if not self.comm.setbaudrate(baud): continue try: self.comm.sendatcommand("Z") self.comm.sendatcommand('E0V1') return True except: pass return False def _setmodephonebook(self): self.log("_setmodephonebook") self.setmode(self.MODEMODEM) self.setmode(self.MODEPHONEBOOK) return True def _setmodephonebooktomodem(self): self.log("_setmodephonebooktomodem") self.log('Switching from phonebook to modem') response=self.comm.sendatcommand("#PMODE=0") return True def sendpbcommand(self, request, responseclass, ignoreerror=False, fixup=None): """Similar to the sendpbcommand in com_sanyo and com_lg, except that a list of responses is returned, one per line of information returned from the phone""" buffer=prototypes.buffer() request.writetobuffer(buffer) data=buffer.getvalue() self.logdata("Samsung phonebook request", data, request) try: response_lines=self.comm.sendatcommand(data, ignoreerror=ignoreerror) except commport.ATError: self.comm.success=False self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") self.comm.success=True reslist=[] for line in response_lines: if fixup: line=fixup(line) self.logdata("Samsung phonebook response", line, responseclass) res=responseclass() buffer=prototypes.buffer(line) res.readfrombuffer(buffer) reslist.append(res) return reslist def get_esn(self): req=self.protocolclass.esnrequest() res=self.sendpbcommand(req, self.protocolclass.esnresponse) try: print res[0].esn return res[0].esn except: pass return '' def read_groups(self): g={} try: self.setmode(self.MODEPHONEBOOK) except: return g req=self.protocolclass.groupnamerequest() for i in range(self.protocolclass.NUMGROUPS+1): req.gid=i try: res=self.sendpbcommand(req, self.protocolclass.groupnameresponse) except: return g g[i]={'name': res[0].entry.groupname} return g def savegroups(self, data): """Write the groups, sending only those groups that have had a name change. (So that ringers don't get messed up)""" groups=data['groups'] groups_onphone=self.read_groups() if not groups_onphone: return keys=groups.keys() keys.sort() for k in keys: if groups[k]['name']!=groups_onphone[k]['name']: if groups[k]['name']!="Unassigned": req=self.protocolclass.groupnamesetrequest() req.gid=k req.groupname=groups[k]['name'] self.sendpbcommand(req, self.protocolclass.unparsedresponse, ignoreerror=True) def pblinerepair(self, line): "Repair a line from a phone with broken firmware" return line def getphonebook(self, result): """Read the phonebook data.""" pbook={} self.setmode(self.MODEPHONEBOOK) count=0 req=self.protocolclass.phonebookslotrequest() lastname="" for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1): req.slot=slot res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse, fixup=self.pblinerepair) if len(res) > 0: lastname=res[0].entry.name self.log(`slot`+": "+lastname) entry=self.extractphonebookentry(res[0].entry, result) pbook[count]=entry count+=1 self.progress(slot, self.protocolclass.NUMPHONEBOOKENTRIES, lastname) result['phonebook']=pbook cats=[] for i in result['groups']: if result['groups'][i]['name']!='Unassigned': cats.append(result['groups'][i]['name']) result['categories']=cats print "returning keys",result.keys() return pbook def extractphonebookentry(self, entry, fundamentals): res={} res['serials']=[ {'sourcetype': self.serialsname, 'slot': entry.slot, 'sourceuniqueid': fundamentals['uniqueserial']} ] res['names']=[ {'full': entry.name} ] cat=fundamentals['groups'].get(entry.group, {'name': "Unassigned"})['name'] if cat!="Unassigned": res['categories']=[ {'category': cat} ] if len(entry.email): res['emails']=[ {'email': entry.email} ] if len(entry.url): res['urls']=[ {'url': entry.url} ] res['numbers']=[] secret=0 speeddialtype=entry.speeddial numberindex=0 for type in self.numbertypetab: if len(entry.numbers[numberindex].number): numhash={'number': entry.numbers[numberindex].number, 'type': type } if entry.numbers[numberindex].secret==1: secret=1 if speeddialtype==numberindex: numhash['speeddial']=entry.uslot res['numbers'].append(numhash) numberindex+=1 res['flags']=[ {'secret': secret} ] if entry.ringtone != self.protocolclass.DEFAULT_RINGTONE: tone=self.serialsname+"Index_"+`entry.ringtone` res['ringtones']=[{'ringtone': tone, 'use': 'call'}] try: if entry.wallpaper != self.protocolclass.DEFAULT_WALLPAPER: tone=self.serialsname+"Index_"+`entry.wallpaper` res['wallpapers']=[{'wallpaper': tone, 'use': 'call'}] except: pass return res def savephonebook(self, data): "Saves out the phonebook" pb=data['phonebook'] keys=pb.keys() keys.sort() keys=keys[:self.protocolclass.NUMPHONEBOOKENTRIES] uslots={} names={} birthdays={} req=self.protocolclass.phonebookslotrequest() self.log('Erasing '+self.desc+' phonebook') progressmax=self.protocolclass.NUMPHONEBOOKENTRIES+len(keys) for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1): req.slot=slot self.progress(slot,progressmax,"Erasing "+`slot`) try: res=self.sendpbcommand(req,self.protocolclass.phonebookslotresponse, fixup=self.pblinerepair) if len(res) > 0: names[slot]=res[0].entry.name birthdays[slot]=res[0].entry.birthday if len(res[0].entry.url)>0: reqhack=self.protocolclass.phonebookslotupdaterequest() reqhack.entry=res[0].entry reqhack.entry.url="" reqhack.entry.ringtone=self.protocolclass.DEFAULT_RINGTONE reqhack.entry.wallpaper=self.protocolclass.DEFAULT_WALLPAPER reqhack.entry.timestamp=[1900,1,1,0,0,0] self.sendpbcommand(reqhack, self.protocolclass.phonebookslotupdateresponse) else: names[slot]="" except: names[slot]="" self.log("Slot "+`slot`+" read failed") reqerase=self.protocolclass.phonebooksloterase() reqerase.slot=slot self.sendpbcommand(reqerase, self.protocolclass.phonebookslotupdateresponse) self.savegroups(data) for i in range(len(keys)): slot=keys[i] req=self.protocolclass.phonebookslotupdaterequest() req.entry=self.makeentry(pb[slot],data) req.entry.ringtone=self.protocolclass.DEFAULT_RINGTONE req.entry.wallpaper=self.protocolclass.DEFAULT_WALLPAPER if names[slot]==req.entry.name: req.entry.birthday=birthdays[slot] self.log('Writing entry '+`slot`+" - "+req.entry.name) self.progress(i+self.protocolclass.NUMPHONEBOOKENTRIES,progressmax,"Writing "+req.entry.name) self.sendpbcommand(req, self.protocolclass.phonebookslotupdateresponse) self.progress(progressmax+1,progressmax+1, "Phone book write completed") return data def makeentry(self, entry, data): e=self.protocolclass.pbentry() for k in entry: if k=='numbertypes' or k=='secrets': continue if k=='ringtone': continue elif k=='wallpaper': continue elif k=='numbers': for numberindex in range(self.protocolclass.NUMPHONENUMBERS): enpn=self.protocolclass.phonenumber() e.numbers.append(enpn) for i in range(len(entry[k])): numberindex=entry['numbertypes'][i] e.numbers[numberindex].number=entry[k][i] e.numbers[numberindex].secret=entry['secrets'][i] continue setattr(e, k, entry[k]) return e def getcalendar(self, result): entries = {} self.log("Getting calendar entries") self.setmode(self.MODEPHONEBOOK) req=self.protocolclass.eventrequest() cal_cnt=0 for slot in range(self.protocolclass.NUMCALENDAREVENTS): req.slot=slot res=self.sendpbcommand(req,self.protocolclass.eventresponse) if len(res) > 0: self.progress(slot+1, self.protocolclass.NUMCALENDAREVENTS, res[0].eventname) entry=bpcalendar.CalendarEntry() entry.start=res[0].start[0:5] if res[0].end: entry.end=res[0].end[0:5] else: entry.end=entry.start entry.description=res[0].eventname try: alarm=self.__cal_alarm_values[res[0].alarm] except: alarm=None entry.alarm=alarm entries[entry.id]=entry cal_cnt += 1 result['calendar']=entries self.setmode(self.MODEMODEM) return result def _set_unused_calendar_fields(self, entry): entry['repeat']=None entry['changeserial']=1 entry['snoozedelay']=0 entry['daybitmap']=0 entry['ringtone']=0 def process_calendar(self, dict): """ Optimize and expand calendar data suitable for phone download """ r={} rp=[] today=datetime.date.today() last_date=today if __debug__: print 'original calendar:' for k,e in dict.items(): if __debug__: print e.description,':',e.start sd=datetime.date(*e.start[:3]) ed=datetime.date(*e.end[:3]) if ed>last_date: last_date=ed if e.repeat is None: if sd>=today: r.setdefault(e.start[:3], []).append(Samsung_Calendar(e)) else: if ed>=today: rp.append(e) delta_1=datetime.timedelta(1) for n in rp: current_date=today end_date=datetime.date(*n.end[:3]) cnt=0 while current_date<=end_date: if n.is_active(current_date.year, current_date.month, current_date.day): cd_l=(current_date.year, current_date.month, current_date.day) r.setdefault(cd_l, []).append(\ Samsung_Calendar(n, cd_l)) cnt+=1 if cnt>self.protocolclass.NUMCALENDAREVENTS: break current_date+=delta_1 res=[] keys=r.keys() keys.sort() for k in keys: r[k].sort() if len(r[k])>self._cal_max_events_per_day: res+=r[k][:self._cal_max_events_per_day] else: res+=r[k] if len(res)>self.protocolclass.NUMCALENDAREVENTS: res=res[:self.protocolclass.NUMCALENDAREVENTS] return res def savecalendar(self, dict, merge): self.log("Sending calendar entries") cal=self.process_calendar(dict['calendar']) if __debug__: print 'processed calendar: ', len(cal), ' items' for c in cal: print c.description,':', c.start self.setmode(self.MODEPHONEBOOK) self.log("Saving calendar entries") cal_cnt=0 req=self.protocolclass.eventupdaterequest() l = self.protocolclass.NUMCALENDAREVENTS for c in cal: req.slot=cal_cnt req.start=list(c.start)+[0] if self.__cal_end_datetime_value is None: req.end=list(c.end)+[0] else: req.end=req.start req.timestamp=list(time.localtime(time.time())[0:6]) req.alarm=c.alarm name=c.description if len(name)>self.__cal_max_name_len: name=name[:self.__cal_max_name_len] req.eventname=name self.progress(cal_cnt+1, l, "Updating "+name) self.sendpbcommand(req,self.protocolclass.eventupdateresponse) cal_cnt += 1 self.log('Deleting unused entries') for k in range(cal_cnt, l): self.progress(k, l, "Deleting entry %d" % k) reqerase=self.protocolclass.eventsloterase() reqerase.slot=k self.sendpbcommand(reqerase, self.protocolclass.eventupdateresponse) self.setmode(self.MODEMODEM) return dict def gettodo(self, result): todos = {} self.log("Getting todo entries") self.setmode(self.MODEPHONEBOOK) req=self.protocolclass.todorequest() for slot in range(self.protocolclass.NUMTODOENTRIES): req.slot=slot res=self.sendpbcommand(req,self.protocolclass.todoresponse) if len(res) > 0: entry = todo.TodoEntry() entry.summary=res[0].subject entry.due_date='%4.4d%2.2d%2.2d'%(res[0].duedate[0],res[0].duedate[1],res[0].duedate[2]) if res[0].priority: entry.priority=1 else: entry.priority=10 self.log("Todo "+`slot`+" "+entry.summary+" "+entry.due_date) todos[entry.id]=entry result['todo']=todos return result def savetodo(self, dict, merge): self.setmode(self.MODEPHONEBOOK) todos=dict.get('todo', {}) todos_len=len(todos) l=self.protocolclass.NUMTODOENTRIES if todos_len > l: self.log("The number of Todo entries (%d) exceeded the mamximum (%d)" % (cal_len, l)) self.setmode(self.MODEPHONEBOOK) self.log("Saving todo entries") todo_cnt=0 req=self.protocolclass.todoupdaterequest() for k in todos: todo=todos[k] print todo.__doc__ if todo_cnt >= l: break req.slot=todo_cnt if todo.priority is not None and todo.priority<5: req.priority=0 else: req.priority=1 dd=todo.due_date req.duedate=(int(dd[0:3]),int(dd[4:5]),int(dd[6:7]),0,0,0) req.timestamp=list(time.localtime(time.time())[0:6]) req.subject=todo.summary self.sendpbcommand(req,self.protocolclass.todoupdateresponse) todo_cnt += 1 req=self.protocolclass.todoerase() for slot in range(todo_cnt, self.protocolclass.NUMTODOENTRIES): req.slot=slot self.sendpbcommand(req,self.protocolclass.todoupdateresponse) def getmemo(self, result): memos = {} self.log("Getting memo entries") self.setmode(self.MODEPHONEBOOK) req=self.protocolclass.memorequest() for slot in range(self.protocolclass.NUMMEMOENTRIES): req.slot=slot res=self.sendpbcommand(req,self.protocolclass.memoresponse) if len(res) > 0: entry=memo.MemoEntry() entry.text=res[0].text entry.set_date_isostr='%4.4d%2.2d%2.2dT%2.2d%2.2d%2.2d'%(res[0].timestamp[0],res[0].timestamp[1],res[0].timestamp[2],res[0].timestamp[3],res[0].timestamp[4],res[0].timestamp[5]) memos[entry.id]=entry result['memo']=memos return result def savememo(self, dict, merge): self.setmode(self.MODEPHONEBOOK) memos=dict.get('memo', {}) memos_len=len(memos) l=self.protocolclass.NUMMEMOENTRIES if memos_len > l: self.log("The number of Memo entries (%d) exceeded the mamximum (%d)" % (cal_len, l)) self.setmode(self.MODEPHONEBOOK) self.log("Saving memo entries") memo_cnt=0 req=self.protocolclass.memoupdaterequest() for k in memos: memo=memos[k] if memo_cnt >= l: break dd=memo.set_date_isostr req.timestamp=list(time.localtime(time.time())[0:6]) req.text=memo.text self.sendpbcommand(req,self.protocolclass.memoupdateresponse) memo_cnt += 1 req=self.protocolclass.memoerase() for slot in range(memo_cnt, self.protocolclass.NUMMEMOENTRIES): req.slot=slot self.sendpbcommand(req,self.protocolclass.memoupdateresponse) getcallhistory=None "Talk to a Samsung phone using AT commands" class Profile (com_phone.Profile) : BP_Calendar_Version=3 usbids=( ( 0x04e8, 0x6601, 1), ) deviceclasses=("modem","serial") WALLPAPER_WIDTH=128 WALLPAPER_HEIGHT=118 OVERSIZE_PERCENTAGE=100 MAX_WALLPAPER_BASENAME_LENGTH=19 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .`~!@#$%^&()-_=+[{]};\'" WALLPAPER_CONVERT_FORMAT="png" MAX_RINGTONE_BASENAME_LENGTH=19 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .`~!@#$%^&()-_=+[{]};\'" _supportedsyncs=() def __init__(self): com_phone.Profile.__init__(self) def _getgroup(self, name, groups): for key in groups: if groups[key]['name']==name: return key,groups[key] return None,None def normalisegroups(self, helper, data): "Assigns groups based on category data" pad=[] keys=data['groups'].keys() keys.sort() for k in keys: if k==self.protocolclass.NUMGROUPS: name=data['groups'][k]['name'] pad.append(name) groups=helper.getmostpopularcategories(self.protocolclass.NUMGROUPS, data['phonebook'], ["Unassigned"], 12, pad) groups.sort() newgroups={} newgroups[self.protocolclass.NUMGROUPS]={'name': 'Unassigned'} for name in groups: if name=="Unassigned": continue key,value=self._getgroup(name, data['groups']) if key is not None: newgroups[key]=value for name in groups: key,value=self._getgroup(name, newgroups) if key is None: for key in range(self.protocolclass.NUMGROUPS): if key not in newgroups: newgroups[key]={'name': name, 'icon': 1} break if data['groups']!=newgroups: data['groups']=newgroups def convertphonebooktophone(self, helper, data): """Converts the data to what will be used by the phone @param data: contains the dict returned by getfundamentals as well as where the results go""" self.normalisegroups(helper, data) results={} pb=data['phonebook'] slots=[ (helper.getserial(pb[pbentry].get("serials", []), self.serialsname, data['uniqueserial'], "slot", None), pbentry) for pbentry in pb] slots.sort() newones=[(pbentry,slot) for slot,pbentry in slots if slot is None] existing=[(pbentry,slot) for slot,pbentry in slots if slot is not None] uslotsused={} tempslot=0 for pbentry,slot in existing+newones: if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES: break try: e={} entry=data['phonebook'][pbentry] secret=helper.getflag(entry.get('flags', []), 'secret', False) if secret: secret=1 else: secret=0 e['name']=helper.getfullname(entry.get('names', []),1,1,20)[0] cat=helper.makeone(helper.getcategory(entry.get('categories',[]),0,1,12), None) if cat is None: e['group']=self.protocolclass.NUMGROUPS else: key,value=self._getgroup(cat, data['groups']) if key is not None: e['group']=key else: e['group']=self.protocolclass.NUMGROUPS e['email']=helper.makeone(helper.getemails(entry.get('emails', []), 0,1,32), "") e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,32), "") minnumbers=1 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS) e['numbertypes']=[] e['numbers']=[] e['secrets']=[] unusednumbers=[] typesused={} defaulttypenum=0 for num in numbers: typename=num['type'] if typesused.has_key(typename): unusednumbers.append(num) continue typesused[typename]=1 for typenum,tnsearch in enumerate(self.numbertypetab): if typename==tnsearch: if defaulttypenum==0: defaulttypenum=typenum number=self.phonize(num['number']) if len(number)>self.protocolclass.MAXNUMBERLEN: number=number[:self.protocolclass.MAXNUMBERLEN] e['numbers'].append(number) if(num.has_key('speeddial')): e['speeddial']=typenum tryuslot = num['speeddial'] e['numbertypes'].append(typenum) e['secrets'].append(secret) break if e.has_key('speeddial'): if tryuslot>=1 and tryuslot<=self.protocolclass.NUMPHONEBOOKENTRIES and not uslotsused.has_key(tryuslot): uslotsused[tryuslot]=1 e['uslot']=tryuslot else: e['speeddial']=defaulttypenum e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) if slot is None or slot<1 or slot>self.protocolclass.NUMPHONEBOOKENTRIES or slot in results: for i in range(1,100000): if i not in results: slot=i break e['slot']=slot e['timestamp']=list(time.localtime(time.time())[0:6]) results[slot]=e except helper.ConversionFailed: continue tryuslot=1 for slot in results.keys(): e=results[slot] if not e.has_key('uslot'): while tryuslot<self.protocolclass.NUMPHONEBOOKENTRIES and uslotsused.has_key(tryuslot): tryuslot += 1 uslotsused[tryuslot]=1 e['uslot'] = tryuslot results[slot] = e data['phonebook']=results return data def phonize(self,str): """Convert the phone number into something the phone understands All digits, P, T, * and # are kept, everything else is removed""" return re.sub("[^0-9PT#*]", "", str)[:self.protocolclass.MAXNUMBERLEN] class Samsung_Calendar : _cal_alarm_values={ 10: 0, 30: 1, 60: 2, -1: 3, 0: 4 } def __init__(self, calendar_entry, new_date=None): self._start=self._end=self._alarm=self._desc=None self._extract_cal_info(calendar_entry, new_date) def _extract_cal_info(self, cal_entry, new_date): s=cal_entry.start if new_date is not None: s=new_date[:3]+s[3:] self._start=s self._end=cal_entry.end self._desc=cal_entry.description self._alarm=0 alarm=cal_entry.alarm _keys=self._cal_alarm_values.keys() _keys.sort() _keys.reverse() for k in _keys: if alarm>=k: self._alarm=self._cal_alarm_values[k] break def __lt__(self, rhs): return self.start<rhs.start def __le__(self, rhs): return self.start<=rhs.start def __eq__(self, rhs): return self.start==rhs.start def __ne__(self, rhs): return self.start!=rhs.start def __gt__(self, rhs): return self.start>rhs.start def __ge__(self, rhs): return self.start>=rhs.start def _get_start(self): return self._start start=property(fget=_get_start) def _get_end(self): return self._end end=property(fget=_get_end) def _get_desc(self): return self._desc description=property(fget=_get_desc) def _get_alarm(self): return self._alarm alarm=property(fget=_get_alarm)