0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2005 Andrew Zitnay <drew@zitnay.com> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: com_lgvi125.py 4365 2007-08-17 21:11:59Z djpham $ 0009 0010 """Communicate with the LG VI125 cell phone""" 0011 0012 # standard modules 0013 import datetime 0014 import re 0015 import time 0016 import cStringIO 0017 import sha 0018 0019 # my modules 0020 import bpcalendar 0021 import common 0022 import commport 0023 import copy 0024 import p_lgvi125 0025 import p_brew 0026 import com_brew 0027 import com_phone 0028 import com_lg 0029 import prototypes 0030 import fileinfo 0031 import call_history 0032 import sms 0033 0034 class Phone(com_phone.Phone,com_brew.BrewProtocol,com_lg.LGPhonebook,com_lg.LGIndexedMedia): 0035 "Talk to the LG VI125 cell phone" 0036 0037 desc="LG-VI125" 0038 wallpaperindexfilename="dloadindex/brewImageIndex.map" 0039 ringerindexfilename="dloadindex/brewRingerIndex.map" 0040 protocolclass=p_lgvi125 0041 serialsname='lgvi125' 0042 0043 imagelocations=( 0044 # offset, index file, files location, type, maximumentries 0045 ( 10, "dloadindex/brewImageIndex.map", "brew/shared", "images", 30), 0046 ) 0047 0048 ringtonelocations=( 0049 # offset, index file, files location, type, maximumentries 0050 ( 50, "dloadindex/brewRingerIndex.map", "user/sound/ringer", "ringers", 30), 0051 ) 0052 0053 builtinimages=('Balloons', 'Soccer', 'Basketball', 'Bird', 0054 'Sunflower', 'Puppy', 'Mountain House', 'Beach') 0055 0056 builtinringtones=( 'Ring 1', 'Ring 2', 'Ring 3', 'Ring 4', 'Ring 5', 0057 'Ring 6', 'Alert 1', 'Alert 2', 'Alert 3', 'Alert 4', 'Alert 5', 0058 'Alert 6', 'Moon light', 'Bumble Bee', 'Latin', 'Baroque', 0059 'Lovable baby', 'LG sound', 'Love Song', 'Badinerie', 'Follow Me', 0060 'Head & Shoulder', 'Lake Trance', 'Beethovan', 'Lets Play', 0061 'Piano Concerto No.1', 'Pleasure', 'Leichte Kavallerie', 0062 'Up & Down Melody', 'Vivaldi - Winter' ) 0063 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 com_lg.LGIndexedMedia.__init__(self) 0071 self.log("Attempting to contact phone") 0072 self.mode=self.MODENONE 0073 self._cal_has_voice_id=hasattr(self.protocolclass, 'cal_has_voice_id') \ 0074 and self.protocolclass.cal_has_voice_id 0075 0076 def getfundamentals(self, results): 0077 """Gets information fundamental to interopating with the phone and UI. 0078 0079 Currently this is: 0080 0081 - 'uniqueserial' a unique serial number representing the phone 0082 - 'groups' the phonebook groups 0083 - 'wallpaper-index' map index numbers to names 0084 - 'ringtone-index' map index numbers to ringtone names 0085 0086 This method is called before we read the phonebook data or before we 0087 write phonebook data. 0088 """ 0089 0090 # use a hash of ESN and other stuff (being paranoid) 0091 self.log("Retrieving fundamental phone information") 0092 self.log("Phone serial number") 0093 results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() 0094 # now read groups 0095 self.log("Reading group information") 0096 buf=prototypes.buffer(self.getfilecontents("pim/pbookgroup.dat")) 0097 g=self.protocolclass.pbgroups() 0098 g.readfrombuffer(buf) 0099 self.logdata("Groups read", buf.getdata(), g) 0100 groups={} 0101 for i in range(len(g.groups)): 0102 if len(g.groups[i].name) and g.groups[i].number!=255: # sometimes have zero length names 0103 groups[g.groups[i].number]={ 'ring': g.groups[i].ring, '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 savesms(self, result, merge): 0111 self._setquicktext(result) 0112 result['rebootphone']=True 0113 return result 0114 0115 def _setquicktext(self, result): 0116 sf=self.protocolclass.sms_quick_text() 0117 quicktext=result.get('canned_msg', []) 0118 count=0 0119 for entry in quicktext: 0120 if count < self.protocolclass.SMS_CANNED_MAX_ITEMS: 0121 sf.msgs.append(entry['text'][:self.protocolclass.SMS_CANNED_MAX_LENGTH-1]) 0122 count+=1 0123 else: 0124 break 0125 if count!=0: 0126 # don't create the file if there are no entries 0127 buf=prototypes.buffer() 0128 sf.writetobuffer(buf) 0129 self.logdata("Writing calendar", buf.getvalue(), sf) 0130 self.writefile(self.protocolclass.SMS_CANNED_FILENAME, buf.getvalue()) 0131 return 0132 0133 def getsms(self, result): 0134 # get the quicktext (LG name for canned messages) 0135 result['canned_msg']=self._getquicktext() 0136 result['sms']=self._readsms() 0137 return result 0138 0139 def _readsms(self): 0140 res={} 0141 # go through the sms directory looking for messages 0142 for item in self.getfilesystem("sms").values(): 0143 if item['type']=='file': 0144 folder=None 0145 for f,pat in self.protocolclass.SMS_PATTERNS.items(): 0146 if pat.match(item['name']): 0147 folder=f 0148 break 0149 if folder: 0150 buf=prototypes.buffer(self.getfilecontents(item['name'], True)) 0151 self.logdata("SMS message file " +item['name'], buf.getdata()) 0152 if folder=='Inbox': 0153 sf=self.protocolclass.sms_in() 0154 sf.readfrombuffer(buf) 0155 entry=self._getinboxmessage(sf) 0156 res[entry.id]=entry 0157 elif folder=='Sent': 0158 sf=self.protocolclass.sms_out() 0159 sf.readfrombuffer(buf) 0160 entry=self._getoutboxmessage(sf) 0161 res[entry.id]=entry 0162 elif folder=='Saved': 0163 sf=self.protocolclass.sms_saved() 0164 sf.readfrombuffer(buf) 0165 if sf.outboxmsg: 0166 entry=self._getoutboxmessage(sf.outbox) 0167 else: 0168 entry=self._getinboxmessage(sf.inbox) 0169 entry.folder=entry.Folder_Saved 0170 res[entry.id]=entry 0171 return res 0172 0173 def _getquicktext(self): 0174 quicks=[] 0175 try: 0176 buf=prototypes.buffer(self.getfilecontents("sms/mediacan000.dat")) 0177 sf=self.protocolclass.sms_quick_text() 0178 sf.readfrombuffer(buf) 0179 self.logdata("SMS quicktext file sms/mediacan000.dat", buf.getdata(), sf) 0180 for rec in sf.msgs: 0181 if rec.msg!="": 0182 quicks.append({ 'text': rec.msg, 'type': sms.CannedMsgEntry.user_type }) 0183 except com_brew.BrewNoSuchFileException: 0184 pass # do nothing if file doesn't exist 0185 return quicks 0186 0187 def _getinboxmessage(self, sf): 0188 entry=sms.SMSEntry() 0189 entry.folder=entry.Folder_Inbox 0190 entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime) 0191 entry._from=self._getsender(sf.sender, sf.sender_length) 0192 entry.subject=sf.subject 0193 entry.locked=sf.locked 0194 if sf.priority==0: 0195 entry.priority=sms.SMSEntry.Priority_Normal 0196 else: 0197 entry.priority=sms.SMSEntry.Priority_High 0198 entry.read=sf.read 0199 txt="" 0200 if sf.num_msg_elements==1 and sf.bin_header1==0: 0201 txt=self._get_text_from_sms_msg_without_header(sf.msgs[0].msg, sf.msglengths[0].msglength) 0202 else: 0203 for i in range(sf.num_msg_elements): 0204 txt+=self._get_text_from_sms_msg_with_header(sf.msgs[i].msg, sf.msglengths[i].msglength) 0205 entry.text=unicode(txt, errors='ignore') 0206 entry.callback=sf.callback 0207 return entry 0208 0209 def _getoutboxmessage(self, sf): 0210 entry=sms.SMSEntry() 0211 entry.folder=entry.Folder_Sent 0212 entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent)) 0213 # add all the recipients 0214 for r in sf.recipients: 0215 if r.number: 0216 confirmed=(r.status==5) 0217 confirmed_date=None 0218 if confirmed: 0219 confirmed_date="%d%02d%02dT%02d%02d00" % r.timereceived 0220 entry.add_recipient(r.number, confirmed, confirmed_date) 0221 entry.subject=sf.subject 0222 txt="" 0223 if sf.num_msg_elements==1 and not sf.messages[0].binary: 0224 txt=self._get_text_from_sms_msg_without_header(sf.messages[0].msg, sf.messages[0].length) 0225 else: 0226 for i in range(sf.num_msg_elements): 0227 txt+=self._get_text_from_sms_msg_with_header(sf.messages[i].msg, sf.messages[i].length) 0228 entry.text=unicode(txt, errors='ignore') 0229 if sf.priority==0: 0230 entry.priority=sms.SMSEntry.Priority_Normal 0231 else: 0232 entry.priority=sms.SMSEntry.Priority_High 0233 entry.locked=sf.locked 0234 entry.callback=sf.callback 0235 return entry 0236 0237 def _get_text_from_sms_msg_without_header(self, msg, num_septets): 0238 out="" 0239 for i in range(num_septets): 0240 tmp = (msg[(i*7)/8].byte<<8) | msg[((i*7)/8) + 1].byte 0241 bit_index = 9 - ((i*7) % 8) 0242 out += chr((tmp >> bit_index) & 0x7f) 0243 return out 0244 0245 def _get_text_from_sms_msg_with_header(self, msg, num_septets): 0246 data_len = ((msg[0].byte+1)*8+6)/7 0247 seven_bits={} 0248 raw={} 0249 out={} 0250 # re-order the text into the correct order for separating into 0251 # 7-bit characters 0252 for i in range(0, (num_septets*7)/8+8, 7): 0253 for k in range(7): 0254 raw[i+6-k]=msg[i+k].byte 0255 # extract the 7-bit chars 0256 for i in range(num_septets+7): 0257 tmp = (raw[(i*7)/8]<<8) | raw[((i*7)/8) + 1] 0258 bit_index = 9 - ((i*7) % 8) 0259 seven_bits[i] = (tmp >> bit_index) & 0x7f 0260 # correct the byte order and remove the data portion of the message 0261 i=0 0262 for i in range(0, num_septets+7, 8): 0263 for k in range(8): 0264 if(i+7-k-data_len>=0): 0265 if i+k<num_septets+7: 0266 out[i+7-k-data_len]=seven_bits[i+k] 0267 res="" 0268 for i in range(num_septets-data_len): 0269 res+=chr(out[i]) 0270 return res 0271 0272 def _getsender(self, raw, len): 0273 result="" 0274 for i in range(len): 0275 if(raw[i].byte==10): 0276 result+="0" 0277 else: 0278 result+="%d" % raw[i].byte 0279 return result 0280 0281 def getcallhistory(self, result): 0282 res={} 0283 # read the incoming call history file 0284 self._readhistoryfile("pim/missed_log.dat", 'Missed', res) 0285 self._readhistoryfile("pim/outgoing_log.dat", 'Outgoing', res) 0286 self._readhistoryfile("pim/incoming_log.dat", 'Incoming', res) 0287 result['call_history']=res 0288 return result 0289 0290 def _readhistoryfile(self, fname, folder, res): 0291 try: 0292 buf=prototypes.buffer(self.getfilecontents(fname)) 0293 ch=self.protocolclass.callhistory() 0294 ch.readfrombuffer(buf) 0295 self.logdata("Call History", buf.getdata(), ch) 0296 for call in ch.calls: 0297 if call.number=='' and call.name=='': 0298 continue 0299 entry=call_history.CallHistoryEntry() 0300 entry.folder=folder 0301 if call.duration: 0302 entry.duration=call.duration 0303 entry.datetime=((call.GPStime)) 0304 if call.number=='': # restricted calls have no number 0305 entry.number=call.name 0306 else: 0307 entry.number=call.number 0308 res[entry.id]=entry 0309 except com_brew.BrewNoSuchFileException: 0310 pass # do nothing if file doesn't exist 0311 return 0312 0313 def getwallpaperindices(self, results): 0314 return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index') 0315 0316 def getringtoneindices(self, results): 0317 return self.getmediaindex(self.builtinringtones, self.ringtonelocations, results, 'ringtone-index') 0318 0319 def getphonebook(self,result): 0320 """Reads the phonebook data. The L{getfundamentals} information will 0321 already be in result.""" 0322 0323 pbook={} 0324 # Bug in the phone. if you repeatedly read the phone book it starts 0325 # returning a random number as the number of entries. We get around 0326 # this by switching into brew mode which clears that. 0327 self.mode=self.MODENONE 0328 self.setmode(self.MODEBREW) 0329 self.log("Reading number of phonebook entries") 0330 req=self.protocolclass.pbinforequest() 0331 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) 0332 numentries=res.numentries 0333 if numentries<0 or numentries>1000: 0334 self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way") 0335 numentries=0 0336 firstserial=None 0337 loop=xrange(0,1000) 0338 hardway=True 0339 else: 0340 self.log("There are %d entries" % (numentries,)) 0341 loop=xrange(0, numentries) 0342 hardway=False 0343 # reset cursor 0344 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) 0345 problemsdetected=False 0346 dupecheck={} 0347 for i in loop: 0348 if hardway: 0349 numentries+=1 0350 req=self.protocolclass.pbreadentryrequest() 0351 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 0352 self.log("Read entry "+`i`+" - "+res.entry.name) 0353 entry=self.extractphonebookentry(res.entry, result) 0354 if hardway and firstserial is None: 0355 firstserial=res.entry.serial1 0356 pbook[i]=entry 0357 if res.entry.serial1 in dupecheck: 0358 self.log("Entry %s has same serial as entry %s. This will cause problems." % (`entry`, dupecheck[res.entry.serial1])) 0359 problemsdetected=True 0360 else: 0361 dupecheck[res.entry.serial1]=entry 0362 self.progress(i, numentries, res.entry.name) 0363 #### Advance to next entry 0364 req=self.protocolclass.pbnextentryrequest() 0365 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 0366 if hardway: 0367 # look to see if we have looped 0368 if res.serial==firstserial or res.serial==0: 0369 break 0370 0371 self.progress(numentries, numentries, "Phone book read completed") 0372 0373 if problemsdetected: 0374 self.log("There are duplicate serial numbers. See above for details.") 0375 raise common.IntegrityCheckFailed(self.desc, "Data in phonebook is inconsistent. There are multiple entries with the same serial number. See the log.") 0376 0377 result['phonebook']=pbook 0378 cats=[] 0379 for i in result['groups']: 0380 if result['groups'][i]['name']!='No Group': 0381 cats.append(result['groups'][i]['name']) 0382 result['categories']=cats 0383 print "returning keys",result.keys() 0384 return pbook 0385 0386 def savegroups(self, data): 0387 groups=data['groups'] 0388 keys=groups.keys() 0389 keys.sort() 0390 0391 g=self.protocolclass.pbgroups() 0392 sg=self.protocolclass.pbseqgroups() 0393 for k in keys: 0394 e=self.protocolclass.pbgroup() 0395 e.number=k 0396 e.unknown1=48 0397 e.ring=groups[k]['ring'] 0398 e.unknown7=0 0399 e.unknown8=0 0400 e.name=groups[k]['name'] 0401 g.groups.append(e) 0402 0403 e=self.protocolclass.pbseqgroup() 0404 e.number=k 0405 e.unknown=48 0406 sg.seqgroups.append(e) 0407 0408 for k in xrange(k+1,20): 0409 e=self.protocolclass.pbseqgroup() 0410 e.number=0 0411 e.unknown=0 0412 sg.seqgroups.append(e) 0413 0414 groupnums=[] 0415 for k in range(self.protocolclass.NUMMAPGROUPS): 0416 groupnums.append(-1); 0417 0418 for k in data['phonebook'].keys(): 0419 groupnums[data['phonebook'][k]['serial1']]=data['phonebook'][k]['group'] 0420 0421 mg=self.protocolclass.pbmapgroups() 0422 for k in range(len(groupnums)): 0423 e=self.protocolclass.pbmapgroup() 0424 if (groupnums[k]==-1): 0425 e.number=255 0426 e.unknown=255 0427 else: 0428 e.number=groupnums[k] 0429 e.unknown=48 0430 mg.mapgroups.append(e) 0431 0432 buffer=prototypes.buffer() 0433 g.writetobuffer(buffer) 0434 self.logdata("New group file", buffer.getvalue(), g) 0435 self.writefile("pim/pbookgroup.dat", buffer.getvalue()) 0436 0437 buffer=prototypes.buffer() 0438 sg.writetobuffer(buffer) 0439 self.logdata("New seqgroup file", buffer.getvalue(), sg) 0440 self.writefile("pim/pbookseqgroup.dat", buffer.getvalue()) 0441 0442 buffer=prototypes.buffer() 0443 mg.writetobuffer(buffer) 0444 self.logdata("New mapgroup file", buffer.getvalue(), mg) 0445 self.writefile("pim/pbookmapgroup.dat", buffer.getvalue()) 0446 0447 def savephonebook(self, data): 0448 "Saves out the phonebook" 0449 self.savegroups(data) 0450 0451 progressmax=len(data['phonebook'].keys()) 0452 0453 # To write the phone book, we scan through all existing entries 0454 # and record their record number and serial. 0455 # We then delete any entries that aren't in data 0456 # We then write out our records, using overwrite or append 0457 # commands as necessary 0458 serialupdates=[] 0459 existingpbook={} # keep track of the phonebook that is on the phone 0460 self.mode=self.MODENONE 0461 self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary 0462 self.setmode(self.MODEPHONEBOOK) 0463 # similar loop to reading 0464 req=self.protocolclass.pbinforequest() 0465 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) 0466 numexistingentries=res.numentries 0467 if numexistingentries<0 or numexistingentries>1000: 0468 self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way") 0469 numexistingentries=0 0470 firstserial=None 0471 loop=xrange(0,1000) 0472 hardway=True 0473 else: 0474 self.log("There are %d existing entries" % (numexistingentries,)) 0475 progressmax+=numexistingentries 0476 loop=xrange(0, numexistingentries) 0477 hardway=False 0478 progresscur=0 0479 # reset cursor 0480 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) 0481 for i in loop: 0482 ### Read current entry 0483 if hardway: 0484 numexistingentries+=1 0485 progressmax+=1 0486 req=self.protocolclass.pbreadentryrequest() 0487 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 0488 0489 entry={ 'number': res.entry.serial1, 'serial1': res.entry.serial1, 0490 'serial2': res.entry.serial2, 'name': res.entry.name} 0491 assert entry['serial1']==entry['serial2'] # always the same 0492 self.log("Reading entry "+`i`+" - "+entry['name']) 0493 if hardway and firstserial is None: 0494 firstserial=res.entry.serial1 0495 existingpbook[i]=entry 0496 self.progress(progresscur, progressmax, "existing "+entry['name']) 0497 #### Advance to next entry 0498 req=self.protocolclass.pbnextentryrequest() 0499 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 0500 progresscur+=1 0501 if hardway: 0502 # look to see if we have looped 0503 if res.serial==firstserial or res.serial==0: 0504 break 0505 # we have now looped around back to begining 0506 0507 # Find entries that have been deleted 0508 pbook=data['phonebook'] 0509 dellist=[] 0510 for i in range(0, numexistingentries): 0511 ii=existingpbook[i] 0512 serial=ii['serial1'] 0513 item=self._findserial(serial, pbook) 0514 if item is None: 0515 dellist.append(i) 0516 0517 progressmax+=len(dellist) # more work to do 0518 0519 # Delete those entries 0520 for i in dellist: 0521 progresscur+=1 0522 numexistingentries-=1 # keep count right 0523 ii=existingpbook[i] 0524 self.log("Deleting entry "+`i`+" - "+ii['name']) 0525 req=self.protocolclass.pbdeleteentryrequest() 0526 req.entrynumber=ii['serial1'] 0527 req.serial1=ii['serial1'] 0528 req.serial2=ii['serial2'] 0529 self.sendpbcommand(req, self.protocolclass.pbdeleteentryresponse) 0530 self.progress(progresscur, progressmax, "Deleting "+ii['name']) 0531 # also remove them from existingpbook 0532 del existingpbook[i] 0533 0534 # counter to keep track of record number (otherwise appends don't work) 0535 counter=0 0536 # Now rewrite out existing entries 0537 keys=existingpbook.keys() 0538 existingserials=[] 0539 keys.sort() # do in same order as existingpbook 0540 for i in keys: 0541 progresscur+=1 0542 ii=pbook[self._findserial(existingpbook[i]['serial1'], pbook)] 0543 self.log("Rewriting entry "+`i`+" - "+ii['name']) 0544 self.progress(progresscur, progressmax, "Rewriting "+ii['name']) 0545 entry=self.makeentry(counter, ii, data) 0546 counter+=1 0547 existingserials.append(existingpbook[i]['serial1']) 0548 req=self.protocolclass.pbupdateentryrequest() 0549 req.entry=entry 0550 res=self.sendpbcommand(req, self.protocolclass.pbupdateentryresponse) 0551 serialupdates.append( ( ii["bitpimserial"], 0552 {'sourcetype': self.serialsname, 'serial1': res.serial1, 'serial2': res.serial1, 0553 'sourceuniqueid': data['uniqueserial']}) 0554 ) 0555 assert ii['serial1']==res.serial1 # serial should stay the same 0556 0557 # Finally write out new entries 0558 keys=pbook.keys() 0559 keys.sort() 0560 for i in keys: 0561 ii=pbook[i] 0562 print "looking for "+`ii['serial1']`+" "+`ii['name']` 0563 if ii['serial1'] in existingserials: 0564 continue # already wrote this one out 0565 print "found new" 0566 progresscur+=1 0567 entry=self.makeentry(counter, ii, data) 0568 counter+=1 0569 self.log("Appending entry "+ii['name']) 0570 self.progress(progresscur, progressmax, "Writing "+ii['name']) 0571 req=self.protocolclass.pbappendentryrequest() 0572 req.entry=entry 0573 res=self.sendpbcommand(req, self.protocolclass.pbappendentryresponse) 0574 serialupdates.append( ( ii["bitpimserial"], 0575 {'sourcetype': self.serialsname, 'serial1': res.newserial, 'serial2': res.newserial, 0576 'sourceuniqueid': data['uniqueserial']}) 0577 ) 0578 data["serialupdates"]=serialupdates 0579 0580 return data 0581 0582 0583 def _findserial(self, serial, dict): 0584 """Searches dict to find entry with matching serial. If not found, 0585 returns None""" 0586 for i in dict: 0587 if dict[i]['serial1']==serial: 0588 return i 0589 return None 0590 0591 def _normaliseindices(self, d): 0592 "turn all negative keys into positive ones for index" 0593 res={} 0594 keys=d.keys() 0595 keys.sort() 0596 keys.reverse() 0597 for k in keys: 0598 if k<0: 0599 for c in range(999999): 0600 if c not in keys and c not in res: 0601 break 0602 res[c]=d[k] 0603 else: 0604 res[k]=d[k] 0605 return res 0606 0607 def extractphonebookentry(self, entry, fundamentals): 0608 """Return a phonebook entry in BitPim format. This is called from getphonebook.""" 0609 res={} 0610 # serials 0611 res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.serial1, 'serial2': entry.serial2, 0612 'sourceuniqueid': fundamentals['uniqueserial']} ] 0613 # only one name 0614 res['names']=[ {'full': entry.name} ] 0615 # only one category 0616 cat=fundamentals['groups'].get(entry.group, {'name': "No Group"})['name'] 0617 if cat!="No Group": 0618 res['categories']=[ {'category': cat} ] 0619 # emails 0620 res['emails']=[] 0621 for i in entry.emails: 0622 if len(i.email): 0623 res['emails'].append( {'email': i.email} ) 0624 if not len(res['emails']): del res['emails'] # it was empty 0625 # urls 0626 if 'url' in entry.getfields() and len(entry.url): 0627 res['urls']=[ {'url': entry.url} ] 0628 # memos 0629 if 'memo' in entry.getfields() and len(entry.memo): 0630 res['memos']=[ {'memo': entry.memo } ] 0631 0632 # ringtones 0633 res['ringtones']=[] 0634 if 'ringtone' in entry.getfields() and entry.ringtone!=self.protocolclass.NORINGTONE: 0635 try: 0636 tone=fundamentals['ringtone-index'][entry.ringtone]['name'] 0637 res['ringtones'].append({'ringtone': tone, 'use': 'call'}) 0638 except: 0639 print "can't find ringtone for index",entry.ringtone 0640 if len(res['ringtones'])==0: 0641 del res['ringtones'] 0642 res=self._assignpbtypeandspeeddials(entry, res) 0643 return res 0644 0645 def _assignpbtypeandspeeddials(self, entry, res): 0646 # numbers 0647 res['numbers']=[] 0648 for i in range(self.protocolclass.NUMPHONENUMBERS): 0649 num=entry.numbers[i].number 0650 type=entry.numbertypes[i].numbertype 0651 speeddial=entry.numberspeeddials[i].numberspeeddial 0652 if len(num): 0653 t=self.protocolclass.numbertypetab[type] 0654 if t[-1]=='2': 0655 t=t[:-1] 0656 if speeddial==255: 0657 res['numbers'].append({'number': num, 'type': t}) 0658 else: 0659 res['numbers'].append({'number': num, 'type': t, 'speeddial': speeddial}) 0660 return res 0661 0662 def _findmediainindex(self, index, name, pbentryname, type): 0663 if type=="ringtone": default=self.protocolclass.NORINGTONE 0664 elif type=="message ringtone": default=self.protocolclass.NOMSGRINGTONE 0665 elif type=="wallpaper": default=self.protocolclass.NOWALLPAPER 0666 else: 0667 assert False, "unknown type "+type 0668 0669 if name is None: 0670 return default 0671 for i in index: 0672 if index[i]['name']==name: 0673 return i 0674 self.log("%s: Unable to find %s %s in the index. Setting to default." % (pbentryname, type, name)) 0675 return default 0676 0677 def makeentry(self, counter, entry, data): 0678 """Creates pbentry object 0679 0680 @param counter: The new entry number 0681 @param entry: The phonebook object (as returned from convertphonebooktophone) that we 0682 are using as the source 0683 @param data: The main dictionary, which we use to get access to media indices amongst 0684 other things 0685 """ 0686 e=self.protocolclass.pbentry() 0687 e.avatar=0 0688 0689 for k in entry: 0690 # special treatment for lists 0691 0692 if k in ('emails', 'numbers', 'numbertypes', 'numberspeeddials'): 0693 l=getattr(e,k) 0694 for item in entry[k]: 0695 l.append(item) 0696 elif k=='ringtone': 0697 e.ringtone=self._findmediainindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone') 0698 elif k in e.getfields(): 0699 # everything else we just set 0700 setattr(e,k,entry[k]) 0701 0702 e.unknown7=0 0703 e.group=0 # couldn't figure out groups; this at least sets it to No Group 0704 e.unknown12=1 # couldn't figure out ringtone; this at least sets it to Default 0705 return e 0706 0707 def is_mode_brew(self): 0708 req=p_brew.memoryconfigrequest() 0709 respc=p_brew.memoryconfigresponse 0710 0711 for baud in 0, 38400, 115200: 0712 if baud: 0713 if not self.comm.setbaudrate(baud): 0714 continue 0715 try: 0716 self.sendbrewcommand(req, respc, callsetmode=False) 0717 return True 0718 except com_phone.modeignoreerrortypes: 0719 pass 0720 return False 0721 0722 brew_version_txt_key='ams_version.txt' 0723 brew_version_file='ams/version.txt' 0724 lgpbinfo_key='lgpbinfo' 0725 esn_file_key='esn_file' 0726 esn_file='nvm/$SYS.ESN' 0727 my_model='VI125' 0728 def get_detect_data(self, res): 0729 # get the data needed for detection 0730 if not res.has_key(self.brew_version_txt_key): 0731 # read the BREW version.txt file, which may contain phone model info 0732 print 'reading BREW version.txt' 0733 try: 0734 # read this file 0735 s=self.getfilecontents(self.brew_version_file) 0736 res[self.brew_version_txt_key]=s 0737 except com_brew.BrewNoSuchFileException: 0738 res[self.brew_version_txt_key]=None 0739 except: 0740 if __debug__: 0741 raise 0742 res[self.brew_version_txt_key]=None 0743 # get pbinfo data, which also may include phone model 0744 if not res.has_key(self.lgpbinfo_key): 0745 print 'getting pbinfo' 0746 try: 0747 req=self.protocolclass.pbinforequest() 0748 resp=self.sendpbcommand(req, self.protocolclass.pbstartsyncresponse) 0749 res[self.lgpbinfo_key]=resp.unknown 0750 except: 0751 if __debug__: 0752 raise 0753 res[self.lgpbinfo_key]=None 0754 # attempt the get the ESN 0755 if not res.has_key(self.esn_file_key): 0756 print 'reading ESN file: '+`self.esn_file`+' end' 0757 try: 0758 s=self.getfilecontents(self.esn_file) 0759 res[self.esn_file_key]=s 0760 except: 0761 res[self.esn_file_key]=None 0762 0763 def get_esn(self, data=None): 0764 # return the ESN for this phone 0765 try: 0766 if data is None: 0767 s=self.getfilecontents(self.esn_file) 0768 else: 0769 s=data 0770 if s: 0771 s=s[85:89] 0772 return '%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), 0773 ord(s[1]), ord(s[0])) 0774 except: 0775 if __debug__: 0776 raise 0777 0778 def eval_detect_data(self, res): 0779 found=False 0780 if res.get(self.brew_version_txt_key, None) is not None: 0781 found=res[self.brew_version_txt_key][:len(self.my_model)]==self.my_model 0782 if not found and res.get(self.lgpbinfo_key, None): 0783 found=res[self.lgpbinfo_key].find(self.my_model)!=-1 0784 if found: 0785 res['model']=self.my_model 0786 res['manufacturer']='LG Electronics Inc' 0787 s=res.get(self.esn_file_key, None) 0788 if s: 0789 res['esn']=self.get_esn(s) 0790 0791 @classmethod 0792 def detectphone(_, coms, likely_ports, res, _module, _log): 0793 if not likely_ports: 0794 # cannot detect any likely ports 0795 return None 0796 for port in likely_ports: 0797 if not res.has_key(port): 0798 res[port]={ 'mode_modem': None, 'mode_brew': None, 0799 'manufacturer': None, 'model': None, 0800 'firmware_version': None, 'esn': None, 0801 'firmwareresponse': None } 0802 try: 0803 if res[port]['mode_brew']==False or \ 0804 res[port]['model']: 0805 # either phone is not in BREW, or a model has already 0806 # been found, not much we can do now 0807 continue 0808 p=_module.Phone(_log, commport.CommConnection(_log, port, timeout=1)) 0809 if res[port]['mode_brew'] is None: 0810 res[port]['mode_brew']=p.is_mode_brew() 0811 if res[port]['mode_brew']: 0812 p.get_detect_data(res[port]) 0813 p.eval_detect_data(res[port]) 0814 p.comm.close() 0815 except: 0816 if __debug__: 0817 raise 0818 0819 # Calendar stuff------------------------------------------------------------ 0820 def getcalendar(self,result): 0821 # Read exceptions file first 0822 try: 0823 buf=prototypes.buffer(self.getfilecontents( 0824 self.protocolclass.cal_exception_file_name)) 0825 ex=self.protocolclass.scheduleexceptionfile() 0826 ex.readfrombuffer(buf) 0827 self.logdata("Calendar exceptions", buf.getdata(), ex) 0828 exceptions={} 0829 for i in ex.items: 0830 exceptions.setdefault(i.pos, []).append( (i.year,i.month,i.day) ) 0831 except com_brew.BrewNoSuchFileException: 0832 exceptions={} 0833 0834 # Now read schedule 0835 try: 0836 buf=prototypes.buffer(self.getfilecontents( 0837 self.protocolclass.cal_data_file_name)) 0838 if len(buf.getdata())<2: 0839 # file is empty, and hence same as non-existent 0840 raise com_brew.BrewNoSuchFileException() 0841 sc=self.protocolclass.schedulefile() 0842 self.logdata("Calendar", buf.getdata(), sc) 0843 sc.readfrombuffer(buf) 0844 sc.readfrombuffer(buf) 0845 res=self.get_cal(sc, exceptions, result.get('ringtone-index', {})) 0846 except com_brew.BrewNoSuchFileException: 0847 res={} 0848 result['calendar']=res 0849 return result 0850 0851 def savecalendar(self, dict, merge): 0852 # ::TODO:: obey merge param 0853 # get the list of available voice alarm files 0854 voice_files={} 0855 if self._cal_has_voice_id: 0856 try: 0857 file_list=self.getfilesystem(self.protocolclass.cal_dir) 0858 for k in file_list.keys(): 0859 if k.endswith(self.protocolclass.cal_voice_ext): 0860 voice_files[int(k[8:11])]=k 0861 except: 0862 self.log('Failed to list Calendar Voice Files') 0863 # build the schedule file 0864 sc=self.protocolclass.schedulefile() 0865 sc_ex=self.set_cal(sc, dict.get('calendar', {}), 0866 dict.get('ringtone-index', {}), 0867 voice_files) 0868 buf=prototypes.buffer() 0869 sc.writetobuffer(buf) 0870 self.writefile(self.protocolclass.cal_data_file_name, 0871 buf.getvalue()) 0872 # build the exceptions 0873 exceptions_file=self.protocolclass.scheduleexceptionfile() 0874 for k,l in sc_ex.items(): 0875 for x in l: 0876 _ex=self.protocolclass.scheduleexception() 0877 _ex.pos=k 0878 _ex.year, _ex.month, _ex.day=x 0879 exceptions_file.items.append(_ex) 0880 buf=prototypes.buffer() 0881 exceptions_file.writetobuffer(buf) 0882 self.writefile(self.protocolclass.cal_exception_file_name, 0883 buf.getvalue()) 0884 # clear out any alarm voice files that may have been deleted 0885 if self._cal_has_voice_id: 0886 for k,e in voice_files.items(): 0887 try: 0888 self.rmfile(e) 0889 except: 0890 self.log('Failed to delete file '+e) 0891 return dict 0892 0893 _repeat_values={ 0894 protocolclass.CAL_REP_DAILY: bpcalendar.RepeatEntry.daily, 0895 protocolclass.CAL_REP_MONFRI: bpcalendar.RepeatEntry.daily, 0896 protocolclass.CAL_REP_WEEKLY: bpcalendar.RepeatEntry.weekly, 0897 protocolclass.CAL_REP_MONTHLY: bpcalendar.RepeatEntry.monthly, 0898 protocolclass.CAL_REP_YEARLY: bpcalendar.RepeatEntry.yearly 0899 } 0900 0901 def _build_cal_repeat(self, event, exceptions): 0902 rep_val=Phone._repeat_values.get(event.repeat, None) 0903 if not rep_val: 0904 return None 0905 rep=bpcalendar.RepeatEntry(rep_val) 0906 if event.repeat==self.protocolclass.CAL_REP_MONFRI: 0907 rep.interval=rep.dow=0 0908 elif event.repeat!=self.protocolclass.CAL_REP_YEARLY: 0909 rep.interval=1 0910 rep.dow=0 0911 # do exceptions 0912 cal_ex=exceptions.get(event.pos, []) 0913 for e in cal_ex: 0914 rep.add_suppressed(*e) 0915 return rep 0916 0917 def _get_voice_id(self, event, entry): 0918 if event.hasvoice: 0919 entry.voice=event.voiceid 0920 0921 def _build_cal_entry(self, event, exceptions, ringtone_index): 0922 # return a copy of bpcalendar object based on my values 0923 # general fields 0924 entry=bpcalendar.CalendarEntry() 0925 entry.start=event.start 0926 entry.end=event.end 0927 entry.description=event.description 0928 # check for allday event 0929 if entry.start[3:]==(0, 0) and entry.end[3:]==(23, 59): 0930 entry.allday=True 0931 # alarm 0932 if event.alarmtype: 0933 entry.alarm=event.alarmhours*60+event.alarmminutes 0934 # ringtone 0935 rt_idx=event.ringtone 0936 # hack to account for the VX4650 weird ringtone setup 0937 if rt_idx<50: 0938 # 1st part of builtin ringtones, need offset by 1 0939 rt_idx+=1 0940 entry.ringtone=ringtone_index.get(rt_idx, {'name': None} )['name'] 0941 # voice ID if applicable 0942 if self._cal_has_voice_id: 0943 self._get_voice_id(event, entry) 0944 # repeat info 0945 entry.repeat=self._build_cal_repeat(event, exceptions) 0946 return entry 0947 0948 def get_cal(self, sch_file, exceptions, ringtone_index): 0949 res={} 0950 for event in sch_file.events: 0951 if event.pos==-1: # blank entry 0952 continue 0953 cal_event=self._build_cal_entry(event, exceptions, ringtone_index) 0954 res[cal_event.id]=cal_event 0955 return res 0956 0957 _alarm_info={ 0958 -1: (protocolclass.CAL_REMINDER_NONE, 100, 100), 0959 0: (protocolclass.CAL_REMINDER_ONTIME, 0, 0), 0960 5: (protocolclass.CAL_REMINDER_5MIN, 5, 0), 0961 10: (protocolclass.CAL_REMINDER_10MIN, 10, 0), 0962 60: (protocolclass.CAL_REMINDER_1HOUR, 0, 1), 0963 1440: (protocolclass.CAL_REMINDER_1DAY, 0, 24), 0964 2880: (protocolclass.CAL_REMINDER_2DAYS, 0, 48) } 0965 _default_alarm=(protocolclass.CAL_REMINDER_NONE, 100, 100) # default alarm is off 0966 _phone_dow={ 0967 1: protocolclass.CAL_DOW_SUN, 0968 2: protocolclass.CAL_DOW_MON, 0969 4: protocolclass.CAL_DOW_TUE, 0970 8: protocolclass.CAL_DOW_WED, 0971 16: protocolclass.CAL_DOW_THU, 0972 32: protocolclass.CAL_DOW_FRI, 0973 64: protocolclass.CAL_DOW_SAT 0974 } 0975 0976 def _set_repeat_event(self, event, entry, exceptions): 0977 rep_val=self.protocolclass.CAL_REP_NONE 0978 day_bitmap=0 0979 rep=entry.repeat 0980 if rep: 0981 rep_type=rep.repeat_type 0982 if rep_type==bpcalendar.RepeatEntry.yearly: 0983 rep_val=self.protocolclass.CAL_REP_YEARLY 0984 else: 0985 rep_interval=rep.interval 0986 rep_dow=rep.dow 0987 if rep_type==bpcalendar.RepeatEntry.daily: 0988 if rep_interval==0: 0989 rep_val=self.protocolclass.CAL_REP_MONFRI 0990 elif rep_interval==1: 0991 rep_val=self.protocolclass.CAL_REP_DAILY 0992 elif rep_type==bpcalendar.RepeatEntry.weekly: 0993 start_dow=1<<datetime.date(*event.start[:3]).isoweekday()%7 0994 if (rep_dow==0 or rep_dow==start_dow) and rep_interval==1: 0995 rep_val=self.protocolclass.CAL_REP_WEEKLY 0996 day_bitmap=self._phone_dow.get(start_dow, 0) 0997 elif rep_type==bpcalendar.RepeatEntry.monthly: 0998 if rep_dow==0: 0999 rep_val=self.protocolclass.CAL_REP_MONTHLY 1000 if rep_val!=self.protocolclass.CAL_REP_NONE: 1001 # build exception list 1002 if rep.suppressed: 1003 day_bitmap|=self.protocolclass.CAL_DOW_EXCEPTIONS 1004 for x in rep.suppressed: 1005 exceptions.setdefault(event.pos, []).append(x.get()[:3]) 1006 # this is a repeat event, set the end date appropriately 1007 if event.end[:3]==entry.no_end_date: 1008 event.end=self.protocolclass.CAL_REPEAT_DATE+event.end[3:] 1009 else: 1010 event.end=entry.end 1011 event.repeat=rep_val 1012 event.daybitmap=day_bitmap 1013 1014 def _set_alarm(self, event, entry): 1015 # set alarm value based on entry's value, or its approximation 1016 keys=Phone._alarm_info.keys() 1017 keys.sort() 1018 keys.reverse() 1019 _alarm_val=entry.alarm 1020 _alarm_key=None 1021 for k in keys: 1022 if _alarm_val>=k: 1023 _alarm_key=k 1024 break 1025 event.alarmtype, event.alarmminutes, event.alarmhours=Phone._alarm_info.get( 1026 _alarm_key, self._default_alarm) 1027 1028 def _set_voice_id(self, event, entry, voice_files): 1029 if entry.voice and \ 1030 voice_files.has_key(entry.voice-self.protocolclass.cal_voice_id_ofs): 1031 event.hasvoice=1 1032 event.voiceid=entry.voice 1033 del voice_files[entry.voice-self.protocolclass.cal_voice_id_ofs] 1034 else: 1035 event.hasvoice=0 1036 event.voiceid=self.protocolclass.CAL_NO_VOICE 1037 1038 def _set_cal_event(self, event, entry, exceptions, ringtone_index, 1039 voice_files): 1040 # desc 1041 event.description=entry.description 1042 # start & end times 1043 if entry.allday: 1044 event.start=entry.start[:3]+(0,0) 1045 event.end=entry.start[:3]+(23,59) 1046 else: 1047 event.start=entry.start 1048 event.end=entry.start[:3]+entry.end[3:] 1049 # make sure the event lasts in 1 calendar day 1050 if event.end<event.start: 1051 event.end=event.start[:3]+(23,59) 1052 # alarm 1053 self._set_alarm(event, entry) 1054 # ringtone 1055 rt=0 # always default to the first bultin ringtone 1056 if entry.ringtone: 1057 for k,e in ringtone_index.items(): 1058 if e['name']==entry.ringtone: 1059 rt=k 1060 break 1061 if rt and rt<50: 1062 rt-=1 1063 event.ringtone=rt 1064 # voice ID 1065 if self._cal_has_voice_id: 1066 self._set_voice_id(event, entry, voice_files) 1067 # repeat 1068 self._set_repeat_event(event, entry, exceptions) 1069 1070 def set_cal(self, sch_file, cal_dict, ringtone_index, voice_files): 1071 sch_file.numactiveitems=len(cal_dict) 1072 exceptions={} 1073 _pos=2 1074 _packet_size=None 1075 ## _today=datetime.date.today().timetuple()[:5] 1076 for k, e in cal_dict.items(): 1077 ## # only send either repeat events or present&future single events 1078 ## if e.repeat or (e.start>=_today): 1079 event=self.protocolclass.scheduleevent() 1080 event.pos=_pos 1081 self._set_cal_event(event, e, exceptions, ringtone_index, 1082 voice_files) 1083 sch_file.events.append(event) 1084 if not _packet_size: 1085 _packet_size=event.packetsize() 1086 _pos+=_packet_size 1087 return exceptions 1088 1089 def _get_phone_number(self): 1090 # return the phone number of this phone 1091 s='' 1092 try: 1093 buf=self.getfilecontents('nvm/nvm/nvm_0000') 1094 ofs=0xce 1095 if buf[ofs]=='\x01': 1096 ofs+=1 1097 while buf[ofs]!='\x01': 1098 s+=buf[ofs] 1099 ofs+=1 1100 except: 1101 if __debug__: 1102 raise 1103 return s 1104 def getfirmwareinformation(self): 1105 self.log("Getting firmware information") 1106 req=p_brew.firmwarerequest() 1107 res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 1108 return res 1109 def getphoneinfo(self, phone_info): 1110 # returning some basic phone info 1111 # double check if this's the right phone. 1112 try: 1113 if self.getfilecontents(self.brew_version_file)[:len(self.my_model)]==self.my_model: 1114 phone_info.model=self.my_model 1115 phone_info.manufacturer=Profile.phone_manufacturer 1116 phone_info.phone_number=self._get_phone_number() 1117 phone_info.firmware_version=self.getfirmwareinformation().firmwareversion 1118 phone_info.esn=self.get_esn() 1119 except: 1120 if __debug__: 1121 raise 1122 1123 def phonize(str): 1124 """Convert the phone number into something the phone understands 1125 1126 All digits, P, T, * and # are kept, everything else is removed""" 1127 return re.sub("[^0-9PT#*]", "", str) 1128 1129 parentprofile=com_phone.Profile 1130 class Profile(parentprofile): 1131 protocolclass=Phone.protocolclass 1132 serialsname=Phone.serialsname 1133 BP_Calendar_Version=3 1134 phone_manufacturer='LG Electronics Inc' 1135 phone_model='VI125' 1136 1137 WALLPAPER_WIDTH=120 1138 WALLPAPER_HEIGHT=98 1139 MAX_WALLPAPER_BASENAME_LENGTH=19 1140 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ." 1141 WALLPAPER_CONVERT_FORMAT="bmp" 1142 1143 MAX_RINGTONE_BASENAME_LENGTH=19 1144 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789 ." 1145 1146 # which usb ids correspond to us 1147 usbids_straight=( ( 0x1004, 0x6000, 2), )# VID=LG Electronics, PID=LG VI125 -internal USB diagnostics interface 1148 usbids_usbtoserial=( 1149 ( 0x067b, 0x2303, None), # VID=Prolific, PID=USB to serial 1150 ( 0x0403, 0x6001, None), # VID=FTDI, PID=USB to serial 1151 ( 0x0731, 0x2003, None), # VID=Susteen, PID=Universal USB to serial 1152 ) 1153 usbids=usbids_straight+usbids_usbtoserial 1154 1155 # which device classes we are. not we are not modem! 1156 deviceclasses=("serial",) 1157 1158 # nb we don't allow save to camera so it isn't listed here 1159 imageorigins={} 1160 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 1161 def GetImageOrigins(self): 1162 return self.imageorigins 1163 1164 # our targets are the same for all origins 1165 imagetargets={} 1166 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 1167 {'width': 120, 'height': 98, 'format': "BMP"})) 1168 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid", 1169 {'width': 120, 'height': 98, 'format': "BMP"})) 1170 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", 1171 {'width': 120, 'height': 133, 'format': "BMP"})) 1172 1173 def GetTargetsForImageOrigin(self, origin): 1174 return self.imagetargets 1175 1176 def __init__(self): 1177 parentprofile.__init__(self) 1178 1179 1180 def _getgroup(self, name, groups): 1181 for key in groups: 1182 if groups[key]['name']==name: 1183 return key,groups[key] 1184 return None,None 1185 1186 1187 def normalisegroups(self, helper, data): 1188 "Assigns groups based on category data" 1189 1190 pad=[] 1191 keys=data['groups'].keys() 1192 keys.sort() 1193 for k in keys: 1194 if k: # ignore key 0 which is 'No Group' 1195 name=data['groups'][k]['name'] 1196 pad.append(name) 1197 1198 groups=helper.getmostpopularcategories(10, data['phonebook'], ["No Group"], 22, pad) 1199 1200 # alpha sort 1201 groups.sort() 1202 1203 # newgroups 1204 newgroups={} 1205 1206 # put in No group 1207 newgroups[0]={'ring': 0, 'name': 'No Group'} 1208 1209 # populate 1210 for name in groups: 1211 # existing entries remain unchanged 1212 if name=="No Group": continue 1213 key,value=self._getgroup(name, data['groups']) 1214 if key is not None and key!=0: 1215 newgroups[key]=value 1216 # new entries get whatever numbers are free 1217 for name in groups: 1218 key,value=self._getgroup(name, newgroups) 1219 if key is None: 1220 for key in range(1,100000): 1221 if key not in newgroups: 1222 newgroups[key]={'ring': 0, 'name': name} 1223 break 1224 1225 # yay, done 1226 if data['groups']!=newgroups: 1227 data['groups']=newgroups 1228 data['rebootphone']=True 1229 1230 def convertphonebooktophone(self, helper, data): 1231 """Converts the data to what will be used by the phone 1232 1233 @param data: contains the dict returned by getfundamentals 1234 as well as where the results go""" 1235 results={} 1236 1237 self.normalisegroups(helper, data) 1238 1239 for pbentry in data['phonebook']: 1240 if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES: 1241 break 1242 e={} # entry out 1243 entry=data['phonebook'][pbentry] # entry in 1244 try: 1245 # serial 1246 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', 0) 1247 serial2=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial2', serial1) 1248 1249 e['serial1']=serial1 1250 e['serial2']=serial2 1251 for ss in entry["serials"]: 1252 if ss["sourcetype"]=="bitpim": 1253 e['bitpimserial']=ss 1254 assert e['bitpimserial'] 1255 1256 # name 1257 e['name']=helper.getfullname(entry.get('names', []),1,1,22)[0] 1258 1259 # categories/groups 1260 cat=helper.makeone(helper.getcategory(entry.get('categories', []),0,1,22), None) 1261 if cat is None: 1262 e['group']=0 1263 else: 1264 key,value=self._getgroup(cat, data['groups']) 1265 if key is not None: 1266 e['group']=key 1267 else: 1268 # sorry no space for this category 1269 e['group']=0 1270 1271 # email addresses 1272 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,48) 1273 e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "") 1274 1275 # url 1276 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,48), "") 1277 1278 # memo (-1 is to leave space for null terminator - not all software puts it in, but we do) 1279 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "") 1280 1281 # phone numbers 1282 # there must be at least one email address or phonenumber 1283 minnumbers=1 1284 if len(emails): minnumbers=0 1285 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS) 1286 e['numberspeeddials']=[] 1287 e['numbertypes']=[] 1288 e['numbers']=[] 1289 for numindex in range(len(numbers)): 1290 num=numbers[numindex] 1291 # deal with type 1292 b4=len(e['numbertypes']) 1293 type=num['type'] 1294 for i,t in enumerate(self.protocolclass.numbertypetab): 1295 if type==t: 1296 # some voodoo to ensure the second home becomes home2 1297 if i in e['numbertypes'] and t[-1]!='2': 1298 type+='2' 1299 continue 1300 e['numbertypes'].append(i) 1301 break 1302 if t=='none': # conveniently last entry 1303 e['numbertypes'].append(i) 1304 break 1305 if len(e['numbertypes'])==b4: 1306 # we couldn't find a type for the number 1307 continue 1308 # deal with number 1309 number=phonize(num['number']) 1310 if len(number)==0: 1311 # no actual digits in the number 1312 continue 1313 if len(number)>48: # get this number from somewhere sensible 1314 # ::TODO:: number is too long and we have to either truncate it or ignore it? 1315 number=number[:48] # truncate for moment 1316 e['numbers'].append(number) 1317 # deal with speed dial 1318 sd=num.get("speeddial", -1) 1319 if self.protocolclass.NUMSPEEDDIALS: 1320 if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL: 1321 e['numberspeeddials'].append(sd) 1322 else: 1323 e['numberspeeddials'].append(255) 1324 1325 e['numberspeeddials']=helper.filllist(e['numberspeeddials'], 5, 255) 1326 e['numbertypes']=helper.filllist(e['numbertypes'], 5, 0) 1327 e['numbers']=helper.filllist(e['numbers'], 5, "") 1328 1329 # ringtone 1330 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) 1331 1332 # flags 1333 e['secret']=helper.getflag(entry.get('flags',[]), 'secret', False) 1334 1335 results[pbentry]=e 1336 1337 except helper.ConversionFailed: 1338 continue 1339 1340 data['phonebook']=results 1341 return data 1342 1343 _supportedsyncs=( 1344 ('phonebook', 'read', None), # all phonebook reading 1345 #('calendar', 'read', None), # all calendar reading 1346 #('wallpaper', 'read', None), # all wallpaper reading 1347 #('ringtone', 'read', None), # all ringtone reading 1348 #('call_history', 'read', None),# all call history list reading 1349 #('sms', 'read', None), # all SMS list reading 1350 #('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 1351 #('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 1352 #('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 1353 #('wallpaper', 'write', 'OVERWRITE'), 1354 #('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 1355 #('ringtone', 'write', 'OVERWRITE'), 1356 #('sms', 'write', 'OVERWRITE'), # all SMS list writing 1357 ) 1358 1359 def QueryAudio(self, origin, currentextension, afi): 1360 # we don't modify any of these 1361 if afi.format in ("MIDI", "QCP", "PMD"): 1362 return currentextension, afi 1363 # examine mp3 1364 if afi.format=="MP3": 1365 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: 1366 return currentextension, afi 1367 # convert it 1368 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 32, 'samplerate': 22050})) 1369 1370 1371 1372
Generated by PyXR 0.9.4