0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2006 Simon Capper <skyjunky@sbcglobal.net> 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 0009 """Communicate with the LG PM225 (Sprint) cell phone""" 0010 0011 # standard modules 0012 import re 0013 import time 0014 import cStringIO 0015 import sha 0016 0017 # my modules 0018 import p_lgpm225 0019 import p_brew 0020 import common 0021 import commport 0022 import com_brew 0023 import com_phone 0024 import com_lg 0025 import com_lgvx4400 0026 import helpids 0027 import prototypes 0028 import call_history 0029 import sms 0030 import fileinfo 0031 import memo 0032 0033 0034 class Phone(com_lgvx4400.Phone): 0035 "Talk to the LG PM225 cell phone" 0036 0037 desc="LG PM225" 0038 helpid=helpids.ID_PHONE_LGPM225 0039 protocolclass=p_lgpm225 0040 serialsname='lgpm225' 0041 0042 # read only locations, for regular ringers/wallpaper this phone stores 0043 # the information in a different location 0044 imagelocations=( 0045 # offset, index file, files location, type, maximumentries 0046 (0x600, "setas/dcamIndex.map", "Dcam/Wallet", "camera", 50, 6), 0047 ) 0048 0049 ringtonelocations=( 0050 # offset, index file, files location, type, maximumentries 0051 (0x1100, "setas/voicememoRingerIndex.map", "VoiceDB/All/Memos", "voice_memo", 50, 11), 0052 ) 0053 0054 builtinimages=('Spectrum', 'Speed', 'Drops', 'Country Road', 0055 'Houses','Tulip', 'Flower', 'Lines', 'Network', 'Abstract') 0056 0057 builtinringtones=( 'Tone 1', 'Tone 2', 'Tone 3', 'Tone 4', 'Tone 5', 'Tone 6', 0058 'Alert 1', 'Alert 2', 'Alert 3', 'Alert 4', 'Alert 5', 'Alert 6', 0059 'Moonlight', 'Bumble Bee', 'Latin', 'Baroque', 0060 'Lovable Baby', 'LG Sound', 'Love Song', 'Badinerie', 0061 'Follow Me', 'Head, Shoulders, Knees & Toes', 'Lake Trance', 'Beethoven', 0062 'Let''s Play', 'Piano Concerto No.1', 'Pleasure', 0063 'Leichte Kavallerie', 'Up & Down Melody', 'Vivaldi - Winter') 0064 0065 def __init__(self, logtarget, commport): 0066 "Calls all the constructors and sets initial modes" 0067 com_phone.Phone.__init__(self, logtarget, commport) 0068 com_brew.BrewProtocol.__init__(self) 0069 com_lg.LGPhonebook.__init__(self) 0070 self.log("Attempting to contact phone") 0071 self._cal_has_voice_id=hasattr(self.protocolclass, 'cal_has_voice_id') \ 0072 and self.protocolclass.cal_has_voice_id 0073 self.mode=self.MODENONE 0074 0075 #----- PhoneBook ----------------------------------------------------------------------- 0076 0077 def getfundamentals(self, results): 0078 """Gets information fundamental to interoperating with the phone and UI. 0079 0080 Currently this is: 0081 0082 - 'uniqueserial' a unique serial number representing the phone 0083 - 'groups' the phonebook groups 0084 0085 This method is called before we read the phonebook data or before we 0086 write phonebook data. 0087 """ 0088 0089 # use a hash of ESN and other stuff (being paranoid) 0090 self.log("Retrieving fundamental phone information") 0091 self.log("Phone serial number") 0092 results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() 0093 self.log(results) 0094 0095 # now read groups 0096 self.log("Reading group information") 0097 buf=prototypes.buffer(self.getfilecontents("pim/pbookgroup.dat")) 0098 g=self.protocolclass.pbgroups() 0099 g.readfrombuffer(buf, logtitle="Groups read") 0100 groups={} 0101 for i in range(len(g.groups)): 0102 if len(g.groups[i].name): # sometimes have zero length names 0103 groups[g.groups[i].group_id]={'name': g.groups[i].name } 0104 results['groups']=groups 0105 self.getwallpaperindices(results) 0106 self.getringtoneindices(results) 0107 self.log("Fundamentals retrieved") 0108 return results 0109 0110 def syncbegin(self): 0111 self.mode = self.MODEPHONEBOOK 0112 self.sendpbcommand(self.protocolclass.pbstartsyncrequest(), self.protocolclass.pbstartsyncresponse) 0113 0114 def syncend(self): 0115 req=self.protocolclass.pbendsyncrequest() 0116 self.sendpbcommand(req, self.protocolclass.pbendsyncresponse) 0117 0118 def getphonebook(self,result): 0119 """Reads the phonebook data. The L{getfundamentals} information will 0120 already be in result.""" 0121 0122 pbook={} 0123 # Bug in the phone. if you repeatedly read the phone book it starts 0124 # returning a random number as the number of entries. We get around 0125 # this by switching into brew mode which clears that. 0126 self.mode=self.MODENONE 0127 self.setmode(self.MODEBREW) 0128 0129 self.log("Reading number of phonebook entries") 0130 self.mode = self.MODEBREW 0131 res=self.sendpbcommand(self.protocolclass.pbinforequest(), self.protocolclass.pbinforesponse) 0132 numentries = res.numentries 0133 self.log("There are %d entries" % (numentries,)) 0134 for i in range(0, numentries): 0135 ### Read current entry 0136 req=self.protocolclass.pbreadentryrequest() 0137 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 0138 self.log("Read entry "+`i`+" - "+res.entry.name) 0139 entry=self.extractphonebookentry(res.entry, result) 0140 pbook[res.entry.entrynumber]=entry 0141 self.progress(i, numentries, res.entry.name) 0142 #### Advance to next entry 0143 req=self.protocolclass.pbnextentryrequest() 0144 self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 0145 0146 pbook=self.get_phonebook_media(pbook, result) 0147 0148 self.progress(numentries, numentries, "Phone book read completed") 0149 self.log("Phone book read completed") 0150 0151 result['phonebook']=pbook 0152 0153 cats=[] 0154 for i in result['groups']: 0155 if result['groups'][i]['name']!='No Group': 0156 cats.append(result['groups'][i]['name']) 0157 result['categories']=cats 0158 return pbook 0159 0160 def get_phonebook_media(self, pbook, fundamentals): 0161 """This phone does not provide ringtone and image info for contacts in 0162 the regular packets so we have to read the filesystem directly """ 0163 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.phonebook_media)) 0164 g=self.protocolclass.pb_contact_media_file() 0165 g.readfrombuffer(buf, logtitle="PB Media read") 0166 for i in range(len(g.contacts)): 0167 # adjust wallpaper for stock 0168 if (g.contacts[i].wallpaper & 0xFF00)==0x100: 0169 g.contacts[i].wallpaper-=0x100 0170 if __debug__: 0171 tone="None" 0172 paper="None" 0173 try: 0174 tone=fundamentals['ringtone-index'][g.contacts[i].ringer]['name'] 0175 except: 0176 pass 0177 try: 0178 paper=fundamentals['wallpaper-index'][g.contacts[i].wallpaper]['name'] 0179 except: 0180 pass 0181 self.log("media entry "+g.contacts[i].name+" ringer "+`tone`+" ("+`g.contacts[i].ringer`+")") 0182 self.log("media entry "+g.contacts[i].name+" wallpaper "+`paper`+" ("+`g.contacts[i].wallpaper`+")") 0183 if g.contacts[i].index in pbook: 0184 self.log("Index "+`g.contacts[i].index`+" found") 0185 if g.contacts[i].ringer: 0186 try: 0187 tone=fundamentals['ringtone-index'][g.contacts[i].ringer]['name'] 0188 pbook[g.contacts[i].index]['ringtones']=[{'ringtone': tone, 'use': 'call'}] 0189 except: 0190 self.log("Exception in ringtone assignment") 0191 if g.contacts[i].wallpaper: 0192 try: 0193 paper=fundamentals['wallpaper-index'][g.contacts[i].wallpaper]['name'] 0194 pbook[g.contacts[i].index]['wallpapers']=[{'wallpaper': paper, 'use': 'call'}] 0195 except: 0196 self.log("Exception in wallpaper assignment") 0197 else: 0198 self.log("Index "+`g.contacts[i].index`+" not found") 0199 return pbook 0200 0201 def extractphonebookentry(self, entry, fundamentals): 0202 """Return a phonebook entry in BitPim format. This is called from getphonebook.""" 0203 res={} 0204 # serials 0205 res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.serial1, 0206 'sourceuniqueid': fundamentals['uniqueserial']} ] 0207 # only one name 0208 res['names']=[ {'full': entry.name} ] 0209 # only one category 0210 cat=fundamentals['groups'].get(entry.group, {'name': "No Group"})['name'] 0211 if cat!="No Group": 0212 res['categories']=[ {'category': cat} ] 0213 # emails 0214 res['emails']=[] 0215 for i in entry.emails: 0216 if len(i.email): 0217 res['emails'].append( {'email': i.email} ) 0218 if not len(res['emails']): del res['emails'] # it was empty 0219 # urls 0220 if 'url' in entry.getfields() and len(entry.url): 0221 res['urls']=[ {'url': entry.url} ] 0222 # private 0223 if 'secret' in entry.getfields() and entry.secret: 0224 # we only supply secret if it is true 0225 res['flags']=[ {'secret': entry.secret } ] 0226 # memos 0227 if 'memo' in entry.getfields() and len(entry.memo): 0228 res['memos']=[ {'memo': entry.memo } ] 0229 0230 # numbers 0231 res['numbers']=[] 0232 for i in range(self.protocolclass.NUMPHONENUMBERS): 0233 num=entry.numbers[i].number 0234 type=entry.numbertypes[i].numbertype 0235 speeddial=entry.numberspeeds[i].numberspeed 0236 if len(num): 0237 t=self.protocolclass.numbertypetab[type] 0238 if speeddial != 0xFF: # invalid entry 0239 res['numbers'].append({'number': num, 'type': t, 'speeddial': speeddial}) 0240 else: 0241 res['numbers'].append({'number': num, 'type': t}) 0242 return res 0243 0244 def makeentry(self, counter, entry, data): 0245 """Creates pbentry object 0246 0247 @param counter: The new entry number 0248 @param entry: The phonebook object (as returned from convertphonebooktophone) that we 0249 are using as the source 0250 @param data: The main dictionary, which we use to get access to media indices amongst 0251 other things 0252 """ 0253 e=self.protocolclass.pbentry() 0254 0255 e.entrynumber=counter 0256 0257 for k in entry: 0258 # special treatment for lists 0259 if k in ('emails', 'numbers', 'numbertypes', 'numberspeeds'): 0260 l=getattr(e,k) 0261 for item in entry[k]: 0262 l.append(item) 0263 elif k=='ringtone': 0264 try: 0265 e.ringtone=self._findmediainindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone') 0266 except: 0267 pass 0268 elif k=='msgringtone': 0269 pass # not supported by phone 0270 #e.msgringtone=self._findmediainindex(data['ringtone-index'], entry['msgringtone'], entry['name'], 'message ringtone') 0271 elif k=='wallpaper': 0272 try: 0273 e.wallpaper=self._findmediainindex(data['wallpaper-index'], entry['wallpaper'], entry['name'], 'wallpaper') 0274 # adjust for stock wallpaper 0275 if e.wallpaper < 0x100: 0276 e.wallpaper+=0x100 0277 except: 0278 pass 0279 elif k in e.getfields(): 0280 # everything else we just set 0281 setattr(e,k,entry[k]) 0282 0283 return e 0284 0285 def save_phonebook_media(self, pb_entries): 0286 """This phone does not provide ringtone and image info for contacts in 0287 the regular packets so we have to write the filesystem directly """ 0288 # we read the file, modify it and write it back 0289 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.phonebook_media)) 0290 g=self.protocolclass.pb_contact_media_file() 0291 g.readfrombuffer(buf, logtitle="PB Media read") 0292 rc=False 0293 for i in range(len(g.contacts)): 0294 if g.contacts[i].index in pb_entries: 0295 if g.contacts[i].ringer!=pb_entries[g.contacts[i].index].ringtone: 0296 g.contacts[i].ringer=pb_entries[g.contacts[i].index].ringtone 0297 rc=True 0298 if g.contacts[i].wallpaper!=pb_entries[g.contacts[i].index].wallpaper: 0299 g.contacts[i].wallpaper=pb_entries[g.contacts[i].index].wallpaper 0300 rc=True 0301 if rc: 0302 buf=prototypes.buffer() 0303 g.writetobuffer(buf, logtitle="Writing PB media file") 0304 self.writefile(self.protocolclass.phonebook_media, buf.getvalue()) 0305 else: 0306 self.log("PB media file up to date, no write required") 0307 return rc 0308 0309 def savegroups(self, data): 0310 groups=data['groups'] 0311 keys=groups.keys() 0312 keys.sort() 0313 g=self.protocolclass.pbgroups() 0314 for k in keys: 0315 e=self.protocolclass.pbgroup() 0316 e.group_id=k 0317 e.rectype = 0x30 0318 e.name=groups[k]['name'] 0319 g.groups.append(e) 0320 buffer=prototypes.buffer() 0321 g.writetobuffer(buffer, logtitle="New group file") 0322 self.writefile("pim/pbookgroup.dat", buffer.getvalue()) 0323 0324 def savephonebook(self, data): 0325 "Saves out the phonebook" 0326 self.savegroups(data) 0327 0328 # if __debug__: 0329 # # for testing without a real phone, the packets are stored in a 0330 # # file and can be examined 0331 # counter=0 0332 # pb_entries={} 0333 # for i in data['phonebook'].keys(): 0334 # ii=data['phonebook'][i] 0335 # entry=self.makeentry(counter, ii, data) 0336 # counter+=1 0337 # req=self.protocolclass.pbupdateentryrequest() 0338 # req.entry=entry 0339 # req.header.sequence=counter 0340 # buffer=prototypes.buffer() 0341 # req.writetobuffer(buffer, logtitle="New contents for pim/pb"+`counter`+".dat") 0342 # self.writefile("pim/pb"+`counter`+".dat", buffer.getvalue()) 0343 # pb_entries[counter]=entry 0344 # self.save_phonebook_media(pb_entries) 0345 0346 # set up progress bar on main window 0347 progressmax=len(data['phonebook'].keys()) 0348 0349 # To write the phone book, we scan through all existing entries 0350 # and record their record number and serials. 0351 # We then delete any entries that aren't in data 0352 # We then write out our records, using overwrite or append 0353 # commands as necessary 0354 serialupdates=[] 0355 pb_entries={} 0356 existingpbook={} # keep track of the phonebook that is on the phone 0357 self.mode=self.MODENONE 0358 self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary 0359 self.setmode(self.MODEPHONEBOOK) 0360 # similar loop to reading 0361 req=self.protocolclass.pbinforequest() 0362 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) 0363 numexistingentries=res.numentries 0364 self.log("There are %d existing entries" % (numexistingentries,)) 0365 progressmax+=numexistingentries 0366 loop=xrange(0, numexistingentries) 0367 progresscur=0 0368 0369 # reset cursor 0370 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) 0371 for i in loop: 0372 ### Read current entry 0373 req=self.protocolclass.pbreadentryrequest() 0374 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 0375 0376 entry={ 'number': res.entry.entrynumber, 'serial1': res.entry.serial1, 'name': res.entry.name} 0377 0378 self.log("Reading entry "+`i`+" - "+str(entry['serial1'])+" - "+entry['name']) 0379 existingpbook[i]=entry 0380 self.progress(progresscur, progressmax, "existing "+entry['name']) 0381 #### Advance to next entry 0382 req=self.protocolclass.pbnextentryrequest() 0383 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 0384 progresscur+=1 0385 # we have now looped around back to begining 0386 0387 # Find entries that have been deleted 0388 pbook=data['phonebook'] 0389 dellist=[] 0390 for i in loop: 0391 ii=existingpbook[i] 0392 serial=ii['serial1'] 0393 item=self._findserial(serial, pbook) 0394 if item is None: 0395 dellist.append(i) 0396 0397 progressmax+=len(dellist) # more work to do 0398 0399 # Delete those entries 0400 for i in dellist: 0401 progresscur+=1 0402 numexistingentries-=1 # keep count right 0403 ii=existingpbook[i] 0404 self.log("Deleting entry "+`i`+" - "+str(ii['serial1'])+" - "+ii['name']) 0405 req=self.protocolclass.pbdeleteentryrequest() 0406 req.serial1=ii['serial1'] 0407 req.serial2=ii['serial1'] 0408 req.entrynumber=ii['number'] 0409 self.sendpbcommand(req, self.protocolclass.pbdeleteentryresponse) 0410 self.progress(progresscur, progressmax, "Deleting "+ii['name']) 0411 # also remove them from existingpbook 0412 del existingpbook[i] 0413 0414 # Now rewrite out existing entries 0415 self.log("Rewrite existing entries") 0416 keys=existingpbook.keys() 0417 existingserials=[] 0418 keys.sort() # do in same order as existingpbook 0419 for i in keys: 0420 progresscur+=1 0421 ii=pbook[self._findserial(existingpbook[i]['serial1'], pbook)] 0422 self.log("Rewriting entry "+`i`+" - "+ii['name']) 0423 self.progress(progresscur, progressmax, "Rewriting "+ii['name']) 0424 entry=self.makeentry(existingpbook[i]['serial1'], ii, data) 0425 existingserials.append(existingpbook[i]['serial1']) 0426 req=self.protocolclass.pbupdateentryrequest() 0427 req.entry=entry 0428 res=self.sendpbcommand(req, self.protocolclass.pbupdateentryresponse) 0429 serialupdates.append( ( ii["bitpimserial"], 0430 {'sourcetype': self.serialsname, 0431 'serial1': res.serial1, 0432 'sourceuniqueid': data['uniqueserial']})) 0433 assert ii['serial1']==res.serial1 # serial should stay the same 0434 pb_entries[res.serial1]=entry 0435 0436 # Finally write out new entries 0437 counter=0 0438 keys=pbook.keys() 0439 self.log("Write new entries") 0440 keys.sort() 0441 for i in keys: 0442 ii=pbook[i] 0443 if ii['serial1'] in existingserials: 0444 continue # already wrote this one out 0445 # find an unused serial number 0446 while True: 0447 if counter in existingserials: 0448 counter+=1 0449 else: 0450 break 0451 progresscur+=1 0452 entry=self.makeentry(counter, ii, data) 0453 self.log("Appending entry "+ii['name']) 0454 self.progress(progresscur, progressmax, "Writing "+ii['name']) 0455 req=self.protocolclass.pbappendentryrequest() 0456 req.entry=entry 0457 res=self.sendpbcommand(req, self.protocolclass.pbappendentryresponse) 0458 serialupdates.append( ( ii["bitpimserial"], 0459 {'sourcetype': self.serialsname, 0460 'serial1': res.newserial, 0461 'sourceuniqueid': data['uniqueserial']})) 0462 pb_entries[res.newserial]=entry 0463 counter+=1 0464 # update the media file 0465 data['serialupdates']=serialupdates 0466 0467 changed=self.save_phonebook_media(pb_entries) 0468 if changed: 0469 data["rebootphone"]=True 0470 return data 0471 0472 #----- SMS --------------------------------------------------------------------------- 0473 0474 def _readsms(self): 0475 res={} 0476 # go through the sms directory looking for messages 0477 for item in self.listfiles("sms").values(): 0478 folder=None 0479 for f,pat in self.protocolclass.SMS_PATTERNS.items(): 0480 if pat.match(item['name']): 0481 folder=f 0482 break 0483 if folder: 0484 buf=prototypes.buffer(self.getfilecontents(item['name'], True)) 0485 self.logdata("SMS message file " +item['name'], buf.getdata()) 0486 if folder=='Inbox': 0487 sf=self.protocolclass.sms_in() 0488 sf.readfrombuffer(buf, logtitle="SMS inbox item") 0489 entry=self._getinboxmessage(sf) 0490 res[entry.id]=entry 0491 elif folder=='Sent': 0492 sf=self.protocolclass.sms_out() 0493 sf.readfrombuffer(buf, logtitle="SMS sent item") 0494 entry=self._getoutboxmessage(sf) 0495 res[entry.id]=entry 0496 return res 0497 0498 0499 def _setquicktext(self, result): 0500 sf=self.protocolclass.sms_canned_file() 0501 quicktext=result.get('canned_msg', []) 0502 count=0 0503 for entry in quicktext: 0504 if count < self.protocolclass.SMS_CANNED_MAX_ITEMS: 0505 msg_entry=self.protocolclass.sms_quick_text() 0506 msg_entry.msg=entry['text'][:self.protocolclass.SMS_CANNED_MAX_LENGTH-1] 0507 sf.msgs.append(msg_entry) 0508 count+=1 0509 else: 0510 break 0511 if count!=0: 0512 # don't create the file if there are no entries 0513 sf.num_active=count 0514 buf=prototypes.buffer() 0515 sf.writetobuffer(buf, logtitle="Writing quicktext") 0516 self.writefile(self.protocolclass.SMS_CANNED_FILENAME, buf.getvalue()) 0517 return 0518 0519 def _getquicktext(self): 0520 quicks=[] 0521 try: 0522 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.SMS_CANNED_FILENAME)) 0523 sf=self.protocolclass.sms_canned_file() 0524 sf.readfrombuffer(buf, logtitle="SMS quicktext file sms/canned_msg.dat") 0525 for rec in sf.msgs: 0526 if rec.msg!="": 0527 quicks.append({ 'text': rec.msg, 'type': sms.CannedMsgEntry.user_type }) 0528 except com_brew.BrewNoSuchFileException: 0529 pass # do nothing if file doesn't exist 0530 return quicks 0531 0532 def _getinboxmessage(self, sf): 0533 entry=sms.SMSEntry() 0534 entry.folder=entry.Folder_Inbox 0535 entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime) 0536 entry._from=self._getsender(sf.sender, sf.sender_length) 0537 entry.subject=sf.subject 0538 entry.locked=sf.locked 0539 # if sf.priority==0: 0540 # entry.priority=sms.SMSEntry.Priority_Normal 0541 # else: 0542 # entry.priority=sms.SMSEntry.Priority_High 0543 entry.read=sf.read 0544 entry.text=sf.msg 0545 entry.callback=sf.callback 0546 return entry 0547 0548 def _getoutboxmessage(self, sf): 0549 entry=sms.SMSEntry() 0550 if not sf.saved: 0551 entry.folder=entry.Folder_Sent 0552 else: 0553 entry.folder=entry.Folder_Saved 0554 entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent)) 0555 # add all the recipients 0556 for r in sf.recipients: 0557 if r.number: 0558 confirmed=(r.status==2) 0559 confirmed_date=None 0560 if confirmed: 0561 confirmed_date="%d%02d%02dT%02d%02d00" % r.time 0562 entry.add_recipient(r.number, confirmed, confirmed_date) 0563 entry.subject=sf.msg[:28] 0564 entry.text=sf.msg 0565 # if sf.priority==0: 0566 # entry.priority=sms.SMSEntry.Priority_Normal 0567 # else: 0568 # entry.priority=sms.SMSEntry.Priority_High 0569 entry.locked=sf.locked 0570 entry.callback=sf.callback 0571 return entry 0572 0573 0574 #----- Media ----------------------------------------------------------------------- 0575 0576 # this phone can take MP3 files, but you have to use the vnd.qcelp MIME type 0577 # this makes renaming the files (from the ##.dat format) back to a real 0578 # name difficult, we assume all vnd.qcelp files are mp3 files as this is the 0579 # most common format 0580 0581 __mimetype={ 'mid': 'audio/midi', 'qcp': 'audio/vnd.qcelp', 'jar': 'application/java-archive', 0582 'jpg': 'image/jpg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 0583 'bmp': 'image/bmp', 'png': 'image/png', 'mp3': 'audio/vnd.qcelp'} 0584 0585 __reverse_mimetype={ 'audio/midi': 'mid', 'audio/vnd.qcelp': 'mp3', 'application/java-archive': 'jar', 0586 'image/jpg': 'jpg', 'image/jpeg': 'jpeg', 'image/gif': 'gif', 0587 'image/bmp': 'bmp', 'image/png': 'png', 'audio/mp3': 'mp3'} 0588 0589 __app_extensions={ 'jar':'' } 0590 0591 0592 def getwallpaperindices(self, results): 0593 return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index') 0594 0595 def getringtoneindices(self, results): 0596 return self.getmediaindex(self.builtinringtones, self.ringtonelocations, results, 'ringtone-index') 0597 0598 def getwallpapers(self, result): 0599 return self.getmedia(self.imagelocations, result, 'wallpapers') 0600 0601 def getringtones(self, result): 0602 return self.getmedia(self.ringtonelocations, result, 'ringtone') 0603 0604 def savewallpapers(self, results, merge): 0605 return self.savemedia('wallpapers', 'wallpaper-index', results, merge, self.getwallpaperindices) 0606 0607 def saveringtones(self, results, merge): 0608 return self.savemedia('ringtone', 'ringtone-index', results, merge, self.getringtoneindices) 0609 0610 def getindex(self, indexfile): 0611 "Read an index file" 0612 index={} 0613 try: 0614 buf=prototypes.buffer(self.getfilecontents(indexfile)) 0615 except com_brew.BrewNoSuchFileException: 0616 # file may not exist 0617 return index 0618 g=self.protocolclass.indexfile() 0619 g.readfrombuffer(buf, logtitle="Read indexfile "+indexfile) 0620 for i in g.items: 0621 if i.index!=0xffff and len(i.name): 0622 index[i.index]=i.name 0623 return index 0624 0625 def get_content_file(self, key): 0626 index={} 0627 if key=='ringtone' or key=='ringtone-index': 0628 type='Ringers' 0629 index_const=self.protocolclass.ringerconst*0x100 0630 indexfile=self.getindex(self.protocolclass.ringerindex) 0631 else: 0632 type='Screen Savers' 0633 index_const=self.protocolclass.imageconst*0x100 0634 indexfile=self.getindex(self.protocolclass.imageindex) 0635 try: 0636 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.content_file_name)) 0637 g=self.protocolclass.content_file() 0638 g.readfrombuffer(buf, logtitle="Content file "+self.protocolclass.content_file_name) 0639 for i in g.items: 0640 if i.type=='!C' and i.content_type==type: 0641 try: 0642 # construct a user friendly filename 0643 ext=self.__reverse_mimetype[i.mime_type] 0644 # find the "file" in the index file and get it's index 0645 # if the index was created by bitpim it will be the same 0646 # as the unfriendly filename, but this is not guarenteed 0647 found=False 0648 for j in indexfile.keys(): 0649 # convert to int to strip leading zero 0650 try: 0651 if int(common.stripext(indexfile[j]))==int(i.index1): 0652 index[j + index_const]=i.name1+'.'+ext 0653 found=True 0654 break; 0655 except: 0656 pass 0657 if not found: 0658 self.log("Unable to find index entry for "+i.name1+". Index : "+`i.index1`) 0659 except: 0660 pass 0661 except com_brew.BrewNoSuchFileException: 0662 pass 0663 return index, indexfile, index_const 0664 0665 def getmedia(self, maps, result, key): 0666 """Returns the contents of media as a dict where the key is a name as returned 0667 by getindex, and the value is the contents of the media""" 0668 media={} 0669 # first read the maps 0670 type=None 0671 for offset,indexfile,location,type,maxentries,const in maps: 0672 index=self.getindex(indexfile) 0673 for i in index: 0674 try: 0675 media[index[i]]=self.getfilecontents(location+"/"+index[i], True) 0676 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException): 0677 self.log("It was in the index, but not on the filesystem") 0678 0679 # secondly read the content file 0680 index, indexfile, index_const=self.get_content_file(key) 0681 for i in index: 0682 try: 0683 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.media_directory+'/'+indexfile[i-index_const], True)) 0684 _fname=index[i] 0685 # This would be nice but this will read the entire file which will slow the media retrieval down 0686 # ext=common.getext(_fname) 0687 # if ext=='mp3': 0688 # # see if it is really an mp3, we will need to rename it 0689 # try: 0690 # qcp_header=self.protocolclass.qcp_media_header() 0691 # qcp_header.readfrombuffer(buf, logtitle="qcp header") 0692 # except: # exception will be thrown if header does not match 0693 # _fname=common.stripext(index[i])+'.'+'qcp' 0694 media[_fname]=buf.getdata() 0695 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException): 0696 self.log("It was in the index, but not on the filesystem") 0697 self.log("Contents not in the filesystem") 0698 0699 result[key]=media 0700 return result 0701 0702 def getmediaindex(self, builtins, maps, results, key): 0703 """Gets the media (wallpaper/ringtone) index 0704 0705 @param builtins: the builtin list on the phone 0706 @param results: places results in this dict 0707 @param maps: the list of index files and locations 0708 @param key: key to place results in 0709 """ 0710 0711 self.log("Reading "+key) 0712 media={} 0713 0714 # builtins 0715 c=1 0716 for name in builtins: 0717 media[c]={'name': name, 'origin': 'builtin' } 0718 c+=1 0719 0720 # the maps 0721 type=None 0722 for offset,indexfile,location,type,maxentries,const in maps: 0723 index=self.getindex(indexfile) 0724 for i in index: 0725 media[i+offset]={'name': index[i], 'origin': type} 0726 0727 # secondly read the content file 0728 if key=='ringtone-index': 0729 type='ringers' 0730 else: 0731 type='images' 0732 index,_,_=self.get_content_file(key) 0733 for i in index: 0734 media[i]={'name': index[i], 'origin': type} 0735 results[key]=media 0736 return media 0737 0738 def savemedia(self, mediakey, mediaindexkey, results, merge, reindexfunction): 0739 """Actually saves out the media 0740 0741 @param mediakey: key of the media (eg 'wallpapers' or 'ringtones') 0742 @param mediaindexkey: index key (eg 'wallpaper-index') 0743 @param maps: list index files and locations 0744 @param results: results dict 0745 @param merge: are we merging or overwriting what is there? 0746 @param reindexfunction: the media is re-indexed at the end. this function is called to do it 0747 """ 0748 content_changed=False 0749 media=results[mediakey].copy() 0750 0751 0752 #figure out if the origin we are interested in 0753 if mediaindexkey=='ringtone-index': 0754 type='ringers' 0755 content_type="Ringers" 0756 indexfile=self.protocolclass.ringerindex 0757 index_const=self.protocolclass.ringerconst 0758 max_media_entries=self.protocolclass.max_ringers 0759 # remove voice_memos 0760 for i in media.keys(): 0761 try: 0762 if media[i]['origin']=='voice_memo': 0763 del media[i] 0764 except: 0765 pass 0766 else: 0767 type='images' 0768 content_type="Screen Savers" 0769 indexfile=self.protocolclass.imageindex 0770 index_const=self.protocolclass.imageconst 0771 max_media_entries=self.protocolclass.max_images 0772 # remove camera images 0773 for i in media.keys(): 0774 try: 0775 if media[i]['origin']=='camera': 0776 del media[i] 0777 except: 0778 pass 0779 0780 #read content file off the phone 0781 content={} 0782 try: 0783 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.content_file_name)) 0784 g=self.protocolclass.content_file() 0785 g.readfrombuffer(buf, logtitle="Read content file") 0786 for i in g.items: 0787 # type !C always appears first 0788 if i.type=='!C': 0789 content[int(i.index1)]= {'C': i} 0790 elif i.type=='!E': 0791 content[int(i.index2)]['E']=i 0792 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException): 0793 pass 0794 # get a list of files in the media directory so we can figure out what to delete 0795 # and what needs to be copied onto the phone 0796 dirlisting=self.getfilesystem(self.protocolclass.media_directory) 0797 # strip path from directory listing 0798 for i in dirlisting.keys(): 0799 dirlisting[i[len(self.protocolclass.media_directory)+1:]]=dirlisting[i] 0800 del dirlisting[i] 0801 0802 # build up list of existing media items into init, remove missing items from content list 0803 init={} 0804 for k in content.keys(): 0805 if content[k]['C'].content_type==content_type: 0806 index=k 0807 name=content[k]['C'].name1 0808 size=int(content[k]['C'].size) 0809 data=None 0810 # find the media content file, check that the size matches 0811 for w in media: 0812 if common.stripext(media[w]['name'])==name and media[w]['data']!=None: 0813 size_fix=((3024+len(media[w]['data']))/1008)*1008 0814 if size_fix==size: 0815 data=media[w]['data'] 0816 del media[w] 0817 break 0818 # remove unused entries from index and filesystem 0819 if not merge and data is None: 0820 # delete the entry 0821 del content[k] 0822 content_changed=True 0823 # we have to remove the data file, the gcd file and the url file 0824 _fname='%02d.dat' % k 0825 if _fname in dirlisting: 0826 self.rmfile(self.protocolclass.media_directory+'/'+_fname) 0827 del dirlisting[_fname] 0828 gcdname='%02d.gcd' % k 0829 if gcdname in dirlisting: 0830 self.rmfile(self.protocolclass.media_directory+'/'+gcdname) 0831 del dirlisting[gcdname] 0832 urlname='%02d.url' % k 0833 if urlname in dirlisting: 0834 self.rmfile(self.protocolclass.media_directory+'/'+urlname) 0835 del dirlisting[urlname] 0836 continue 0837 init[index]={'name': name, 'data': data} 0838 0839 # go through adding new media not currently on the phone 0840 # assign these item keys in the content media 0841 applications={} 0842 for sp in range(100): 0843 if len(media.keys()) == 0: 0844 break 0845 if sp not in content: 0846 #found a hole 0847 for w in media.keys(): 0848 C,E,add_to_index=self.make_new_media_entry(media[w], sp, type) 0849 if add_to_index=='index': 0850 content[sp]= {'C': C, 'E': E} 0851 init[sp]=media[w] 0852 content_changed=True 0853 elif add_to_index=='content': 0854 content[sp]= {'C': C, 'E': E} 0855 applications[sp]=media[w] 0856 content_changed=True 0857 else: 0858 self.log("Unknown media type for "+`media[w]['name']`+". Not written to phone.") 0859 sp-=1 0860 del media[w] 0861 break 0862 0863 if len(media.keys()): 0864 self.log("Phone index full, some media not written to phone") 0865 0866 # write out the new index, there are two files on this phone 0867 # the content file and the media index, kinda dumb having two copies 0868 # of the same thing 0869 # content_file 0870 content_count=0 0871 keys=content.keys() 0872 keys.sort() 0873 cfile=self.protocolclass.content_file() 0874 # add in the !C types first 0875 for k in keys: 0876 content_count+=1 0877 cfile.items.append(content[k]['C']) 0878 for k in keys: 0879 cfile.items.append(content[k]['E']) 0880 #terminator 0881 entry=self.protocolclass.content_entry() 0882 entry.type='!F' 0883 cfile.items.append(entry) 0884 buffer=prototypes.buffer() 0885 cfile.writetobuffer(buffer, logtitle="Updated content file "+self.protocolclass.content_file_name) 0886 self.writefile(self.protocolclass.content_file_name, buffer.getvalue()) 0887 0888 countfile=self.protocolclass.content_count() 0889 countfile.count=`content_count` 0890 buffer=prototypes.buffer() 0891 countfile.writetobuffer(buffer, logtitle="Updated content count file "+self.protocolclass.content_count_file_name) 0892 self.writefile(self.protocolclass.content_count_file_name, buffer.getvalue()) 0893 0894 # now write out the index file (this is like the verizon LG phones) 0895 keys=init.keys() 0896 keys.sort() 0897 ifile=self.protocolclass.indexfile() 0898 ifile.numactiveitems=len(keys) 0899 for k in keys: 0900 entry=self.protocolclass.indexentry() 0901 entry.index=k 0902 entry.const=index_const 0903 entry.name='%02d.dat' % k 0904 ifile.items.append(entry) 0905 # fill the remainder of the entries with unused keys 0906 for k in range(max_media_entries): 0907 if k in keys: 0908 continue 0909 entry=self.protocolclass.indexentry() 0910 entry.index=k 0911 entry.const=index_const 0912 ifile.items.append(entry) 0913 buffer=prototypes.buffer() 0914 ifile.writetobuffer(buffer, logtitle="Updated index file "+indexfile) 0915 self.writefile(indexfile, buffer.getvalue()) 0916 0917 # Write out files - we compare against existing dir listing and don't rewrite if they 0918 # are the same size 0919 for k in keys: 0920 entry=init[k] 0921 data=entry.get("data", None) 0922 _fname='%02d.dat' % k 0923 gcdname='%02d.gcd' % k 0924 urlname='%02d.url' % k 0925 if data is None: 0926 if _fname not in dirlisting: 0927 self.log("Index error. I have no data for "+entry['name']+" and it isn't already in the filesystem") 0928 continue 0929 contentsize=len(data) 0930 urlcontents='file://'+entry['name'] 0931 gcdcontents=self.makegcd(entry['name'],contentsize) 0932 if _fname in dirlisting and len(data)==dirlisting[_fname]['size']: 0933 self.log("Skipping writing %s/%s as there is already a file of the same length" % (self.protocolclass.media_directory,entry['name'])) 0934 # repair gcd and url incase they are missing 0935 if gcdname not in dirlisting: 0936 self.writefile(self.protocolclass.media_directory+"/"+gcdname, gcdcontents) 0937 if urlname not in dirlisting: 0938 self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents) 0939 continue 0940 self.writefile(self.protocolclass.media_directory+"/"+_fname, data) 0941 self.writefile(self.protocolclass.media_directory+"/"+gcdname, gcdcontents) 0942 self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents) 0943 0944 # write out applications 0945 for k in applications.keys(): 0946 entry=applications[k] 0947 data=entry.get("data", None) 0948 _fname='%02d.jar' % k 0949 jadname='%02d.jad' % k 0950 urlname='%02d.url' % k 0951 if data is None: 0952 if _fname not in dirlisting: 0953 self.log("Index error. I have no data for "+entry['name']+" and it isn't already in the filesystem") 0954 print "here2" 0955 continue 0956 contentsize=len(data) 0957 urlcontents='file://'+entry['name'] 0958 jadcontents=self.makejad(entry['name'],contentsize) 0959 print "here3" 0960 if _fname in dirlisting and len(data)==dirlisting[_fname]['size']: 0961 self.log("Skipping writing %s/%s as there is already a file of the same length" % (self.protocolclass.media_directory,entry['name'])) 0962 # repair jad and url incase they are missing 0963 if jadname not in dirlisting: 0964 self.writefile(self.protocolclass.media_directory+"/"+jadname, jadcontents) 0965 if urlname not in dirlisting: 0966 self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents) 0967 print "here4" 0968 continue 0969 print "here5" 0970 self.writefile(self.protocolclass.media_directory+"/"+_fname, data) 0971 self.writefile(self.protocolclass.media_directory+"/"+jadname, jadcontents) 0972 self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents) 0973 0974 del results[mediakey] # done with it 0975 reindexfunction(results) 0976 if content_changed: 0977 results["rebootphone"]=True 0978 return results 0979 0980 def make_new_media_entry(self, entry, index, type): 0981 c=self.protocolclass.content_entry() 0982 e=self.protocolclass.content_entry() 0983 name=common.stripext(entry['name']) 0984 ext=common.getext(entry['name']) 0985 data=entry.get("data", None) 0986 add_to_index='index' 0987 c.type='!C' 0988 c.index1=index 0989 c.name1=name 0990 try: 0991 c.mime_type=self.__mimetype[ext] 0992 except: 0993 # bad file type 0994 add_to_index='none' 0995 return c, e, add_to_index 0996 if c.mime_type=='application/java-archive': 0997 c.content_type='Games' 0998 e.location_maybe='midlet:'+name 0999 add_to_index='content' 1000 elif type=='ringers': 1001 c.content_type='Ringers' 1002 else: 1003 c.content_type='Screen Savers' 1004 c.size=`((3024+len(data))/1008)*1008` 1005 e.type='!E' 1006 e.index2=index 1007 e.name2=name 1008 return c, e, add_to_index 1009 1010 def makegcd(self,filename,size): 1011 "Build a GCD file for filename" 1012 ext=common.getext(filename.lower()) 1013 noextname=common.stripext(filename) 1014 try: 1015 mimetype=self.__mimetype[ext] 1016 gcdcontent="Content-Type: "+mimetype+"\nContent-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file://"+filename+"\nContent-Size: "+`size`+"\nContent-Description: Content for V10044 LG PM225"+"\n\n\n" 1017 except: 1018 gcdcontent="Content-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file://"+filename+"\nContent-Size: "+`size`+"\n\n\n" 1019 return gcdcontent 1020 1021 def makejad(self,filename,size): 1022 "Build a JAD file for filename" 1023 ext=common.getext(filename.lower()) 1024 noextname=common.stripext(filename) 1025 jadcontent="MIDlet-1: "+noextname+", "+noextname+".png, BitPim\nMIDlet-Jar-Size: "+`size`+"\nMIDlet-Jar-URL: "+filename+"\nMIDlet-Name: "+noextname+"\nMIDlet-Vendor: Unknown\nMIDlet-Version: 1.0\nMicroEdition-Configuration: CLDC-1.0\nMicroEdition-Profile: MIDP-1.0\nContent-Folder: Games\n\n\n" 1026 return jadcontent 1027 1028 #----- Phone Detection ----------------------------------------------------------- 1029 1030 brew_version_file='ams/version.txt' 1031 brew_version_txt_key='ams_version.txt' 1032 my_model='PM225' 1033 1034 def getphoneinfo(self, phone_info): 1035 self.log('Getting Phone Info') 1036 try: 1037 s=self.getfilecontents('ams/version.txt') 1038 if s[:5]==self.my_model: 1039 phone_info.append('Model:', self.my_model) 1040 phone_info.append('ESN:', self.get_brew_esn()) 1041 req=p_brew.firmwarerequest() 1042 #res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 1043 #phone_info.append('Firmware Version:', res.firmware) 1044 txt=self.getfilecontents("nvm/nvm/nvm_0000")[207:217] 1045 phone_info.append('Phone Number:', txt) 1046 except: 1047 pass 1048 return 1049 1050 1051 parentprofile=com_lgvx4400.Profile 1052 class Profile(parentprofile): 1053 protocolclass=Phone.protocolclass 1054 serialsname=Phone.serialsname 1055 BP_Calendar_Version=3 1056 phone_manufacturer='LG Electronics Inc' 1057 phone_model='PM225' # from Sprint 1058 brew_required=True 1059 RINGTONE_LIMITS= { 1060 'MAXSIZE': 250000 1061 } 1062 1063 WALLPAPER_WIDTH=160 1064 WALLPAPER_HEIGHT=120 1065 MAX_WALLPAPER_BASENAME_LENGTH=30 1066 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .-_" 1067 WALLPAPER_CONVERT_FORMAT="jpg" 1068 1069 MAX_RINGTONE_BASENAME_LENGTH=30 1070 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .-_" 1071 DIALSTRING_CHARS="[^0-9PT#*]" 1072 1073 # our targets are the same for all origins 1074 imagetargets={} 1075 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 1076 {'width': 160, 'height': 120, 'format': "JPEG"})) 1077 # delay auto-detection when the phone is plugged in (in seconds) 1078 autodetect_delay=3 1079 1080 def convertphonebooktophone(self, helper, data): 1081 """Converts the data to what will be used by the phone 1082 1083 @param data: contains the dict returned by getfundamentals 1084 as well as where the results go""" 1085 results={} 1086 1087 speeds={} 1088 1089 for pbentry in data['phonebook']: 1090 if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES: 1091 break 1092 e={} # entry out 1093 entry=data['phonebook'][pbentry] # entry in 1094 try: 1095 # serials, can't use 0 as default as the phone uses this for the first entry 1096 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', 0xFFFFFFFF) 1097 1098 e['serial1']=serial1 1099 for ss in entry["serials"]: 1100 if ss["sourcetype"]=="bitpim": 1101 e['bitpimserial']=ss 1102 assert e['bitpimserial'] 1103 1104 # name 1105 e['name']=helper.getfullname(entry.get('names', []),1,1,32)[0] 1106 1107 # categories/groups 1108 cat=helper.makeone(helper.getcategory(entry.get('categories', []),0,1,32), None) 1109 if cat is None: 1110 e['group']=0 1111 else: 1112 key,value=self._getgroup(cat, data['groups']) 1113 if key is not None: 1114 e['group']=key 1115 else: 1116 # sorry no space for this category 1117 e['group']=0 1118 1119 # email addresses 1120 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,72) 1121 e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "") 1122 1123 # url 1124 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,74), "") 1125 1126 # memo (-1 is to leave space for null terminator - not all software puts it in, but we do) 1127 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "") 1128 1129 # phone numbers 1130 # there must be at least one email address or phonenumber 1131 minnumbers=1 1132 if len(emails): minnumbers=0 1133 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS) 1134 e['numberspeeds']=[] 1135 e['numbertypes']=[] 1136 e['numbers']=[] 1137 for numindex in range(len(numbers)): 1138 num=numbers[numindex] 1139 # deal with type 1140 b4=len(e['numbertypes']) 1141 type=num['type'] 1142 for i,t in enumerate(self.protocolclass.numbertypetab): 1143 if type==t: 1144 e['numbertypes'].append(i) 1145 break 1146 if t=='none': # conveniently last entry 1147 e['numbertypes'].append(i) 1148 break 1149 if len(e['numbertypes'])==b4: 1150 # we couldn't find a type for the number 1151 helper.add_error_message("%s has number %s of type %s, the phone does not support this type" % 1152 (e['name'], num['number'], num['type'])) 1153 continue 1154 # deal with number 1155 number=self.phonize(num['number']) 1156 if len(number)==0: 1157 # no actual digits in the number 1158 continue 1159 if len(number)>48: # get this number from somewhere sensible 1160 # ::TODO:: number is too long and we have to either truncate it or ignore it? 1161 number=number[:48] # truncate for moment 1162 e['numbers'].append(number) 1163 # deal with speed dial 1164 sd=num.get("speeddial", 0xFF) 1165 if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL: 1166 e['numberspeeds'].append(sd) 1167 else: 1168 e['numberspeeds'].append(0xFF) 1169 1170 e['numberspeeds']=helper.filllist(e['numberspeeds'], 5, 0xFF) 1171 e['numbertypes']=helper.filllist(e['numbertypes'], 5, 0) 1172 e['numbers']=helper.filllist(e['numbers'], 5, "") 1173 1174 # ringtones, wallpaper 1175 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) 1176 # e['msgringtone']=helper.getringtone(entry.get('ringtones', []), 'message', None) 1177 e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None) 1178 1179 # flags 1180 e['secret']=helper.getflag(entry.get('flags',[]), 'secret', False) 1181 results[pbentry]=e 1182 1183 except helper.ConversionFailed: 1184 continue 1185 1186 data['phonebook']=results 1187 return data 1188 1189 _supportedsyncs=( 1190 ('phonebook', 'read', None), # all phonebook reading 1191 ('calendar', 'read', None), # all calendar reading 1192 ('wallpaper', 'read', None), # all wallpaper reading 1193 ('ringtone', 'read', None), # all ringtone reading 1194 ('call_history', 'read', None),# all call history list reading 1195 ('memo', 'read', None), # all memo list reading 1196 ('sms', 'read', None), # all SMS list reading 1197 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 1198 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 1199 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 1200 ('wallpaper', 'write', 'OVERWRITE'), 1201 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 1202 ('ringtone', 'write', 'OVERWRITE'), 1203 ('memo', 'write', 'OVERWRITE'), # all memo list writing 1204 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 1205 ) 1206 1207 def QueryAudio(self, origin, currentextension, afi): 1208 # we don't modify any of these 1209 if afi.format in ("MIDI", "QCP"): 1210 return currentextension, afi 1211 # examine mp3 1212 if afi.format=="MP3": 1213 if afi.channels==1 and 8<=afi.bitrate<=128 and 16000<=afi.samplerate<=44100: 1214 return currentextension, afi 1215 # convert it 1216 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 32, 'samplerate': 22050})) 1217 1218 1219 field_color_data={ 1220 'phonebook': { 1221 'name': { 1222 'first': 0, 'middle': 0, 'last': 0, 'full': 1, 1223 'nickname': 0, 'details': 1 }, 1224 'number': { 1225 'type': 5, 'speeddial': 5, 'number': 5, 'details': 5 }, 1226 'email': 3, 1227 'address': { 1228 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 1229 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 1230 'details': 0 }, 1231 'url': 1, 1232 'memo': 1, 1233 'category': 1, 1234 'wallpaper': 1, 1235 'ringtone': 1, 1236 'storage': 0, 1237 }, 1238 'calendar': { 1239 'description': True, 'location': False, 'allday': True, 1240 'start': True, 'end': True, 'priority': False, 1241 'alarm': True, 'vibrate': False, 1242 'repeat': True, 1243 'memo': False, 1244 'category': False, 1245 'wallpaper': False, 1246 'ringtone': True, 1247 }, 1248 'memo': { 1249 'subject': True, 1250 'date': False, 1251 'secret': False, 1252 'category': False, 1253 'memo': True, 1254 }, 1255 'todo': { 1256 'summary': False, 1257 'status': False, 1258 'due_date': False, 1259 'percent_complete': False, 1260 'completion_date': False, 1261 'private': False, 1262 'priority': False, 1263 'category': False, 1264 'memo': False, 1265 }, 1266 } 1267
Generated by PyXR 0.9.4