def sendpbcommand(self, request, responseclass): self.setmode(self.MODEBREW) buffer = prototypes.buffer() request.writetobuffer(buffer, logtitle="audiovox cdm8900 phonebook request") data = buffer.getvalue() data = common.pppescape(data + common.crcs(data)) + common.pppterminator first = data[0] try: data = self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False) except com_phone.modeignoreerrortypes: self.mode = self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") self.comm.success = True origdata = data # sometimes there is junk at the begining, eg if the user # turned off the phone and back on again. So if there is more # than one 7e in the escaped data we should start after the # second to last one d = data.rfind(common.pppterminator, 0, -1) if d >= 0: self.log( "Multiple PB packets in data - taking last one starting at " + ` d + 1 `) self.logdata("Original pb data", origdata, None) data = data[d + 1:] # turn it back to normal data = common.pppunescape(data) # sometimes there is other crap at the begining d = data.find(first) if d > 0: self.log("Junk at begining of pb packet, data at " + ` d `) self.logdata("Original pb data", origdata, None) self.logdata("Working on pb data", data, None) data = data[d:] # take off crc and terminator crc = data[-3:-1] data = data[:-3] if common.crcs(data) != crc: self.logdata("Original pb data", origdata, None) self.logdata("Working on pb data", data, None) raise common.CommsDataCorruption( "Audiovox phonebook packet failed CRC check", self.desc) # parse data buffer = prototypes.buffer(data) res = responseclass() res.readfrombuffer(buffer, logtitle="Audiovox phonebook response") return res
def sendpbcommand(self, request, responseclass): self.setmode(self.MODEBREW) buffer=prototypes.buffer() request.writetobuffer(buffer, logtitle="audiovox cdm8900 phonebook request") data=buffer.getvalue() data=common.pppescape(data+common.crcs(data))+common.pppterminator first=data[0] try: data=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False) except com_phone.modeignoreerrortypes: self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") self.comm.success=True origdata=data # sometimes there is junk at the begining, eg if the user # turned off the phone and back on again. So if there is more # than one 7e in the escaped data we should start after the # second to last one d=data.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple PB packets in data - taking last one starting at "+`d+1`) self.logdata("Original pb data", origdata, None) data=data[d+1:] # turn it back to normal data=common.pppunescape(data) # sometimes there is other crap at the begining d=data.find(first) if d>0: self.log("Junk at begining of pb packet, data at "+`d`) self.logdata("Original pb data", origdata, None) self.logdata("Working on pb data", data, None) data=data[d:] # take off crc and terminator crc=data[-3:-1] data=data[:-3] if common.crcs(data)!=crc: self.logdata("Original pb data", origdata, None) self.logdata("Working on pb data", data, None) raise common.CommsDataCorruption("Audiovox phonebook packet failed CRC check", self.desc) # parse data buffer=prototypes.buffer(data) res=responseclass() res.readfrombuffer(buffer, logtitle="Audiovox phonebook response") return res
def sendpbcommand(self, request, responseclass, callsetmode=True): if callsetmode: self.setmode(self.MODEPHONEBOOK) buffer=prototypes.buffer() request.header.sequence=self.pbseq self.pbseq+=1 if self.pbseq>0xff: self.pbseq=0 request.writetobuffer(buffer, logtitle="lg phonebook request") data=buffer.getvalue() data=common.pppescape(data+common.crcs(data))+common.pppterminator firsttwo=data[:2] try: data=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False) except com_phone.modeignoreerrortypes: self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") self.comm.success=True origdata=data d=data.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple LG packets in data - taking last one starting at "+`d+1`) self.logdata("Original LG data", origdata, None) data=data[d+1:] data=common.pppunescape(data) crc=data[-3:-1] data=data[:-3] calccrc=common.crcs(data) if calccrc!=crc: d=data.find(firsttwo) if d>0: self.log("Junk at begining of LG packet, data at "+`d`) self.logdata("Original LG data", origdata, None) self.logdata("Working on LG data", data, None) data=data[d:] calccrc=common.crcs(data) if calccrc!=crc: self.logdata("Original LG data", origdata, None) self.logdata("Working on LG data", data, None) raise common.CommsDataCorruption("LG packet failed CRC check", self.desc) if ord(data[0])==0x13: raise com_brew.BrewBadBrewCommandException() if ord(data[0])==0x14: raise com_brew.BrewMalformedBrewCommandException() buffer=prototypes.buffer(data) res=responseclass() res.readfrombuffer(buffer, logtitle="lg phonebook response") return res
def modemmoderequest(self): self.log("Attempting to put phone in modem mode") req=p_brew.setmodemmoderequest() buffer=prototypes.buffer() req.writetobuffer(buffer, logtitle="modem mode request") data=buffer.getvalue() data=common.pppescape(data+common.crcs(data))+common.pppterminator self.comm.write(data) self.comm.readsome(numchars=5) self.mode=self.MODENONE # Probably should add a modem mode
def sendpbcommand(self, request, responseclass): self.setmode(self.MODEBREW) buffer=prototypes.buffer() request.writetobuffer(buffer) data=buffer.getvalue() self.logdata("toshiba vm4050 phonebook request", data, request) data=common.pppescape(data+common.crcs(data))+common.pppterminator first=data[0] try: data=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False) except com_phone.modeignoreerrortypes: self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") self.comm.success=True origdata=data d=data.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple PB packets in data - taking last one starting at "+`d+1`) self.logdata("Original pb data", origdata, None) data=data[d+1:] data=common.pppunescape(data) d=data.find(first) if d>0: self.log("Junk at begining of pb packet, data at "+`d`) self.logdata("Original pb data", origdata, None) self.logdata("Working on pb data", data, None) data=data[d:] crc=data[-3:-1] data=data[:-3] if common.crcs(data)!=crc: self.logdata("Original pb data", origdata, None) self.logdata("Working on pb data", data, None) raise common.CommsDataCorruption("toshiba phonebook packet failed CRC check", self.desc) self.logdata("toshiba phonebook response", data, responseclass) buffer=prototypes.buffer(data) res=responseclass() res.readfrombuffer(buffer) return res
def sendbrewcommand(self, request, responseclass, callsetmode=True): if callsetmode: self.setmode(self.MODEBREW) buffer=prototypes.buffer() request.writetobuffer(buffer, logtitle="sendbrewcommand") data=buffer.getvalue() 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) crc=data[-3:-1] data=data[:-3] calccrc=common.crcs(data) if calccrc!=crc: 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:] 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": # Y is 0x59 which is brew command prefix 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() if err==0x04: raise BrewAccessDeniedException() if err==0x16: raise BrewFileSystemFullException() raise BrewCommandException(err) if ord(data[0])==0x13: if firsttwo[0]=="Y": # brew command raise BrewAccessDeniedException() else: raise BrewBadBrewCommandException() if ord(data[0])==0x14: raise BrewMalformedBrewCommandException() buffer=prototypes.buffer(data) res=responseclass() try: res.readfrombuffer(buffer, autolog=False) except: self.log(formatpacketerrorlog("Error decoding response", origdata, data, responseclass)) raise return res
def sendpbcommand(self, request, responseclass): self.setmode(self.MODEBREW) buffer=prototypes.buffer() request.writetobuffer(buffer) data=buffer.getvalue() self.logdata("toshiba vm4050 phonebook request", data, request) data=common.pppescape(data+common.crcs(data))+common.pppterminator first=data[0] try: data=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False) except com_phone.modeignoreerrortypes: self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") self.comm.success=True origdata=data d=data.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple PB packets in data - taking last one starting at "+`d+1`) self.logdata("Original pb data", origdata, None) data=data[d+1:] data=common.pppunescape(data) d=data.find(first) if d>0: self.log("Junk at begining of pb packet, data at "+`d`) self.logdata("Original pb data", origdata, None) self.logdata("Working on pb data", data, None) data=data[d:] crc=data[-3:-1] data=data[:-3] if common.crcs(data)!=crc: self.logdata("Original pb data", origdata, None) self.logdata("Working on pb data", data, None) raise common.CommsDataCorruption("toshiba phonebook packet failed CRC check", self.desc) self.logdata("toshiba phonebook response", data, responseclass) buffer=prototypes.buffer(data) res=responseclass() res.readfrombuffer(buffer) return res def get_detect_data(self, res): self.modemmoderequest() res['manufacturer']=self.comm.sendatcommand('+GMI')[0] res['model']=self.comm.sendatcommand('+GMM')[0] res['firmware_version']=self.comm.sendatcommand('+GMR')[0] res['esn']=self.comm.sendatcommand('+GSN')[0][2:] return def detectphone(coms, likely_ports, res, _module, _log): if not likely_ports: return None for port in likely_ports: if not res.has_key(port): res[port]={ 'mode_modem': None, 'mode_brew': None, 'manufacturer': None, 'model': None, 'firmware_version': None, 'esn': None, 'firmwareresponse': None } try: if res[port]['model']: continue p=_module.Phone(_log, commport.CommConnection(_log, port, timeout=1)) res[port]['mode_brew']=p._setmodebrew() if res[port]['mode_brew']: p.get_detect_data(res[port]) p.comm.close() except: if __debug__: raise detectphone=staticmethod(detectphone) "Talk to Toshiba VM4050 cell phone" class Profile (com_phone.Profile) : protocolclass=Phone.protocolclass serialsname=Phone.serialsname phone_manufacturer='Audiovox Communications Corporation' phone_model='VM4050' usbids=((0x05C6, 0x3100, 1), ) deviceclasses=("modem",) _supportedsyncs=( ('phonebook', 'read', None), ('phonebook', 'write', 'OVERWRITE'), ) 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""" results={} slotsused={} for pbentry in data['phonebook']: entry=data['phonebook'][pbentry] serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', -1) if(serial1 >= 0 and serial1 < self.protocolclass.NUMSLOTS): slotsused[serial1]=1 lastunused=0 for pbentry in data['phonebook']: if len(results)==self.protocolclass.NUMSLOTS: break e={} entry=data['phonebook'][pbentry] try: e['name']=helper.getfullname(entry.get('names', []),1,1,36)[0] serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', -1) if(serial1 >= 0 and serial1 < self.protocolclass.NUMSLOTS): e['slot']=serial1 else: while(slotsused.has_key(lastunused)): lastunused+=1 if(lastunused >= self.protocolclass.NUMSLOTS): helper.add_error_message("Name: %s. Unable to add, phonebook full" % e['name']) raise helper.ConversionFailed() e['slot']=lastunused slotsused[lastunused]=1 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.MAXEMAILS,self.protocolclass.MAXEMAILLEN) e['emails']=helper.filllist(emails, self.protocolclass.MAXEMAILS, "") e['web_page']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,48), "") numbers=helper.getnumbers(entry.get('numbers', []),0,self.protocolclass.MAXPHONENUMBERS) e['numbertypes']=[] e['numbers']=[] typesused={} for num in numbers: type=num['type'] if(typesused.has_key(type)): continue typesused[type]=1 for typenum,tnsearch in enumerate(self.protocolclass.numbertypetab): if type==tnsearch: number=self.phonize(num['number']) if len(number)==0: continue if len(number)>self.protocolclass.MAXPHONENUMBERLEN: number=number[:self.protocolclass.MAXPHONENUMBERLEN] e['numbers'].append(number) e['numbertypes'].append(typenum) break e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) e['secret']=helper.getflag(entry.get('flags',[]), 'secret', False) results[pbentry]=e except helper.ConversionFailed: continue data['phonebook']=results return data def phonize(self, str): """Convert the phone number into something the phone understands All digits, P, T, *, # are kept, everything else is removed. In theory the phone can store a dash in the phonebook, but that is not normal.""" return re.sub("[^0-9PT#*]", "", str)[:self.protocolclass.MAXPHONENUMBERLEN]
def sendpbcommand(self, request, responseclass, callsetmode=True, writemode=False, numsendretry=0, returnerror=False): if writemode: numretry=3 else: numretry=0 if callsetmode: self.setmode(self.MODEPHONEBOOK) buffer=prototypes.buffer() request.writetobuffer(buffer) data=buffer.getvalue() firsttwo=data[:2] data=common.pppescape(data+common.crcs(data))+common.pppterminator self.logdata("N400 phonebook request", data, request) isendretry=numsendretry while isendretry>=0: try: rdata=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False, numfailures=numretry) break except com_phone.modeignoreerrortypes: if isendretry>0: self.log("Resending request packet...") time.sleep(0.3) else: self.comm.success=False self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") isendretry-=1 self.comm.success=True origdata=rdata d=rdata.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple N400 packets in data - taking last one starting at "+`d+1`) self.logdata("Original N400 data", origdata, None) rdata=rdata[d+1:] data=common.pppunescape(rdata) d=data.find(firsttwo) crc=data[-3:-1] crcok=False for i in range(0,d+1): trydata=data[i:-3] if common.crcs(trydata)==crc: crcok=True break if not crcok: self.logdata("first two",firsttwo, None) self.logdata("Original N400 data", origdata, None) self.logdata("Working on N400 data", data, None) raise common.CommsDataCorruption("N400 packet failed CRC check", self.desc) res=responseclass() if d>0: if d==i: self.log("Junk at beginning of N400 packet, data at "+`d`) self.logdata("Original N400 data", origdata, None) self.logdata("Working on N400 data", data, None) else: if returnerror: res=self.protocolclass.sanyoerror() else: self.log("N400 Error code "+`ord(data[0])`) self.logdata("Samsung phonebook response", data, None) raise N400CommandException(ord(data[0])) data=trydata self.logdata("sanyo phonebook response", data, responseclass) buffer=prototypes.buffer(data) res.readfrombuffer(buffer) return res
def modemmoderequest(self): self.log("Attempting to put phone in modem mode") req=p_brew.setmodemmoderequest() buffer=prototypes.buffer() req.writetobuffer(buffer) data=buffer.getvalue() self.logdata("brew request", data, req) data=common.pppescape(data+common.crcs(data))+common.pppterminator self.comm.write(data) self.comm.readsome(numchars=5) self.mode=self.MODENONE def mkdir(self, name): self.log("Making directory '"+name+"'") req=p_brew.mkdirrequest() req.dirname=name self.sendbrewcommand(req, p_brew.mkdirresponse) def mkdirs(self, directory): if len(directory)<1: return dirs=directory.split('/') for i in range(0,len(dirs)): try: self.mkdir("/".join(dirs[:i+1])) except: pass def rmdir(self,name): self.log("Deleting directory '"+name+"'") req=p_brew.rmdirrequest() req.dirname=name self.sendbrewcommand(req, p_brew.rmdirresponse) def rmfile(self,name): self.log("Deleting file '"+name+"'") req=p_brew.rmfilerequest() req.filename=name self.sendbrewcommand(req, p_brew.rmfileresponse) file_cache.clear(name) def rmdirs(self, path): self.progress(0,1, "Listing child files and directories") all=self.getfilesystem(path, 100) keys=all.keys() keys.sort() keys.reverse() count=0 for k in keys: self.progress(count, len(keys), "Deleting "+k) count+=1 if all[k]['type']=='directory': self.rmdir(k) else: self.rmfile(k) self.rmdir(path) def exists(self, path): req=p_brew.listfilerequest() req.dirname=path req.entrynumber=0 try: res=self.sendbrewcommand(req,p_brew.listfileresponse) except (BrewBadPathnameException, BrewNoSuchDirectoryException, ValueError): return False except: if __debug__: raise return False return True def listsubdirs(self, dir='', recurse=0): results={} self.log("Listing subdirs in dir: '"+dir+"'") req=p_brew.listdirectoryrequest() req.dirname=dir for i in xrange(10000): try: req.entrynumber=i res=self.sendbrewcommand(req,p_brew.listdirectoryresponse) f=res.subdir.rfind("/") if f>=0: subdir=res.subdir[f+1:] else: subdir=res.subdir if len(dir): subdir=dir+"/"+subdir results[subdir]={ 'name': subdir, 'type': 'directory' } except BrewNoMoreEntriesException: break except (BrewBadPathnameException, ValueError): self.log('Failed to list dir '+dir) return {} if recurse: for k,_subdir in results.items(): results.update(self.listsubdirs(_subdir['name'], recurse-1)) return results def hassubdirs(self, dir=''): self.log('Checking for subdirs in dir: "'+dir+'"') req=p_brew.listdirectoryrequest() req.dirname=dir req.entrynumber=0 try: res=self.sendbrewcommand(req,p_brew.listdirectoryresponse) return True except BrewNoMoreEntriesException: return False except: if __debug__: raise return False def listfiles(self, dir=''): results={} self.log("Listing files in dir: '"+dir+"'") req=p_brew.listfilerequest() req.dirname=dir for i in xrange(10000): try: req.entrynumber=i res=self.sendbrewcommand(req,p_brew.listfileresponse) results[res.filename]={ 'name': res.filename, 'type': 'file', 'size': res.size } if res.date==0: results[res.filename]['date']=(0, "") else: try: date=res.date+self._brewepochtounix results[res.filename]['date']=(date, time.strftime("%x %X", time.gmtime(date))) except: results[res.filename]['date']=(0, "") except BrewNoMoreEntriesException: break except (BrewBadPathnameException, ValueError): self.log('Failed to list files in dir '+dir) return {} return results def getfilesystem(self, dir="", recurse=0): self.log("Getting file system in dir '"+dir+"'") results=self.listsubdirs(dir) subdir_list=[x['name'] for k,x in results.items()] results.update(self.listfiles(dir)) if recurse: for _subdir in subdir_list: results.update(self.getfilesystem(_subdir, recurse-1)) return results def statfile(self, name): try: self.log('stat file '+name) req=p_brew.statfilerequest() req.filename=name res=self.sendbrewcommand(req, p_brew.statfileresponse) results={ 'name': name, 'type': 'file', 'size': res.size, 'datevalue': res.date } if res.date==0: results['date']=(0, '') else: try: date=res.date+self._brewepochtounix results['date']=(date, time.strftime("%x %X", time.gmtime(date))) except: results['date']=(0, '') return results except BrewNoSuchFileException: return None except: if __debug__: raise return None def setfileattr(self, filename, date): self.log('set file date '+filename +`date`) req=p_brew.setfileattrrequest() req.date=date-self._brewepochtounix req.filename=filename self.sendbrewcommand(req, p_brew.setfileattrresponse) 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 sendpbcommand(self, request, responseclass, callsetmode=True, writemode=False, numsendretry=0, returnerror=False): if writemode: numretry=3 else: numretry=0 if callsetmode: self.setmode(self.MODEPHONEBOOK) buffer=prototypes.buffer() request.writetobuffer(buffer) data=buffer.getvalue() firsttwo=data[:2] data=common.pppescape(data+common.crcs(data))+common.pppterminator self.logdata("N400 phonebook request", data, request) isendretry=numsendretry while isendretry>=0: try: rdata=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False, numfailures=numretry) break except com_phone.modeignoreerrortypes: if isendretry>0: self.log("Resending request packet...") time.sleep(0.3) else: self.comm.success=False self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") isendretry-=1 self.comm.success=True origdata=rdata d=rdata.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple N400 packets in data - taking last one starting at "+`d+1`) self.logdata("Original N400 data", origdata, None) rdata=rdata[d+1:] data=common.pppunescape(rdata) d=data.find(firsttwo) crc=data[-3:-1] crcok=False for i in range(0,d+1): trydata=data[i:-3] if common.crcs(trydata)==crc: crcok=True break if not crcok: self.logdata("first two",firsttwo, None) self.logdata("Original N400 data", origdata, None) self.logdata("Working on N400 data", data, None) raise common.CommsDataCorruption("N400 packet failed CRC check", self.desc) res=responseclass() if d>0: if d==i: self.log("Junk at beginning of N400 packet, data at "+`d`) self.logdata("Original N400 data", origdata, None) self.logdata("Working on N400 data", data, None) else: if returnerror: res=self.protocolclass.sanyoerror() else: self.log("N400 Error code "+`ord(data[0])`) self.logdata("Samsung phonebook response", data, None) raise N400CommandException(ord(data[0])) data=trydata self.logdata("sanyo phonebook response", data, responseclass) buffer=prototypes.buffer(data) res.readfrombuffer(buffer) return res def getphonebook(self, result): pbook={} reqname=self.protocolclass.phonebooknamerequest() reqnumbers=self.protocolclass.phonebooknumbersrequest() count = 0 for i in range(2, 251): reqname.slot=i resname=self.sendpbcommand(reqname, self.protocolclass.phonebooknameresponse) if resname.entry.nonzeroifused: entry={} entry['serials']=[ {'sourcetype': self.serialsname, 'slot': i, 'sourceuniqueid': result['uniqueserial']} ] entry['names']=[{'full': resname.entry.name} ] entry['numbers'] = [] for i in range(7): numptr = resname.entry.numbers[i].pnumber if numptr: reqnumbers.slot=numptr resnumbers=self.sendpbcommand(reqnumbers, self.protocolclass.phonebooknumbersresponse) numhash={'number': resnumbers.entry.number, 'type': numbertypetab[i]} if numptr==resname.entry.pspeed: numhash['speeddial']=i entry['numbers'].append(numhash) if resname.entry.pemail: reqnumbers.slot=resname.entry.pemail resnumbers=self.sendpbcommand(reqnumbers, self.protocolclass.phonebooknumbersresponse) entry['emails']=[{'email': resnumbers.entry.number}] if resname.entry.purl: reqnumbers.slot=resname.entry.purl resnumbers=self.sendpbcommand(reqnumbers, self.protocolclass.phonebooknumbersresponse) entry['urls']=[{'url': resnumbers.entry.number}] self.log("Read entry "+`i`+": "+resname.entry.name) pbook[count]=entry count+=1 result['phonebook']=pbook return result def getcalendar(self, result): pbook={} result['calendar']=pbook return result def getwallpapers(self, results): pass def getringtones(self, results): pass "Talk to a Samsung SPH-N400 cell phone" class Profile (com_phone.Profile) : protocolclass=Phone.protocolclass serialsname=Phone.serialsname phone_manufacturer='SAMSUNG' phone_model='SPH-N400' usbids_usbtoserial=( ( 0x067b, 0x2303, None), ( 0x0403, 0x6001, None), ( 0x0731, 0x2003, None), ) usbids=usbids_usbtoserial deviceclasses=("serial",) def __init__(self): com_phone.Profile.__init__(self) _supportedsyncs=( ('phonebook', 'read', None), )
def sendpbcommand(self, request, responseclass, callsetmode=True, writemode=False, numsendretry=0, returnerror=False): if writemode: numretry=3 else: numretry=0 if callsetmode: self.setmode(self.MODEPHONEBOOK) buffer=prototypes.buffer() request.writetobuffer(buffer, logtitle="Samsung phonebook request") data=buffer.getvalue() firsttwo=data[:2] data=common.pppescape(data+common.crcs(data))+common.pppterminator isendretry=numsendretry while isendretry>=0: try: rdata=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False, numfailures=numretry) break except com_phone.modeignoreerrortypes: if isendretry>0: self.log("Resending request packet...") time.sleep(0.3) else: self.comm.success=False self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") isendretry-=1 self.comm.success=True origdata=rdata # sometimes there is junk at the beginning, eg if the user # turned off the phone and back on again. So if there is more # than one 7e in the escaped data we should start after the # second to last one d=rdata.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple Samsung packets in data - taking last one starting at "+`d+1`) self.logdata("Original Samsung data", origdata, None) rdata=rdata[d+1:] # turn it back to normal data=common.pppunescape(rdata) # Sometimes there is other crap at the beginning. But it might # be a Sanyo error byte. So strip off bytes from the beginning # until the crc agrees, or we get to the first two bytes of the # request packet. d=data.find(firsttwo) crc=data[-3:-1] crcok=False for i in range(0,d+1): trydata=data[i:-3] if common.crcs(trydata)==crc: crcok=True break if not crcok: self.logdata("first two",firsttwo, None) self.logdata("Original Sanyo data", origdata, None) self.logdata("Working on Sanyo data", data, None) raise common.CommsDataCorruption("Sanyo packet failed CRC check", self.desc) res=responseclass() if d>0: if d==i: self.log("Junk at beginning of Sanyo packet, data at "+`d`) self.logdata("Original Sanyo data", origdata, None) self.logdata("Working on Sanyo data", data, None) else: if returnerror: res=self.protocolclass.sanyoerror() else: self.log("Sanyo Error code "+`ord(data[0])`) self.logdata("sanyo phonebook response", data, None) raise SanyoCommandException(ord(data[0])) data=trydata # parse data buffer=prototypes.buffer(data) res.readfrombuffer(buffer, logtitle="sanyo phonebook response") return res
def sendpbcommand(self, request, responseclass, callsetmode=True, writemode=False, numsendretry=0, returnerror=False): if writemode: numretry=3 else: numretry=0 if callsetmode: self.setmode(self.MODEPHONEBOOK) buffer=prototypes.buffer() request.writetobuffer(buffer) data=buffer.getvalue() self.logdata("Sanyo phonebook request", data, request) firsttwo=data[:2] data=common.pppescape(data+common.crcs(data))+common.pppterminator isendretry=numsendretry while isendretry>=0: try: rdata=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False, numfailures=numretry) break except com_phone.modeignoreerrortypes: if isendretry>0: self.log("Resending request packet...") time.sleep(0.3) else: self.comm.success=False self.mode=self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") isendretry-=1 self.comm.success=True origdata=rdata d=rdata.rfind(common.pppterminator,0,-1) if d>=0: self.log("Multiple Sanyo packets in data - taking last one starting at "+`d+1`) self.logdata("Original Sanyo data", origdata, None) rdata=rdata[d+1:] data=common.pppunescape(rdata) d=data.find(firsttwo) crc=data[-3:-1] crcok=False for i in range(0,d+1): trydata=data[i:-3] if common.crcs(trydata)==crc: crcok=True break if not crcok: self.logdata("first two",firsttwo, None) self.logdata("Original Sanyo data", origdata, None) self.logdata("Working on Sanyo data", data, None) raise common.CommsDataCorruption("Sanyo packet failed CRC check", self.desc) res=responseclass() if d>0: if d==i: self.log("Junk at beginning of Sanyo packet, data at "+`d`) self.logdata("Original Sanyo data", origdata, None) self.logdata("Working on Sanyo data", data, None) else: if returnerror: res=self.protocolclass.sanyoerror() else: self.log("Sanyo Error code "+`ord(data[0])`) self.logdata("sanyo phonebook response", data, None) raise SanyoCommandException(ord(data[0])) data=trydata self.logdata("sanyo phonebook response", data, responseclass) buffer=prototypes.buffer(data) res.readfrombuffer(buffer) return res def getfundamentals(self, results): """Gets information fundamental to interopating with the phone and UI.""" print "HASRINGPICBUF=",self.protocolclass.HASRINGPICBUF self.log("Retrieving fundamental phone information") self.log("Phone serial number") results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() self.getmediaindices(results) self.log("Fundamentals retrieved") return results def sanyosort(self, a, b): "Sanyo sort order. Case insensitive, letters first" x=a[1] y=b[1] if(x[0:1].isalpha() and not y[0:1].isalpha()): return -1 if(y[0:1].isalpha() and not x[0:1].isalpha()): return 1 return cmp(x.lower(), y.lower()) def getsms(self, result): gsms = {} self.log("Getting sms entries") req=self.protocolclass.messagerequest() for slot in range(self.protocolclass.NUMMESSAGESLOTS): req.slot=slot res=self.sendpbcommand(req, self.protocolclass.messageresponse) if res.entry.dunno4==2: entry=sms.SMSEntry() entry.folder=entry.Folder_Inbox entry.datetime="200%d%02d%02dT%02d%02d%02d" % ((res.entry.year, res.entry.month, res.entry.day, res.entry.hour,res.entry.minute, res.entry.second)) if res.entry.read==17: entry.read=1 else: entry.read=0 entry._from=res.entry.phonenum entry.callback=res.entry.callback entry.subject="%d-%s-%s" % ((res.entry.counter, res.entry.phonenum, res.entry.message[:12])) self.log(res.entry.message[:8]) if res.entry.priority==100: entry.priority=sms.SMSEntry.Priority_Normal if res.entry.priority==200: entry.priority=sms.SMSEntry.Priority_High entry.text=unicode(res.entry.message, errors='ignore') gsms[entry.id]=entry result['sms']=gsms return result def gettodo(self, result): gtodo = {} self.log("Getting todo entries") req=self.protocolclass.todorequest() for slot in range(self.protocolclass.NUMTODOSLOTS): req.slot=slot res=self.sendpbcommand(req, self.protocolclass.todoresponse) entry=todo.TodoEntry() if res.entry.flag: entry.summary=res.entry.todo if res.entry.priority==2: entry.status=4 if res.entry.priority==0: entry.priority=5 if res.entry.priority==1: entry.priority=1 gtodo[entry.id]=entry result['todo']=gtodo return result def getphonebook(self,result): pbook={} sortstuff = self.getsanyobuffer(self.protocolclass.pbsortbuffer) if self.protocolclass.HASRINGPICBUF: ringpic = self.getsanyobuffer(self.protocolclass.ringerpicbuffer) speedslot=[] speedtype=[] for i in range(self.protocolclass._NUMSPEEDDIALS): speedslot.append(sortstuff.speeddialindex[i].pbslotandtype & 0xfff) numtype=(sortstuff.speeddialindex[i].pbslotandtype>>12)-1 if(numtype >= 0 and numtype <= len(self.numbertypetab)): speedtype.append(self.numbertypetab[numtype]) else: speedtype.append("") numentries=sortstuff.slotsused self.log("There are %d entries" % (numentries,)) count = 0 numcount = 0 numemail = 0 numurl = 0 res=self.protocolclass.phonebookentry() usedefaultnum = 'defaultnum' in res.getfields() print "usedefaultnum =",usedefaultnum req=self.protocolclass.phonebookslotrequest() for i in range(0, self.protocolclass._NUMPBSLOTS): if sortstuff.usedflags[i].used: req.slot = i res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse) self.log("Read entry "+`i`+" - "+res.entry.name) entry=self.extractphonebookentry(res.entry, result) for j in range(len(speedslot)): if(speedslot[j]==req.slot): for k in range(len(entry['numbers'])): if(entry['numbers'][k]['type']==speedtype[j]): entry['numbers'][k]['speeddial']=j+2 break if self.protocolclass.HASRINGPICBUF: if ringpic.ringtones[i].ringtone>0: try: tone=result['ringtone-index'][ringpic.ringtones[i].ringtone]['name'] except: tone=self.serialsname+"Index_"+`ringpic.ringtones[i].ringtone` entry['ringtones']=[{'ringtone': tone, 'use': 'call'}] if ringpic.wallpapers[i].wallpaper>0: try: paper=result['wallpaper-index'][ringpic.wallpapers[i].wallpaper]['name'] except: paper=self.serialsname+"Index_"+`ringpic.wallpapers[i].wallpaper` entry['wallpapers']=[{'wallpaper': paper, 'use': 'call'}] if usedefaultnum: firsttype=res.entry.defaultnum-1 if firsttype < len(self.numbertypetab): defaulttype=self.numbertypetab[firsttype] k=0 for j in range(len(entry['numbers'])): if entry['numbers'][j]['type'] == defaulttype: k=j break if k>0: exchange=entry['numbers'][k] for kk in range(k,0,-1): entry['numbers'][kk]=entry['numbers'][kk-1] entry['numbers'][0]=exchange pbook[count]=entry self.progress(count, numentries, res.entry.name) count+=1 numcount+=len(entry['numbers']) if entry.has_key('emails'): numemail+=len(entry['emails']) if entry.has_key('urls'): numurl+=len(entry['urls']) self.progress(numentries, numentries, "Phone book read completed") self.log("Phone contains "+`count`+" contacts, "+`numcount`+" phone numbers, "+`numemail`+" Emails, "+`numurl`+" URLs") result['phonebook']=pbook return pbook def extractphonebookentry(self, entry, fundamentals): """Return a phonebook entry in BitPim format""" res={} res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.slot, 'serial2': entry.slotdup, 'sourceuniqueid': fundamentals['uniqueserial']} ] res['names']=[ {'full': entry.name} ] if len(entry.email): res['emails']=[ {'email': entry.email} ] if len(entry.url): res['urls']=[ {'url': entry.url} ] res['flags']=[ {'secret': entry.secret } ] res['numbers']=[] numberindex = 0 for type in self.numbertypetab: if len(entry.numbers[numberindex].number): res['numbers'].append({'number': entry.numbers[numberindex].number, 'type': type }) numberindex+=1 return res def _findmediaindex(self, index, name, pbentryname, type): if name is None: return 0 for i in index: if index[i]['name']==name: return i pos=name.find('_') if(pos>=0): i=int(name[pos+1:]) return i return 0 def makeentry(self, entry, dict): e=self.protocolclass.phonebookentry() for k in entry: if k=='ringtones' or k=='wallpapers' or k=='numbertypes': continue if 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].number_len=len(e.numbers[numberindex].number) continue setattr(e,k,entry[k]) return e def writewait(self): """Loop until phone status indicates ready to write""" for i in range(100): req=self.protocolclass.statusrequest() res=self.sendpbcommand(req, self.protocolclass.statusresponse) if res.ready==res.readyvalue: return time.sleep(0.1) self.log("Phone did not transfer to ready to write state") self.log("Waiting a bit longer and trying anyway") return def savephonebook(self, data): newphonebook={} self.mode=self.MODENONE self.setmode(self.MODEBREW) self.setmode(self.MODEPHONEBOOK) sortstuff=self.protocolclass.pbsortbuffer() ringpic=self.protocolclass.ringerpicbuffer() callerid=self.protocolclass.calleridbuffer() res=self.protocolclass.phonebookentry() usedefaultnum = 'defaultnum' in res.getfields() for i in range(self.protocolclass._NUMPBSLOTS): sortstuff.usedflags.append(0) sortstuff.firsttypes.append(0) sortstuff.sortorder.append(0xffff) sortstuff.sortorder2.append(0xffff) sortstuff.emails.append(0xffff) sortstuff.urls.append(0xffff) ringpic.ringtones.append(0) ringpic.wallpapers.append(0) for i in range(self.protocolclass._NUMSPEEDDIALS): sortstuff.speeddialindex.append(0xffff) for i in range(self.protocolclass._NUMLONGNUMBERS): sortstuff.longnumbersindex.append(0xffff) namemap=[] emailmap=[] urlmap=[] callerid.numentries=0 pbook=data['phonephonebook'] self.log("Putting phone into write mode") req=self.protocolclass.beginendupdaterequest() req.beginend=1 res=self.sendpbcommand(req, self.protocolclass.beginendupdateresponse, writemode=True) self.writewait() keys=pbook.keys() keys.sort() sortstuff.slotsused=len(keys) sortstuff.numemail=0 sortstuff.numurl=0 progresscur=0 progressmax=len(keys) self.log("Writing %d entries to phone" % (len(keys),)) nlongphonenumbers=0 nonumbercount=0 for ikey in keys: ii=pbook[ikey] slot=ii['slot'] progresscur+=1 self.progress(progresscur, progressmax, "Writing "+ii['name']) self.log("Writing entry "+`slot`+" - "+ii['name']) entry=self.makeentry(ii, data) if not usedefaultnum: delattr(entry,'defaultnum') req=self.protocolclass.phonebookslotupdaterequest() req.entry=entry res=self.sendpbcommand(req, self.protocolclass.phonebookslotresponse, writemode=True) entry=self.extractphonebookentry(entry, data) entry['ringtones']=[{'ringtone': ii['ringtone'], 'use': 'call'}] entry['wallpapers']=[{'wallpaper': ii['wallpaper'], 'use': 'call'}] sortstuff.usedflags[slot].used=1 if(len(ii['numbers'])): sortstuff.firsttypes[slot].firsttype=min(ii['numbertypes'])+1 else: if(len(ii['email'])): sortstuff.firsttypes[slot].firsttype=8 nonumbercount+=1 elif(len(ii['url'])): sortstuff.firsttypes[slot].firsttype=9 nonumbercount+=1 else: sortstuff.firsttypes[slot].firsttype=0 for i in range(len(ii['numbers'])): nindex=ii['numbertypes'][i] speeddial=ii['speeddials'][i] code=slot+((nindex+1)<<12) if(speeddial>=2 and speeddial<=self.protocolclass._NUMSPEEDDIALS+1): sortstuff.speeddialindex[speeddial-2]=code for k in range(len(entry['numbers'])): if(entry['numbers'][k]['type']==nindex): entry['numbers'][k]['speeddial']=speeddial break if(callerid.numentries<callerid.maxentries): phonenumber=ii['numbers'][i] cidentry=self.makecidentry(phonenumber,slot,nindex) callerid.items.append(cidentry) callerid.numentries+=1 if(len(phonenumber)>self.protocolclass._LONGPHONENUMBERLEN): if(nlongphonenumbers<self.protocolclass._NUMLONGNUMBERS): sortstuff.longnumbersindex[nlongphonenumbers].pbslotandtype=code namemap.append((slot,ii['name'])) if(len(ii['email'])): emailmap.append((slot,ii['email'])) sortstuff.numemail+=1 if(len(ii['url'])): urlmap.append((slot,ii['url'])) sortstuff.numurl+=1 ringpic.ringtones[slot].ringtone=self._findmediaindex(data['ringtone-index'], ii['ringtone'],ii['name'],'ringtone') ringpic.wallpapers[slot].wallpaper=self._findmediaindex(data['wallpaper-index'], ii['wallpaper'],ii['name'],'wallpaper') newphonebook[slot]=entry sortstuff.slotsused2=len(keys)-nonumbercount i=0 j=0 sortstuff.pbfirstletters="" namemap.sort(self.sanyosort) for (slot, name) in namemap: sortstuff.sortorder[i].pbslot=slot if sortstuff.firsttypes[slot].firsttype<=7: sortstuff.sortorder2[j].pbslot=slot j+=1 if name: sortstuff.pbfirstletters+=name[0] else: sortstuff.pbfirstletters+=chr(0) i+=1 i=0 sortstuff.emailfirstletters="" emailmap.sort(self.sanyosort) for (slot, email) in emailmap: sortstuff.emails[i].pbslot=slot sortstuff.emailfirstletters+=email[0] i+=1 i=0 sortstuff.urlfirstletters="" urlmap.sort(self.sanyosort) for (slot, url) in urlmap: sortstuff.urls[i].pbslot=slot sortstuff.urlfirstletters+=url[0] i+=1 self.sendsanyobuffer(sortstuff) if self.protocolclass.HASRINGPICBUF: self.sendsanyobuffer(ringpic) self.sendsanyobuffer(callerid) time.sleep(1.0) data['phonebook']=newphonebook del data['phonephonebook'] data['rebootphone'] = 1 def makecidentry(self, number, slot, nindex): "Prepare entry for caller ID lookup buffer" numstripped=re.sub("[^0-9PT#*]", "", number) numonly=re.sub("^(\\d*).*$", "\\1", numstripped) cidentry=self.protocolclass.calleridentry() cidentry.pbslotandtype=slot+((nindex+1)<<12) cidentry.actualnumberlen=len(numonly) cidentry.numberfragment=numonly[-10:] return cidentry def savewallpapers(self, results, merge): return self.savemedia('wallpapers', 'wallpaper-index', 'images', results, merge) def saveringtones(self, results, merge): return self.savemedia('ringtone', 'ringtone-index', 'ringers', results, merge) def savemedia(self, mediakey, mediaindexkey, mediatype, results, merge): """Actually saves out the media @param mediakey: key of the media (eg 'wallpapers' or 'ringtones') Index of media in wallpaper or ringer tab @param mediaindexkey: index key (eg 'wallpaper-index') Index of media on the phone @param results: results dict """ wp=results[mediakey].copy() wpi=results[mediaindexkey].copy() for k in wpi.keys(): if wpi[k]['origin']=='builtin': del wpi[k] for w in wp.keys(): name=wp[w]['name'] if wp[w].get("origin", "")=='camera': self.log("Not transferring camera picture "+name+" to phone") del wp[w] else: for k in wpi.keys(): if name==wpi[k]['name']: self.log("Not transferring "+name+" as it is already on the phone") del wp[w] break init={} init[mediatype]={} idx=0 for w in wp.keys(): idx-=1 data=wp[w]['data'] name=wp[w]['name'] init[mediatype][idx]={'name': name, 'data': data} index=init[mediatype] errors=False for key in index: efile=index[key]['name'] content=index[key]['data'] if content is None: continue if mediatype=='images': content = conversions.convertto8bitpng(content,16383) if not self.writesanyofile(efile, content): errors=True if errors: self.log("One or more files were rejected by the phone, due to") self.log("invalid file type (only PNG or MIDI are allowed), file") self.log("size too big, or too many ringers or wallpaper on the phone") self.log("See Sanyo Error Codes section of the Sanyh Phone Notes") self.log("in the help for more information") return results def writesanyofile(self, name, contents): start=time.time() self.log("Writing file '"+name+"' bytes "+`len(contents)`) desc="Writing "+name try: name=name[:name.index(".mid")] except: try: name=name[:name.index(".png")] except: pass if hasattr(self.protocolclass,"sanyomediathingyrequest"): req=self.protocolclass.sanyomediathingyrequest() req.faset = 0x10 res=self.sendpbcommand(req, self.protocolclass.sanyomediathingyresponse) time.sleep(10.0) req.faset = 0x13 res=self.sendpbcommand(req, self.protocolclass.sanyomediathingyresponse) req=self.protocolclass.sanyosendfilename() req.filename=name res=self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, returnerror=True) if 'errorcode' in res.getfields(): if res.errorcode==0x65: self.alert("Please put your phone into PC Sync Mode", False) else: raise SanyoCommandException(res.errorcode) waitcount=120 while waitcount>0: time.sleep(1.0) res=self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, returnerror=True) if not 'errorcode' in res.getfields(): break waitcount-=1 if waitcount==0: raise SanyoCommandException(res.errorcode) req=self.protocolclass.sanyosendfilesize() req.filesize=len(contents) self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse) req=self.protocolclass.sanyosendfilefragment() packetsize=req.payloadsize offset=0 count=0 numblocks=len(contents)/packetsize+1 for offset in range(0, len(contents), packetsize): count+=1 if count % 5==0: self.progress(count,numblocks,desc) req.header.command=offset req.data=contents[offset:min(offset+packetsize,len(contents))] self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, writemode=True, returnerror=True) if 'errorcode' in res.getfields(): self.log(name+" not written due to error code "+`res.errorcode`) return False req=self.protocolclass.sanyosendfileterminator() try: res=self.sendpbcommand(req, self.protocolclass.sanyosendfileresponse, writemode=True, returnerror=True) if 'errorcode' in res.getfields(): self.log(name+" not written due to error code "+`res.errorcode`) return False except: self.log("Exception on writing terminator for file "+name) self.log("Continuing...") end=time.time() if end-start>3: self.log("Wrote "+`len(contents)`+" bytes at "+`int(len(contents)/(end-start))`+" bytes/second") return True def getcalendar(self,result): calres={} progressmax=self.protocolclass._NUMEVENTSLOTS+self.protocolclass._NUMCALLALARMSLOTS req=self.protocolclass.eventrequest() count=0 try: reqflag=self.protocolclass.eventslotinuserequest() except: reqflag=0 for i in range(0, self.protocolclass._NUMEVENTSLOTS): self.progress(i,progressmax,"Events") if reqflag: reqflag.slot=i resflag=self.sendpbcommand(reqflag, self.protocolclass.eventslotinuseresponse) if not resflag.flag: continue req.slot = i res=self.sendpbcommand(req, self.protocolclass.eventresponse) if not reqflag: if not res.entry.flag: continue self.log("Read calendar event "+`i`+" - "+res.entry.eventname+", alarm ID "+`res.entry.ringtone`) entry=bpcalendar.CalendarEntry() entry.changeserial=res.entry.serial entry.description=res.entry.eventname entry.location=res.entry.location starttime=res.entry.start entry.start=self.decodedate(starttime) entry.end=self.decodedate(res.entry.end) repeat=self._calrepeatvalues[res.entry.period] entry.repeat = self.makerepeat(repeat,entry.start) if res.entry.alarm==0xffffffff: entry.alarm=res.entry.alarmdiff/60 else: alarmtime=res.entry.alarm entry.alarm=(starttime-alarmtime)/60 ringtone=res.entry.ringtone if ringtone in self.calendar_tonerange: ringtone-=self.calendar_toneoffset if ringtone!=self.calendar_defaultringtone: if result['ringtone-index'].has_key(ringtone): entry.ringtone=result['ringtone-index'][ringtone]['name'] entry.snoozedelay=0 calres[entry.id]=entry count+=1 req=self.protocolclass.callalarmrequest() for i in range(0, self.protocolclass._NUMCALLALARMSLOTS): self.progress(self.protocolclass._NUMEVENTSLOTS,progressmax,"Call Alarms") req.slot=i res=self.sendpbcommand(req, self.protocolclass.callalarmresponse) if res.entry.flag and res.entry.date: self.log("Read call alarm entry "+`i`+" - "+res.entry.phonenum+", alarm ID "+`res.entry.ringtone`) entry=bpcalendar.CalendarEntry() entry.changeserial=res.entry.serial entry.description=res.entry.phonenum starttime=res.entry.date entry.start=self.decodedate(starttime) entry.end=entry.start repeat=self._calrepeatvalues[res.entry.period] entry.repeat = self.makerepeat(repeat,entry.start) entry.alarm=0 if res.entry.ringtone!=self.calendar_defaultcaringtone: entry.ringtone=result['ringtone-index'][res.entry.ringtone]['name'] entry.snoozedelay=0 calres[entry.id]=entry count+=1 result['calendar']=calres return result def makerepeat(self, repeatword, startdate): if repeatword is None: repeat_entry=None else: repeat_entry=bpcalendar.RepeatEntry() if repeatword=='daily': repeat_entry.repeat_type=repeat_entry.daily repeat_entry.interval=1 elif repeatword=='monfri': repeat_entry.repeat_type=repeat_entry.daily repeat_entry.interval=0 elif repeatword=='weekly': repeat_entry.repeat_type=repeat_entry.weekly repeat_entry.interval=1 dow=datetime.date(*startdate[:3]).isoweekday()%7 repeat_entry.dow=1<<dow elif repeatword=='monthly': repeat_entry.repeat_type=repeat_entry.monthly else: repeat_entry.repeat_type=repeat_entry.yearly return repeat_entry def savecalendar(self, dict, merge): cal=dict['calendar'] newcal={} keys=cal.keys() zonedif=time.mktime(time.gmtime(0))-time.mktime(time.localtime(0)) try: reqflag=self.protocolclass.eventslotinuseupdaterequest() except: reqflag=0 eventslot=0 callslot=0 progressmax=self.protocolclass._NUMEVENTSLOTS+self.protocolclass._NUMCALLALARMSLOTS for k in keys: entry=cal[k] descloc=entry.description self.progress(eventslot+callslot, progressmax, "Writing "+descloc) rp=entry.repeat if rp is None: repeat=0 else: repeatname=None if rp.repeat_type==rp.daily: repeatname='daily' elif rp.repeat_type==rp.weekly: repeatname='weekly' elif rp.repeat_type==rp.monthly: repeatname='monthly' elif rp.repeat_type==rp.yearly: repeatname='yearly' for k,v in self._calrepeatvalues.items(): if repeatname==v: repeat=k break if repeatname is None: self.log(descloc+": Repeat type "+`entry.repeat`+" not valid for this phone") repeat=0 phonenum=re.sub("\-","",descloc) now=time.mktime(time.localtime(time.time()))-zonedif if(phonenum.isdigit()): self.log("Write calendar call alarm slot "+`callslot`+ " - "+descloc) e=self.protocolclass.callalarmentry() e.slot=callslot e.phonenum=phonenum e.phonenum_len=len(e.phonenum) timearray=list(entry.start)+[0,0,0,0] starttimelocal=time.mktime(timearray)-zonedif if(starttimelocal<now and repeat==0): e.flag=2 else: e.flag=1 e.date=starttimelocal-self._sanyoepochtounix e.datedup=e.date e.phonenumbertype=0 e.phonenumberslot=0 e.name="" e.name_len=len(e.name) e.ringtone=self.calendar_defaultcaringtone print "Setting ringtone "+`e.ringtone` req=self.protocolclass.callalarmupdaterequest() callslot+=1 respc=self.protocolclass.callalarmresponse else: self.log("Write calendar event slot "+`eventslot`+ " - "+descloc) e=self.protocolclass.evententry() e.slot=eventslot slashpos=descloc.find('/') if(slashpos >= 0): eventname=descloc[0:slashpos] location=descloc[slashpos+1:] else: eventname=descloc location='' e.eventname=descloc e.eventname_len=len(e.eventname) e.location=entry.location e.location_len=len(e.location) timearray=list(entry.start)+[0,0,0,0] starttimelocal=time.mktime(timearray)-zonedif e.start=starttimelocal-self._sanyoepochtounix try: timearray=list(entry.end)+[0,0,0,0] endtimelocal=time.mktime(timearray)-zonedif e.end=endtimelocal-self._sanyoepochtounix except: e.end=e.start+60 alarmdiff=entry.alarm if alarmdiff<0: alarmdiff=0 alarmdiff=max(alarmdiff,0)*60 e.alarmdiff=alarmdiff e.alarm=starttimelocal-self._sanyoepochtounix-alarmdiff if reqflag: reqflag.slot=eventslot if(e.alarm+self._sanyoepochtounix<now and repeat==0): reqflag.flag=4 else: reqflag.flag=1 res=self.sendpbcommand(reqflag, self.protocolclass.eventslotinuseresponse) else: if(starttimelocal<now and repeat==0): e.flag=2 else: e.flag=1 try: timearray=list(entry.end)+[0,0,0,0] endtimelocal=time.mktime(timearray)-zonedif e.end=endtimelocal-self._sanyoepochtounix except: e.end=e.start+60 e.ringtone=self.calendar_defaultringtone print "Setting ringtone "+`e.ringtone` req=self.protocolclass.eventupdaterequest() eventslot+=1 respc=self.protocolclass.eventresponse e.period=repeat e.dom=entry.start[2] if entry.id>=0 and entry.id<256: e.serial=entry.id else: e.serial=0 req.entry=e res=self.sendpbcommand(req, respc, writemode=True) if reqflag: req=reqflag req.flag=0 else: e=self.protocolclass.evententry() e.flag=0 e.eventname="" e.eventname_len=0 e.location="" e.location_len=0 e.start=0 e.end=0 e.period=0 e.dom=0 e.ringtone=0 e.alarm=0 e.alarmdiff=0 req=self.protocolclass.eventupdaterequest() req.entry=e for eventslot in range(eventslot,self.protocolclass._NUMEVENTSLOTS): self.progress(eventslot+callslot, progressmax, "Writing unused") self.log("Write calendar event slot "+`eventslot`+ " - Unused") if reqflag: req.slot=eventslot else: req.entry.slot=eventslot res=self.sendpbcommand(req, self.protocolclass.eventresponse, writemode=True) e=self.protocolclass.callalarmentry() e.flag=0 e.name="" e.name_len=0 e.phonenum="" e.phonenum_len=0 e.date=0 e.datedup=0 e.period=0 e.dom=0 e.ringtone=0 e.phonenumbertype=0 e.phonenumberslot=0 req=self.protocolclass.callalarmupdaterequest() req.entry=e for callslot in range(callslot,self.protocolclass._NUMCALLALARMSLOTS): self.progress(eventslot+callslot, progressmax, "Writing unused") self.log("Write calendar call alarm slot "+`callslot`+ " - Unused") req.entry.slot=callslot res=self.sendpbcommand(req, self.protocolclass.callalarmresponse, writemode=True) self.progress(progressmax, progressmax, "Calendar write done") dict['rebootphone'] = True return dict def getcallhistory(self, result): res={} self._readhistory(self.protocolclass.OUTGOING,'Outgoing',res) self._readhistory(self.protocolclass.MISSED,'Missed',res) self._readhistory(self.protocolclass.INCOMING,'Incoming',res) result['call_history']=res return result def _readhistory(self, type, folder, res): req=self.protocolclass.historyrequest() req.type=type for slot in range(self.protocolclass.NUMCALLHISTORY): req.slot=slot call=self.sendpbcommand(req, self.protocolclass.historyresponse) if call.entry.phonenum=='' and call.entry.name=='': continue entry=call_history.CallHistoryEntry() entry.folder=folder entry.name=call.entry.name entry.number=call.entry.phonenum entry.datetime=((call.entry.date)) res[entry.id]=entry def decodedate(self,val): """Unpack 32 bit value into date/time @rtype: tuple @return: (year, month, day, hour, minute) """ return list(time.gmtime(val+self._sanyoepochtounix)[:5]) _calrepeatvalues={ 0: None, 1: 'daily', 2: 'weekly', 3: 'monthly', 4: 'yearly' } "Talk to a Sanyo Sprint Phone such as SCP-4900, SCP-5300, or SCP-8100" def phonize(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) class Profile (com_phone.Profile) : serialsname='sanyo' WALLPAPER_WIDTH=120 WALLPAPER_HEIGHT=128 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 .`~!@#$%^&()-_=+[{]};\'" usbids=( ( 0x0474, 0x0701, 1), ) deviceclasses=("modem",) BP_Calendar_Version=3 def __init__(self): self.numbertypetab=numbertypetab com_phone.Profile.__init__(self) _supportedsyncs=( ('phonebook', 'read', None), ('calendar', 'read', None), ('phonebook', 'write', 'OVERWRITE'), ('calendar', 'write', 'OVERWRITE'), ('wallpaper', 'write', 'MERGE'), ('ringtone', 'write', 'MERGE'), ('call_history', 'read', None), ('sms', 'read', None), ) def convertphonebooktophone(self, helper, data): "Converts the data to what will be used by the phone" results={} slotsused={} for pbentry in data['phonebook']: entry=data['phonebook'][pbentry] serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', -1) if(serial1 >= 0 and serial1 < self.protocolclass._NUMPBSLOTS): slotsused[serial1]=1 lastunused=0 for pbentry in data['phonebook']: e={} entry=data['phonebook'][pbentry] try: try: e['name']=helper.getfullname(entry.get('names', []),1,1,16)[0] except: e['name']='' e['name_len']=len(e['name']) serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', -1) if(serial1 >= 0 and serial1 < self.protocolclass._NUMPBSLOTS): e['slot']=serial1 else: while(slotsused.has_key(lastunused)): lastunused+=1 if(lastunused >= self.protocolclass._NUMPBSLOTS): raise helper.ConversionFailed() e['slot']=lastunused slotsused[lastunused]=1 e['slotdup']=e['slot'] e['email']=helper.makeone(helper.getemails(entry.get('emails', []),0,1,self.protocolclass._MAXEMAILLEN), "") e['email_len']=len(e['email']) e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,self.protocolclass._MAXEMAILLEN), "") e['url_len']=len(e['url']) numbers=helper.getnumbers(entry.get('numbers', []),0,7) e['numbertypes']=[] e['numbers']=[] e['speeddials']=[] unusednumbers=[] typesused={} defaultnum=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 defaultnum==0: defaultnum=typenum+1 number=phonize(num['number']) if len(number)>self.protocolclass._MAXNUMBERLEN: number=number[:self.protocolclass._MAXNUMBERLEN] e['numbers'].append(number) if(num.has_key('speeddial')): e['speeddials'].append(num['speeddial']) else: e['speeddials'].append(-1) e['numbertypes'].append(typenum) break trytype=len(self.numbertypetab) for num in unusednumbers: while trytype>0: trytype-=1 if not typesused.has_key(self.numbertypetab[trytype]): break else: break if defaultnum==0: defaultnum=trytype+1 number=phonize(num['number']) if len(number)>self.protocolclass._MAXNUMBERLEN: number=number[:self.protocolclass._MAXNUMBERLEN] e['numbers'].append(number) e['numbertypes'].append(trytype) if(num.has_key('speeddial')): e['speeddials'].append(num['speeddial']) else: e['speeddials'].append(-1) if defaultnum==0: if e['url_len'] > 0: defaultnum=8 elif e['email_len'] > 0: defaultnum=9 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) e['secret']=helper.getflag(entry.get('flags', []), 'secret', False) e['defaultnum']=defaultnum results[pbentry]=e except helper.ConversionFailed: print "No Free Slot for "+e['name'] continue data['phonephonebook']=results return data class Phone (SanyoPhonebook,com_phone.Phone,com_brew.BrewProtocol) : "Talk to a Sanyo Sprint Phone such as SCP-4900, SCP-5300, or SCP-8100" desc="Sanyo" imagelocations=() ringtonelocations=() builtinimages=() builtinringtones=() def __init__(self, logtarget, commport): "Call all the contructors and sets initial modes" com_phone.Phone.__init__(self, logtarget, commport) com_brew.BrewProtocol.__init__(self) SanyoPhonebook.__init__(self) self.log("Attempting to contact phone") self.mode=self.MODENONE getwallpapers=None getringtones=None "Talk to a Sanyo Sprint Phone such as SCP-4900, SCP-5300, or SCP-8100"
def sendpbcommand( self, request, responseclass, callsetmode=True, writemode=False, numsendretry=0, returnerror=False ): if writemode: numretry = 3 else: numretry = 0 if callsetmode: self.setmode(self.MODEPHONEBOOK) buffer = prototypes.buffer() request.writetobuffer(buffer, logtitle="Samsung phonebook request") data = buffer.getvalue() firsttwo = data[:2] data = common.pppescape(data + common.crcs(data)) + common.pppterminator isendretry = numsendretry while isendretry >= 0: try: rdata = self.comm.writethenreaduntil( data, False, common.pppterminator, logreaduntilsuccess=False, numfailures=numretry ) break except com_phone.modeignoreerrortypes: if isendretry > 0: self.log("Resending request packet...") time.sleep(0.3) else: self.comm.success = False self.mode = self.MODENONE self.raisecommsdnaexception("manipulating the phonebook") isendretry -= 1 self.comm.success = True origdata = rdata # sometimes there is junk at the beginning, eg if the user # turned off the phone and back on again. So if there is more # than one 7e in the escaped data we should start after the # second to last one d = rdata.rfind(common.pppterminator, 0, -1) if d >= 0: self.log("Multiple Samsung packets in data - taking last one starting at " + ` d + 1 `) self.logdata("Original Samsung data", origdata, None) rdata = rdata[d + 1 :] # turn it back to normal data = common.pppunescape(rdata) # Sometimes there is other crap at the beginning. But it might # be a Sanyo error byte. So strip off bytes from the beginning # until the crc agrees, or we get to the first two bytes of the # request packet. d = data.find(firsttwo) crc = data[-3:-1] crcok = False for i in range(0, d + 1): trydata = data[i:-3] if common.crcs(trydata) == crc: crcok = True break if not crcok: self.logdata("first two", firsttwo, None) self.logdata("Original Sanyo data", origdata, None) self.logdata("Working on Sanyo data", data, None) raise common.CommsDataCorruption("Sanyo packet failed CRC check", self.desc) res = responseclass() if d > 0: if d == i: self.log("Junk at beginning of Sanyo packet, data at " + ` d `) self.logdata("Original Sanyo data", origdata, None) self.logdata("Working on Sanyo data", data, None) else: if returnerror: res = self.protocolclass.sanyoerror() else: self.log("Sanyo Error code " + ` ord(data[0]) `) self.logdata("sanyo phonebook response", data, None) raise SanyoCommandException(ord(data[0])) data = trydata # parse data buffer = prototypes.buffer(data) res.readfrombuffer(buffer, logtitle="sanyo phonebook response") return res