0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2008 Stephen Wood <saw@bitpim.org> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: com_samsungspha900.py 3918 2007-01-19 05:15:12Z djpham $ 0009 0010 """Communicate with a Samsung SPH-A900""" 0011 0012 import sha 0013 import re 0014 import struct 0015 0016 import common 0017 import commport 0018 import p_brew 0019 import p_samsungspha900 0020 import com_brew 0021 import com_phone 0022 import prototypes 0023 import helpids 0024 0025 numbertypetab=('cell','home','office','pager','none') 0026 0027 class Phone(com_phone.Phone, com_brew.BrewProtocol): 0028 "Talk to a Samsung SPH-A900 phone" 0029 0030 desc="SPH-A900" 0031 helpid=helpids.ID_PHONE_SAMSUNGOTHERS 0032 protocolclass=p_samsungspha900 0033 serialsname='spha900' 0034 0035 MODEPHONEBOOK="modephonebook" # can speak the phonebook protocol 0036 0037 # jpeg Remove first 124 characters 0038 0039 imagelocations=( 0040 # offset, index file, files location, origin, maximumentries, header offset 0041 # Offset is arbitrary. 100 is reserved for amsRegistry indexed files 0042 (400, "cam/dldJpeg", "camera", 100, 124), 0043 (300, "cam/jpeg", "camera", 100, 124), 0044 ) 0045 0046 ringtonelocations=( 0047 # offset, index file, files location, type, maximumentries, header offset 0048 ) 0049 0050 0051 def __init__(self, logtarget, commport): 0052 com_phone.Phone.__init__(self, logtarget, commport) 0053 com_brew.BrewProtocol.__init__(self) 0054 self.numbertypetab=numbertypetab 0055 self.mode=self.MODENONE 0056 0057 def _setmodephonebook(self): 0058 self.setmode(self.MODEBREW) 0059 req=self.protocolclass.firmwarerequest() 0060 respc=self.protocolclass.firmwareresponse 0061 try: 0062 self.sendpbcommand(req, respc, callsetmode=False) 0063 return 1 0064 except com_phone.modeignoreerrortypes: 0065 pass 0066 try: 0067 self.comm.setbaudrate(38400) 0068 self.sendpbcommand(req, respc, callsetmode=False) 0069 return 1 0070 except com_phone.modeignoreerrortypes: 0071 pass 0072 return 0 0073 0074 def sendpbcommand(self, request, responseclass, callsetmode=True, writemode=False, numsendretry=0, returnerror=False): 0075 if writemode: 0076 numretry=3 0077 else: 0078 numretry=0 0079 0080 if callsetmode: 0081 self.setmode(self.MODEPHONEBOOK) 0082 buffer=prototypes.buffer() 0083 0084 request.writetobuffer(buffer, logtitle="Samsung phonebook request") 0085 data=buffer.getvalue() 0086 firsttwo=data[:2] 0087 data=common.pppescape(data+common.crcs(data))+common.pppterminator 0088 isendretry=numsendretry 0089 while isendretry>=0: 0090 try: 0091 rdata=self.comm.writethenreaduntil(data, False, common.pppterminator, logreaduntilsuccess=False, numfailures=numretry) 0092 break 0093 except com_phone.modeignoreerrortypes: 0094 if isendretry>0: 0095 self.log("Resending request packet...") 0096 time.sleep(0.3) 0097 else: 0098 self.comm.success=False 0099 self.mode=self.MODENONE 0100 self.raisecommsdnaexception("manipulating the phonebook") 0101 isendretry-=1 0102 0103 self.comm.success=True 0104 0105 origdata=rdata 0106 # sometimes there is junk at the beginning, eg if the user 0107 # turned off the phone and back on again. So if there is more 0108 # than one 7e in the escaped data we should start after the 0109 # second to last one 0110 d=rdata.rfind(common.pppterminator,0,-1) 0111 if d>=0: 0112 self.log("Multiple Samsung packets in data - taking last one starting at "+`d+1`) 0113 self.logdata("Original Samsung data", origdata, None) 0114 rdata=rdata[d+1:] 0115 0116 # turn it back to normal 0117 data=common.pppunescape(rdata) 0118 0119 # Sometimes there is other crap at the beginning. But it might 0120 # be a Sanyo error byte. So strip off bytes from the beginning 0121 # until the crc agrees, or we get to the first two bytes of the 0122 # request packet. 0123 d=data.find(firsttwo) 0124 crc=data[-3:-1] 0125 crcok=False 0126 for i in range(0,d+1): 0127 trydata=data[i:-3] 0128 if common.crcs(trydata)==crc: 0129 crcok=True 0130 break 0131 0132 if not crcok: 0133 self.logdata("first two",firsttwo, None) 0134 self.logdata("Original Sanyo data", origdata, None) 0135 self.logdata("Working on Sanyo data", data, None) 0136 raise common.CommsDataCorruption("Sanyo packet failed CRC check", self.desc) 0137 0138 res=responseclass() 0139 if d>0: 0140 if d==i: 0141 self.log("Junk at beginning of Sanyo packet, data at "+`d`) 0142 self.logdata("Original Sanyo data", origdata, None) 0143 self.logdata("Working on Sanyo data", data, None) 0144 else: 0145 if returnerror: 0146 res=self.protocolclass.sanyoerror() 0147 else: 0148 self.log("Sanyo Error code "+`ord(data[0])`) 0149 self.logdata("sanyo phonebook response", data, None) 0150 raise SanyoCommandException(ord(data[0])) 0151 0152 data=trydata 0153 0154 # parse data 0155 buffer=prototypes.buffer(data) 0156 res.readfrombuffer(buffer, logtitle="sanyo phonebook response") 0157 return res 0158 0159 def getfundamentals(self, results): 0160 """Gets information fundamental to interopating with the phone and UI.""" 0161 results['uniqueserial']=1 0162 return results 0163 0164 def getphonebook(self,result): 0165 pbook={} 0166 0167 count = 0 0168 numcount = 0 0169 numemail = 0 0170 numurl = 0 0171 0172 reqname=self.protocolclass.namerequest() 0173 reqnumber=self.protocolclass.numberrequest() 0174 for slot in range(1,self.protocolclass.NUMPHONEBOOKENTRIES+1): 0175 reqname.slot=slot 0176 resname=self.sendpbcommand(reqname, self.protocolclass.nameresponse) 0177 bitmask=resname.entry.bitmask 0178 if bitmask: 0179 entry={} 0180 name=resname.entry.name 0181 entry['serials']=[ {'sourcetype': self.serialsname, 0182 0183 'slot': slot, 0184 'sourceuniqueid': result['uniqueserial']} ] 0185 entry['names']=[{'full':name}] 0186 entry['numbers']=[] 0187 bit=1 0188 print resname.entry.p2,name 0189 for num in range(self.protocolclass.NUMPHONENUMBERS): 0190 bit <<= 1 0191 if bitmask & bit: 0192 numslot=resname.entry.numberps[num].slot 0193 reqnumber.slot=numslot 0194 resnumber=self.sendpbcommand(reqnumber, self.protocolclass.numberresponse) 0195 numhash={'number':resnumber.entry.num, 'type' : self.numbertypetab[resnumber.entry.numbertype-1]} 0196 entry['numbers'].append(numhash) 0197 0198 print " ",self.numbertypetab[resnumber.entry.numbertype-1]+": ",numslot,resnumber.entry.num 0199 bit <<= 1 0200 if bitmask & bit: 0201 reqnumber.slot=resname.entry.emailp 0202 resnumber=self.sendpbcommand(reqnumber, self.protocolclass.numberresponse) 0203 print " Email: ",resname.entry.emailp,resnumber.entry.num 0204 entry['emails']=[] 0205 entry['emails'].append({'email':resnumber.entry.num}) 0206 bit <<= 1 0207 if bitmask & bit: 0208 reqnumber.slot=resname.entry.urlp 0209 resnumber=self.sendpbcommand(reqnumber, self.protocolclass.numberresponse) 0210 print " URL: ",resname.entry.urlp,resnumber.entry.num 0211 entry['urls']=[{'url':resnumber.entry.num}] 0212 if resname.entry.nickname: 0213 print " Nick: ",resname.entry.nickname 0214 if resname.entry.memo: 0215 print " Memo ",resname.entry.memo 0216 entry['memos']=[{'memo':resname.entry.memo}] 0217 0218 pbook[count]=entry 0219 self.progress(slot, self.protocolclass.NUMPHONEBOOKENTRIES+1, name) 0220 count+=1 0221 numcount+=len(entry['numbers']) 0222 if entry.has_key('emails'): 0223 numemail+=len(entry['emails']) 0224 if entry.has_key('urls'): 0225 numurl+=len(entry['urls']) 0226 0227 self.progress(slot,slot,"Phonebook read completed") 0228 self.log("Phone contains "+`count`+" contacts, "+`numcount`+" phone numbers, "+`numemail`+" Emails, "+`numurl`+" URLs") 0229 result['phonebook']=pbook 0230 0231 return pbook 0232 0233 def writewait(self): 0234 """Loop until phone status indicates ready to write""" 0235 for i in range(100): 0236 req=self.protocolclass.statusrequest() 0237 res=self.sendpbcommand(req, self.protocolclass.statusresponse) 0238 # print res.flag0, res.ready, res.flag2, res.flag3 0239 if res.ready==res.readyvalue: 0240 return 0241 time.sleep(0.1) 0242 0243 self.log("Phone did not transfer to ready to write state") 0244 self.log("Waiting a bit longer and trying anyway") 0245 return 0246 0247 def savephonebook(self, data): 0248 newphonebook={} 0249 0250 nump=2 0251 urlp=0 0252 memop=0 0253 addressp=0 0254 emailp=0 0255 slotsused=1 0256 0257 namemap=[] 0258 contactmap=[] 0259 urlmap=[] 0260 0261 pbook=data['phonebook'] 0262 self.log("Putting phone into write mode") 0263 req=self.protocolclass.beginendupdaterequest() 0264 req.beginend=1 # Start update 0265 res=self.sendpbcommand(req, self.protocolclass.beginendupdateresponse, writemode=True) 0266 # self.writewait() 0267 0268 req=self.protocolclass.writeenable() 0269 try: 0270 res=self.sendpbcommand(req, self.protocolclass.writeenableresponse, writemode=True) 0271 except: 0272 pass 0273 0274 keys=pbook.keys() 0275 keys.sort() 0276 nnames = len(keys) 0277 0278 reqnumber=self.protocolclass.numberupdaterequest() 0279 for ikey in keys: 0280 ii=pbook[ikey] 0281 slot=slotsused 0282 slotsused+=1 0283 0284 reqname=self.protocolclass.nameupdaterequest() 0285 reqname.slot=slot 0286 name=ii['name'] 0287 self.progress(slot-1, nnames, "Writing "+name) 0288 self.log("Writing "+`name`+" to slot "+`slot`) 0289 namemap.append((slot,name)) 0290 0291 reqname.entry.name=name 0292 reqname.entry.name_len=ii['name_len'] 0293 0294 bitmask=0 0295 bit=1 0296 for numindex in range(len(ii['numbers'])): 0297 reqnumber.slot=nump 0298 reqnumber.entry.num=ii['numbers'][numindex] 0299 reqnumber.entry.numlen=len(ii['numbers'][numindex]) 0300 numbertype=ii['numbertypes'][numindex] 0301 reqnumber.entry.numbertype=numbertype 0302 reqnumber.entry.pos=numindex+1 0303 0304 resnumber=self.sendpbcommand(reqnumber,self.protocolclass.numberresponse) 0305 0306 reqname.entry.numberps.append(nump) 0307 bitmask |= bit 0308 bit <<= 1 0309 0310 nump+=1 0311 0312 for numindex in range(len(ii['numbers']),self.protocolclass.NUMPHONENUMBERS): 0313 reqname.entry.numberps.append(0) 0314 0315 reqname.entry.bitmask=bitmask 0316 resname=self.sendpbcommand(reqname,self.protocolclass.nameresponse) 0317 0318 # Zero out unused slots 0319 for slot in range(nump,self.protocolclass.NUMPHONEBOOKENTRIES+2): 0320 reqnumber.slot=slot 0321 reqnumber.entry.num='' 0322 reqnumber.entry.numlen=0 0323 reqnumber.entry.numbertype=0 0324 reqnumber.entry.pos=0 0325 resnumber=self.sendpbcommand(reqnumber,self.protocolclass.numberresponse) 0326 for slot in range(slotsused,self.protocolclass.NUMPHONEBOOKENTRIES+1): 0327 reqname=self.protocolclass.nameupdaterequest() 0328 reqname.slot=slot 0329 reqname.entry.name='' 0330 reqname.entry.name_len=0 0331 for numindex in range(self.protocolclass.NUMPHONENUMBERS): 0332 reqname.entry.numberps.append(0) 0333 reqname.entry.bitmask=0 0334 resname=self.sendpbcommand(reqname,self.protocolclass.nameresponse) 0335 0336 self.progress(1,1, "Phonebook write completed") 0337 0338 req=self.protocolclass.beginendupdaterequest() 0339 req.beginend=2 # Finish update 0340 res=self.sendpbcommand(req, self.protocolclass.beginendupdateresponse, writemode=True) 0341 req=p_brew.setmodemmoderequest() 0342 res=self.sendpbcommand(req, p_brew.setmoderesponse, writemode=True) 0343 #data['rebootphone']=1 0344 0345 def getcalendar(self, results): 0346 return result 0347 0348 getwallpapers=None 0349 getringtones=None 0350 0351 0352 class Profile(com_phone.Profile): 0353 deviceclasses=("modem",) 0354 0355 usbids=( ( 0x04e8, 0x6601, 1), # Samsung internal USB interface 0356 ) 0357 # which device classes we are. 0358 deviceclasses=("modem","serial") 0359 0360 protocolclass=Phone.protocolclass 0361 serialsname=Phone.serialsname 0362 phone_manufacturer='SAMSUNG' 0363 phone_model='SPH-A900/154' 0364 0365 _supportedsyncs=( 0366 ('phonebook', 'read', None), # all phonebook reading 0367 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 0368 ) 0369 0370 def __init__(self): 0371 self.numbertypetab=numbertypetab 0372 com_phone.Profile.__init__(self) 0373 0374 def convertphonebooktophone(self, helper, data): 0375 "Converts the data to what will be used by the phone" 0376 0377 results={} 0378 0379 slotsused={} 0380 pb=data['phonebook'] 0381 for pbentry in pb: 0382 entry=pb[pbentry] 0383 slot=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'slot', -1) 0384 if(slot > 0 and slot <= self.protocolclass.NUMPHONEBOOKENTRIES): 0385 slotsused[slot]=1 0386 0387 lastunused=0 # One more than last unused slot 0388 0389 for pbentry in pb: 0390 e={} # entry out 0391 entry=pb[pbentry] # entry in 0392 try: 0393 try: 0394 e['name']=helper.getfullname(entry.get('names', []),1,1,20)[0] 0395 except: 0396 e['name']='' 0397 e['name_len']=len(e['name']) 0398 if len(e['name'])==0: 0399 print "Entry with no name" 0400 0401 # cat=helper.makeone(helper.getcategory(entry.get('categories',[]),0,1,16), None) 0402 # if cat is None: 0403 # e['group']=0 0404 # else: 0405 # key,value=self._getgroup(cat, data['groups']) 0406 # if key is not None: 0407 # e['group']=key 0408 # else: 0409 # e['group']=0 0410 e['group']=0 0411 0412 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'slot', -1) 0413 0414 if(slot > 0 and slot <= self.protocolclass.NUMPHONEBOOKENTRIES): 0415 e['slot']=slot 0416 else: # A new entry. Must find unused slot 0417 while(slotsused.has_key(lastunused)): 0418 lastunused+=1 0419 if(lastunused > self.protocolclass.NUMPHONEBOOKENTRIES): 0420 raise helper.ConversionFailed() 0421 e['slot']=lastunused 0422 slotsused[lastunused]=1 0423 0424 # e['emails']=helper.getemails(entry.get('emails', []),0,self.protocolclass.NUMEMAILS,self.protocolclass.MAXEMAILLEN) 0425 0426 # e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,self.protocolclass.MAXURLLEN), "") 0427 0428 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0,1,self.protocolclass.MAXMEMOLEN), "") 0429 0430 numbers=helper.getnumbers(entry.get('numbers', []),0,self.protocolclass.NUMPHONENUMBERS) 0431 e['numbertypes']=[] 0432 e['numbers']=[] 0433 e['numberspeeds']=[] 0434 0435 for numindex in range(len(numbers)): 0436 num=numbers[numindex] 0437 type=num['type'] 0438 numtype=len(self.numbertypetab) # None type is default 0439 for i,t in enumerate(self.numbertypetab): 0440 if type==t: 0441 numtype=i+1 0442 break 0443 # If type not found, give it "none" type 0444 e['numbertypes'].append(numtype) 0445 0446 # Need to enforce phone number length limit 0447 number=self.phonize(num['number']) 0448 e['numbers'].append(number) 0449 0450 # deal with speed dial 0451 # sd=num.get("speeddial", 0xFF) 0452 # if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL: 0453 # e['numberspeeds'].append(sd) 0454 # else: 0455 # e['numberspeeds'].append(0) 0456 0457 #e['numberspeeds']=helper.filllist(e['numberspeeds'], self.protocolclass.NUMPHONENUMBERS, 0xFF) 0458 #e['numbertypes']=helper.filllist(e['numbertypes'], self.protocolclass.NUMPHONENUMBERS, 0) 0459 #e['numbers']=helper.filllist(e['numbers'], self.protocolclass.NUMPHONENUMBERS, "") 0460 0461 0462 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) 0463 e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) 0464 0465 e['secret']=helper.getflag(entry.get('flags', []), 'secret', False) 0466 results[pbentry]=e 0467 0468 except helper.ConversionFailed: 0469 #self.log("No Free Slot for "+e['name']) 0470 print "No Free Slot for "+e['name'] 0471 continue 0472 0473 data['phonebook']=results 0474 return data 0475
Generated by PyXR 0.9.4