Package phones :: Module com_lgvi125
[hide private]
[frames] | no frames]

Source Code for Module phones.com_lgvi125

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2005 Andrew Zitnay <drew@zitnay.com> 
   4  ### 
   5  ### This program is free software; you can redistribute it and/or modify 
   6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   7  ### 
   8  ### $Id: com_lgvi125.py 4365 2007-08-17 21:11:59Z djpham $ 
   9   
  10  """Communicate with the LG VI125 cell phone""" 
  11   
  12  # standard modules 
  13  import datetime 
  14  import re 
  15  import time 
  16  import cStringIO 
  17  import sha 
  18   
  19  # my modules 
  20  import bpcalendar 
  21  import common 
  22  import commport 
  23  import copy 
  24  import p_lgvi125 
  25  import p_brew 
  26  import com_brew 
  27  import com_phone 
  28  import com_lg 
  29  import prototypes 
  30  import fileinfo 
  31  import call_history 
  32  import sms 
33 34 -class Phone(com_phone.Phone,com_brew.BrewProtocol,com_lg.LGPhonebook,com_lg.LGIndexedMedia):
35 "Talk to the LG VI125 cell phone" 36 37 desc="LG-VI125" 38 wallpaperindexfilename="dloadindex/brewImageIndex.map" 39 ringerindexfilename="dloadindex/brewRingerIndex.map" 40 protocolclass=p_lgvi125 41 serialsname='lgvi125' 42 43 imagelocations=( 44 # offset, index file, files location, type, maximumentries 45 ( 10, "dloadindex/brewImageIndex.map", "brew/shared", "images", 30), 46 ) 47 48 ringtonelocations=( 49 # offset, index file, files location, type, maximumentries 50 ( 50, "dloadindex/brewRingerIndex.map", "user/sound/ringer", "ringers", 30), 51 ) 52 53 builtinimages=('Balloons', 'Soccer', 'Basketball', 'Bird', 54 'Sunflower', 'Puppy', 'Mountain House', 'Beach') 55 56 builtinringtones=( 'Ring 1', 'Ring 2', 'Ring 3', 'Ring 4', 'Ring 5', 57 'Ring 6', 'Alert 1', 'Alert 2', 'Alert 3', 'Alert 4', 'Alert 5', 58 'Alert 6', 'Moon light', 'Bumble Bee', 'Latin', 'Baroque', 59 'Lovable baby', 'LG sound', 'Love Song', 'Badinerie', 'Follow Me', 60 'Head & Shoulder', 'Lake Trance', 'Beethovan', 'Lets Play', 61 'Piano Concerto No.1', 'Pleasure', 'Leichte Kavallerie', 62 'Up & Down Melody', 'Vivaldi - Winter' ) 63 64
65 - def __init__(self, logtarget, commport):
66 "Calls all the constructors and sets initial modes" 67 com_phone.Phone.__init__(self, logtarget, commport) 68 com_brew.BrewProtocol.__init__(self) 69 com_lg.LGPhonebook.__init__(self) 70 com_lg.LGIndexedMedia.__init__(self) 71 self.log("Attempting to contact phone") 72 self.mode=self.MODENONE 73 self._cal_has_voice_id=hasattr(self.protocolclass, 'cal_has_voice_id') \ 74 and self.protocolclass.cal_has_voice_id
75
76 - def getfundamentals(self, results):
77 """Gets information fundamental to interopating with the phone and UI. 78 79 Currently this is: 80 81 - 'uniqueserial' a unique serial number representing the phone 82 - 'groups' the phonebook groups 83 - 'wallpaper-index' map index numbers to names 84 - 'ringtone-index' map index numbers to ringtone names 85 86 This method is called before we read the phonebook data or before we 87 write phonebook data. 88 """ 89 90 # use a hash of ESN and other stuff (being paranoid) 91 self.log("Retrieving fundamental phone information") 92 self.log("Phone serial number") 93 results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest() 94 # now read groups 95 self.log("Reading group information") 96 buf=prototypes.buffer(self.getfilecontents("pim/pbookgroup.dat")) 97 g=self.protocolclass.pbgroups() 98 g.readfrombuffer(buf) 99 self.logdata("Groups read", buf.getdata(), g) 100 groups={} 101 for i in range(len(g.groups)): 102 if len(g.groups[i].name) and g.groups[i].number!=255: # sometimes have zero length names 103 groups[g.groups[i].number]={ 'ring': g.groups[i].ring, 'name': g.groups[i].name } 104 results['groups']=groups 105 self.getwallpaperindices(results) 106 self.getringtoneindices(results) 107 self.log("Fundamentals retrieved") 108 return results
109
110 - def savesms(self, result, merge):
111 self._setquicktext(result) 112 result['rebootphone']=True 113 return result
114
115 - def _setquicktext(self, result):
116 sf=self.protocolclass.sms_quick_text() 117 quicktext=result.get('canned_msg', []) 118 count=0 119 for entry in quicktext: 120 if count < self.protocolclass.SMS_CANNED_MAX_ITEMS: 121 sf.msgs.append(entry['text'][:self.protocolclass.SMS_CANNED_MAX_LENGTH-1]) 122 count+=1 123 else: 124 break 125 if count!=0: 126 # don't create the file if there are no entries 127 buf=prototypes.buffer() 128 sf.writetobuffer(buf) 129 self.logdata("Writing calendar", buf.getvalue(), sf) 130 self.writefile(self.protocolclass.SMS_CANNED_FILENAME, buf.getvalue()) 131 return
132
133 - def getsms(self, result):
134 # get the quicktext (LG name for canned messages) 135 result['canned_msg']=self._getquicktext() 136 result['sms']=self._readsms() 137 return result
138
139 - def _readsms(self):
140 res={} 141 # go through the sms directory looking for messages 142 for item in self.getfilesystem("sms").values(): 143 if item['type']=='file': 144 folder=None 145 for f,pat in self.protocolclass.SMS_PATTERNS.items(): 146 if pat.match(item['name']): 147 folder=f 148 break 149 if folder: 150 buf=prototypes.buffer(self.getfilecontents(item['name'], True)) 151 self.logdata("SMS message file " +item['name'], buf.getdata()) 152 if folder=='Inbox': 153 sf=self.protocolclass.sms_in() 154 sf.readfrombuffer(buf) 155 entry=self._getinboxmessage(sf) 156 res[entry.id]=entry 157 elif folder=='Sent': 158 sf=self.protocolclass.sms_out() 159 sf.readfrombuffer(buf) 160 entry=self._getoutboxmessage(sf) 161 res[entry.id]=entry 162 elif folder=='Saved': 163 sf=self.protocolclass.sms_saved() 164 sf.readfrombuffer(buf) 165 if sf.outboxmsg: 166 entry=self._getoutboxmessage(sf.outbox) 167 else: 168 entry=self._getinboxmessage(sf.inbox) 169 entry.folder=entry.Folder_Saved 170 res[entry.id]=entry 171 return res
172
173 - def _getquicktext(self):
174 quicks=[] 175 try: 176 buf=prototypes.buffer(self.getfilecontents("sms/mediacan000.dat")) 177 sf=self.protocolclass.sms_quick_text() 178 sf.readfrombuffer(buf) 179 self.logdata("SMS quicktext file sms/mediacan000.dat", buf.getdata(), sf) 180 for rec in sf.msgs: 181 if rec.msg!="": 182 quicks.append({ 'text': rec.msg, 'type': sms.CannedMsgEntry.user_type }) 183 except com_brew.BrewNoSuchFileException: 184 pass # do nothing if file doesn't exist 185 return quicks
186
187 - def _getinboxmessage(self, sf):
188 entry=sms.SMSEntry() 189 entry.folder=entry.Folder_Inbox 190 entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime) 191 entry._from=self._getsender(sf.sender, sf.sender_length) 192 entry.subject=sf.subject 193 entry.locked=sf.locked 194 if sf.priority==0: 195 entry.priority=sms.SMSEntry.Priority_Normal 196 else: 197 entry.priority=sms.SMSEntry.Priority_High 198 entry.read=sf.read 199 txt="" 200 if sf.num_msg_elements==1 and sf.bin_header1==0: 201 txt=self._get_text_from_sms_msg_without_header(sf.msgs[0].msg, sf.msglengths[0].msglength) 202 else: 203 for i in range(sf.num_msg_elements): 204 txt+=self._get_text_from_sms_msg_with_header(sf.msgs[i].msg, sf.msglengths[i].msglength) 205 entry.text=unicode(txt, errors='ignore') 206 entry.callback=sf.callback 207 return entry
208
209 - def _getoutboxmessage(self, sf):
210 entry=sms.SMSEntry() 211 entry.folder=entry.Folder_Sent 212 entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent)) 213 # add all the recipients 214 for r in sf.recipients: 215 if r.number: 216 confirmed=(r.status==5) 217 confirmed_date=None 218 if confirmed: 219 confirmed_date="%d%02d%02dT%02d%02d00" % r.timereceived 220 entry.add_recipient(r.number, confirmed, confirmed_date) 221 entry.subject=sf.subject 222 txt="" 223 if sf.num_msg_elements==1 and not sf.messages[0].binary: 224 txt=self._get_text_from_sms_msg_without_header(sf.messages[0].msg, sf.messages[0].length) 225 else: 226 for i in range(sf.num_msg_elements): 227 txt+=self._get_text_from_sms_msg_with_header(sf.messages[i].msg, sf.messages[i].length) 228 entry.text=unicode(txt, errors='ignore') 229 if sf.priority==0: 230 entry.priority=sms.SMSEntry.Priority_Normal 231 else: 232 entry.priority=sms.SMSEntry.Priority_High 233 entry.locked=sf.locked 234 entry.callback=sf.callback 235 return entry
236
237 - def _get_text_from_sms_msg_without_header(self, msg, num_septets):
238 out="" 239 for i in range(num_septets): 240 tmp = (msg[(i*7)/8].byte<<8) | msg[((i*7)/8) + 1].byte 241 bit_index = 9 - ((i*7) % 8) 242 out += chr((tmp >> bit_index) & 0x7f) 243 return out
244
245 - def _get_text_from_sms_msg_with_header(self, msg, num_septets):
246 data_len = ((msg[0].byte+1)*8+6)/7 247 seven_bits={} 248 raw={} 249 out={} 250 # re-order the text into the correct order for separating into 251 # 7-bit characters 252 for i in range(0, (num_septets*7)/8+8, 7): 253 for k in range(7): 254 raw[i+6-k]=msg[i+k].byte 255 # extract the 7-bit chars 256 for i in range(num_septets+7): 257 tmp = (raw[(i*7)/8]<<8) | raw[((i*7)/8) + 1] 258 bit_index = 9 - ((i*7) % 8) 259 seven_bits[i] = (tmp >> bit_index) & 0x7f 260 # correct the byte order and remove the data portion of the message 261 i=0 262 for i in range(0, num_septets+7, 8): 263 for k in range(8): 264 if(i+7-k-data_len>=0): 265 if i+k<num_septets+7: 266 out[i+7-k-data_len]=seven_bits[i+k] 267 res="" 268 for i in range(num_septets-data_len): 269 res+=chr(out[i]) 270 return res
271
272 - def _getsender(self, raw, len):
273 result="" 274 for i in range(len): 275 if(raw[i].byte==10): 276 result+="0" 277 else: 278 result+="%d" % raw[i].byte 279 return result
280
281 - def getcallhistory(self, result):
282 res={} 283 # read the incoming call history file 284 self._readhistoryfile("pim/missed_log.dat", 'Missed', res) 285 self._readhistoryfile("pim/outgoing_log.dat", 'Outgoing', res) 286 self._readhistoryfile("pim/incoming_log.dat", 'Incoming', res) 287 result['call_history']=res 288 return result
289
290 - def _readhistoryfile(self, fname, folder, res):
291 try: 292 buf=prototypes.buffer(self.getfilecontents(fname)) 293 ch=self.protocolclass.callhistory() 294 ch.readfrombuffer(buf) 295 self.logdata("Call History", buf.getdata(), ch) 296 for call in ch.calls: 297 if call.number=='' and call.name=='': 298 continue 299 entry=call_history.CallHistoryEntry() 300 entry.folder=folder 301 if call.duration: 302 entry.duration=call.duration 303 entry.datetime=((call.GPStime)) 304 if call.number=='': # restricted calls have no number 305 entry.number=call.name 306 else: 307 entry.number=call.number 308 res[entry.id]=entry 309 except com_brew.BrewNoSuchFileException: 310 pass # do nothing if file doesn't exist 311 return
312
313 - def getwallpaperindices(self, results):
314 return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index')
315
316 - def getringtoneindices(self, results):
317 return self.getmediaindex(self.builtinringtones, self.ringtonelocations, results, 'ringtone-index')
318
319 - def getphonebook(self,result):
320 """Reads the phonebook data. The L{getfundamentals} information will 321 already be in result.""" 322 323 pbook={} 324 # Bug in the phone. if you repeatedly read the phone book it starts 325 # returning a random number as the number of entries. We get around 326 # this by switching into brew mode which clears that. 327 self.mode=self.MODENONE 328 self.setmode(self.MODEBREW) 329 self.log("Reading number of phonebook entries") 330 req=self.protocolclass.pbinforequest() 331 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) 332 numentries=res.numentries 333 if numentries<0 or numentries>1000: 334 self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way") 335 numentries=0 336 firstserial=None 337 loop=xrange(0,1000) 338 hardway=True 339 else: 340 self.log("There are %d entries" % (numentries,)) 341 loop=xrange(0, numentries) 342 hardway=False 343 # reset cursor 344 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) 345 problemsdetected=False 346 dupecheck={} 347 for i in loop: 348 if hardway: 349 numentries+=1 350 req=self.protocolclass.pbreadentryrequest() 351 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 352 self.log("Read entry "+`i`+" - "+res.entry.name) 353 entry=self.extractphonebookentry(res.entry, result) 354 if hardway and firstserial is None: 355 firstserial=res.entry.serial1 356 pbook[i]=entry 357 if res.entry.serial1 in dupecheck: 358 self.log("Entry %s has same serial as entry %s. This will cause problems." % (`entry`, dupecheck[res.entry.serial1])) 359 problemsdetected=True 360 else: 361 dupecheck[res.entry.serial1]=entry 362 self.progress(i, numentries, res.entry.name) 363 #### Advance to next entry 364 req=self.protocolclass.pbnextentryrequest() 365 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 366 if hardway: 367 # look to see if we have looped 368 if res.serial==firstserial or res.serial==0: 369 break 370 371 self.progress(numentries, numentries, "Phone book read completed") 372 373 if problemsdetected: 374 self.log("There are duplicate serial numbers. See above for details.") 375 raise common.IntegrityCheckFailed(self.desc, "Data in phonebook is inconsistent. There are multiple entries with the same serial number. See the log.") 376 377 result['phonebook']=pbook 378 cats=[] 379 for i in result['groups']: 380 if result['groups'][i]['name']!='No Group': 381 cats.append(result['groups'][i]['name']) 382 result['categories']=cats 383 print "returning keys",result.keys() 384 return pbook
385
386 - def savegroups(self, data):
387 groups=data['groups'] 388 keys=groups.keys() 389 keys.sort() 390 391 g=self.protocolclass.pbgroups() 392 sg=self.protocolclass.pbseqgroups() 393 for k in keys: 394 e=self.protocolclass.pbgroup() 395 e.number=k 396 e.unknown1=48 397 e.ring=groups[k]['ring'] 398 e.unknown7=0 399 e.unknown8=0 400 e.name=groups[k]['name'] 401 g.groups.append(e) 402 403 e=self.protocolclass.pbseqgroup() 404 e.number=k 405 e.unknown=48 406 sg.seqgroups.append(e) 407 408 for k in xrange(k+1,20): 409 e=self.protocolclass.pbseqgroup() 410 e.number=0 411 e.unknown=0 412 sg.seqgroups.append(e) 413 414 groupnums=[] 415 for k in range(self.protocolclass.NUMMAPGROUPS): 416 groupnums.append(-1); 417 418 for k in data['phonebook'].keys(): 419 groupnums[data['phonebook'][k]['serial1']]=data['phonebook'][k]['group'] 420 421 mg=self.protocolclass.pbmapgroups() 422 for k in range(len(groupnums)): 423 e=self.protocolclass.pbmapgroup() 424 if (groupnums[k]==-1): 425 e.number=255 426 e.unknown=255 427 else: 428 e.number=groupnums[k] 429 e.unknown=48 430 mg.mapgroups.append(e) 431 432 buffer=prototypes.buffer() 433 g.writetobuffer(buffer) 434 self.logdata("New group file", buffer.getvalue(), g) 435 self.writefile("pim/pbookgroup.dat", buffer.getvalue()) 436 437 buffer=prototypes.buffer() 438 sg.writetobuffer(buffer) 439 self.logdata("New seqgroup file", buffer.getvalue(), sg) 440 self.writefile("pim/pbookseqgroup.dat", buffer.getvalue()) 441 442 buffer=prototypes.buffer() 443 mg.writetobuffer(buffer) 444 self.logdata("New mapgroup file", buffer.getvalue(), mg) 445 self.writefile("pim/pbookmapgroup.dat", buffer.getvalue())
446
447 - def savephonebook(self, data):
448 "Saves out the phonebook" 449 self.savegroups(data) 450 451 progressmax=len(data['phonebook'].keys()) 452 453 # To write the phone book, we scan through all existing entries 454 # and record their record number and serial. 455 # We then delete any entries that aren't in data 456 # We then write out our records, using overwrite or append 457 # commands as necessary 458 serialupdates=[] 459 existingpbook={} # keep track of the phonebook that is on the phone 460 self.mode=self.MODENONE 461 self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary 462 self.setmode(self.MODEPHONEBOOK) 463 # similar loop to reading 464 req=self.protocolclass.pbinforequest() 465 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) 466 numexistingentries=res.numentries 467 if numexistingentries<0 or numexistingentries>1000: 468 self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way") 469 numexistingentries=0 470 firstserial=None 471 loop=xrange(0,1000) 472 hardway=True 473 else: 474 self.log("There are %d existing entries" % (numexistingentries,)) 475 progressmax+=numexistingentries 476 loop=xrange(0, numexistingentries) 477 hardway=False 478 progresscur=0 479 # reset cursor 480 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) 481 for i in loop: 482 ### Read current entry 483 if hardway: 484 numexistingentries+=1 485 progressmax+=1 486 req=self.protocolclass.pbreadentryrequest() 487 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 488 489 entry={ 'number': res.entry.serial1, 'serial1': res.entry.serial1, 490 'serial2': res.entry.serial2, 'name': res.entry.name} 491 assert entry['serial1']==entry['serial2'] # always the same 492 self.log("Reading entry "+`i`+" - "+entry['name']) 493 if hardway and firstserial is None: 494 firstserial=res.entry.serial1 495 existingpbook[i]=entry 496 self.progress(progresscur, progressmax, "existing "+entry['name']) 497 #### Advance to next entry 498 req=self.protocolclass.pbnextentryrequest() 499 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 500 progresscur+=1 501 if hardway: 502 # look to see if we have looped 503 if res.serial==firstserial or res.serial==0: 504 break 505 # we have now looped around back to begining 506 507 # Find entries that have been deleted 508 pbook=data['phonebook'] 509 dellist=[] 510 for i in range(0, numexistingentries): 511 ii=existingpbook[i] 512 serial=ii['serial1'] 513 item=self._findserial(serial, pbook) 514 if item is None: 515 dellist.append(i) 516 517 progressmax+=len(dellist) # more work to do 518 519 # Delete those entries 520 for i in dellist: 521 progresscur+=1 522 numexistingentries-=1 # keep count right 523 ii=existingpbook[i] 524 self.log("Deleting entry "+`i`+" - "+ii['name']) 525 req=self.protocolclass.pbdeleteentryrequest() 526 req.entrynumber=ii['serial1'] 527 req.serial1=ii['serial1'] 528 req.serial2=ii['serial2'] 529 self.sendpbcommand(req, self.protocolclass.pbdeleteentryresponse) 530 self.progress(progresscur, progressmax, "Deleting "+ii['name']) 531 # also remove them from existingpbook 532 del existingpbook[i] 533 534 # counter to keep track of record number (otherwise appends don't work) 535 counter=0 536 # Now rewrite out existing entries 537 keys=existingpbook.keys() 538 existingserials=[] 539 keys.sort() # do in same order as existingpbook 540 for i in keys: 541 progresscur+=1 542 ii=pbook[self._findserial(existingpbook[i]['serial1'], pbook)] 543 self.log("Rewriting entry "+`i`+" - "+ii['name']) 544 self.progress(progresscur, progressmax, "Rewriting "+ii['name']) 545 entry=self.makeentry(counter, ii, data) 546 counter+=1 547 existingserials.append(existingpbook[i]['serial1']) 548 req=self.protocolclass.pbupdateentryrequest() 549 req.entry=entry 550 res=self.sendpbcommand(req, self.protocolclass.pbupdateentryresponse) 551 serialupdates.append( ( ii["bitpimserial"], 552 {'sourcetype': self.serialsname, 'serial1': res.serial1, 'serial2': res.serial1, 553 'sourceuniqueid': data['uniqueserial']}) 554 ) 555 assert ii['serial1']==res.serial1 # serial should stay the same 556 557 # Finally write out new entries 558 keys=pbook.keys() 559 keys.sort() 560 for i in keys: 561 ii=pbook[i] 562 print "looking for "+`ii['serial1']`+" "+`ii['name']` 563 if ii['serial1'] in existingserials: 564 continue # already wrote this one out 565 print "found new" 566 progresscur+=1 567 entry=self.makeentry(counter, ii, data) 568 counter+=1 569 self.log("Appending entry "+ii['name']) 570 self.progress(progresscur, progressmax, "Writing "+ii['name']) 571 req=self.protocolclass.pbappendentryrequest() 572 req.entry=entry 573 res=self.sendpbcommand(req, self.protocolclass.pbappendentryresponse) 574 serialupdates.append( ( ii["bitpimserial"], 575 {'sourcetype': self.serialsname, 'serial1': res.newserial, 'serial2': res.newserial, 576 'sourceuniqueid': data['uniqueserial']}) 577 ) 578 data["serialupdates"]=serialupdates 579 580 return data
581 582
583 - def _findserial(self, serial, dict):
584 """Searches dict to find entry with matching serial. If not found, 585 returns None""" 586 for i in dict: 587 if dict[i]['serial1']==serial: 588 return i 589 return None
590
591 - def _normaliseindices(self, d):
592 "turn all negative keys into positive ones for index" 593 res={} 594 keys=d.keys() 595 keys.sort() 596 keys.reverse() 597 for k in keys: 598 if k<0: 599 for c in range(999999): 600 if c not in keys and c not in res: 601 break 602 res[c]=d[k] 603 else: 604 res[k]=d[k] 605 return res
606
607 - def extractphonebookentry(self, entry, fundamentals):
608 """Return a phonebook entry in BitPim format. This is called from getphonebook.""" 609 res={} 610 # serials 611 res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.serial1, 'serial2': entry.serial2, 612 'sourceuniqueid': fundamentals['uniqueserial']} ] 613 # only one name 614 res['names']=[ {'full': entry.name} ] 615 # only one category 616 cat=fundamentals['groups'].get(entry.group, {'name': "No Group"})['name'] 617 if cat!="No Group": 618 res['categories']=[ {'category': cat} ] 619 # emails 620 res['emails']=[] 621 for i in entry.emails: 622 if len(i.email): 623 res['emails'].append( {'email': i.email} ) 624 if not len(res['emails']): del res['emails'] # it was empty 625 # urls 626 if 'url' in entry.getfields() and len(entry.url): 627 res['urls']=[ {'url': entry.url} ] 628 # memos 629 if 'memo' in entry.getfields() and len(entry.memo): 630 res['memos']=[ {'memo': entry.memo } ] 631 632 # ringtones 633 res['ringtones']=[] 634 if 'ringtone' in entry.getfields() and entry.ringtone!=self.protocolclass.NORINGTONE: 635 try: 636 tone=fundamentals['ringtone-index'][entry.ringtone]['name'] 637 res['ringtones'].append({'ringtone': tone, 'use': 'call'}) 638 except: 639 print "can't find ringtone for index",entry.ringtone 640 if len(res['ringtones'])==0: 641 del res['ringtones'] 642 res=self._assignpbtypeandspeeddials(entry, res) 643 return res
644
645 - def _assignpbtypeandspeeddials(self, entry, res):
646 # numbers 647 res['numbers']=[] 648 for i in range(self.protocolclass.NUMPHONENUMBERS): 649 num=entry.numbers[i].number 650 type=entry.numbertypes[i].numbertype 651 speeddial=entry.numberspeeddials[i].numberspeeddial 652 if len(num): 653 t=self.protocolclass.numbertypetab[type] 654 if t[-1]=='2': 655 t=t[:-1] 656 if speeddial==255: 657 res['numbers'].append({'number': num, 'type': t}) 658 else: 659 res['numbers'].append({'number': num, 'type': t, 'speeddial': speeddial}) 660 return res
661
662 - def _findmediainindex(self, index, name, pbentryname, type):
663 if type=="ringtone": default=self.protocolclass.NORINGTONE 664 elif type=="message ringtone": default=self.protocolclass.NOMSGRINGTONE 665 elif type=="wallpaper": default=self.protocolclass.NOWALLPAPER 666 else: 667 assert False, "unknown type "+type 668 669 if name is None: 670 return default 671 for i in index: 672 if index[i]['name']==name: 673 return i 674 self.log("%s: Unable to find %s %s in the index. Setting to default." % (pbentryname, type, name)) 675 return default
676
677 - def makeentry(self, counter, entry, data):
678 """Creates pbentry object 679 680 @param counter: The new entry number 681 @param entry: The phonebook object (as returned from convertphonebooktophone) that we 682 are using as the source 683 @param data: The main dictionary, which we use to get access to media indices amongst 684 other things 685 """ 686 e=self.protocolclass.pbentry() 687 e.avatar=0 688 689 for k in entry: 690 # special treatment for lists 691 692 if k in ('emails', 'numbers', 'numbertypes', 'numberspeeddials'): 693 l=getattr(e,k) 694 for item in entry[k]: 695 l.append(item) 696 elif k=='ringtone': 697 e.ringtone=self._findmediainindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone') 698 elif k in e.getfields(): 699 # everything else we just set 700 setattr(e,k,entry[k]) 701 702 e.unknown7=0 703 e.group=0 # couldn't figure out groups; this at least sets it to No Group 704 e.unknown12=1 # couldn't figure out ringtone; this at least sets it to Default 705 return e
706
707 - def is_mode_brew(self):
708 req=p_brew.memoryconfigrequest() 709 respc=p_brew.memoryconfigresponse 710 711 for baud in 0, 38400, 115200: 712 if baud: 713 if not self.comm.setbaudrate(baud): 714 continue 715 try: 716 self.sendbrewcommand(req, respc, callsetmode=False) 717 return True 718 except com_phone.modeignoreerrortypes: 719 pass 720 return False
721 722 brew_version_txt_key='ams_version.txt' 723 brew_version_file='ams/version.txt' 724 lgpbinfo_key='lgpbinfo' 725 esn_file_key='esn_file' 726 esn_file='nvm/$SYS.ESN' 727 my_model='VI125'
728 - def get_detect_data(self, res):
729 # get the data needed for detection 730 if not res.has_key(self.brew_version_txt_key): 731 # read the BREW version.txt file, which may contain phone model info 732 print 'reading BREW version.txt' 733 try: 734 # read this file 735 s=self.getfilecontents(self.brew_version_file) 736 res[self.brew_version_txt_key]=s 737 except com_brew.BrewNoSuchFileException: 738 res[self.brew_version_txt_key]=None 739 except: 740 if __debug__: 741 raise 742 res[self.brew_version_txt_key]=None 743 # get pbinfo data, which also may include phone model 744 if not res.has_key(self.lgpbinfo_key): 745 print 'getting pbinfo' 746 try: 747 req=self.protocolclass.pbinforequest() 748 resp=self.sendpbcommand(req, self.protocolclass.pbstartsyncresponse) 749 res[self.lgpbinfo_key]=resp.unknown 750 except: 751 if __debug__: 752 raise 753 res[self.lgpbinfo_key]=None 754 # attempt the get the ESN 755 if not res.has_key(self.esn_file_key): 756 print 'reading ESN file: '+`self.esn_file`+' end' 757 try: 758 s=self.getfilecontents(self.esn_file) 759 res[self.esn_file_key]=s 760 except: 761 res[self.esn_file_key]=None
762
763 - def get_esn(self, data=None):
764 # return the ESN for this phone 765 try: 766 if data is None: 767 s=self.getfilecontents(self.esn_file) 768 else: 769 s=data 770 if s: 771 s=s[85:89] 772 return '%02X%02X%02X%02X'%(ord(s[3]), ord(s[2]), 773 ord(s[1]), ord(s[0])) 774 except: 775 if __debug__: 776 raise
777
778 - def eval_detect_data(self, res):
779 found=False 780 if res.get(self.brew_version_txt_key, None) is not None: 781 found=res[self.brew_version_txt_key][:len(self.my_model)]==self.my_model 782 if not found and res.get(self.lgpbinfo_key, None): 783 found=res[self.lgpbinfo_key].find(self.my_model)!=-1 784 if found: 785 res['model']=self.my_model 786 res['manufacturer']='LG Electronics Inc' 787 s=res.get(self.esn_file_key, None) 788 if s: 789 res['esn']=self.get_esn(s)
790 791 @classmethod
792 - def detectphone(_, coms, likely_ports, res, _module, _log):
793 if not likely_ports: 794 # cannot detect any likely ports 795 return None 796 for port in likely_ports: 797 if not res.has_key(port): 798 res[port]={ 'mode_modem': None, 'mode_brew': None, 799 'manufacturer': None, 'model': None, 800 'firmware_version': None, 'esn': None, 801 'firmwareresponse': None } 802 try: 803 if res[port]['mode_brew']==False or \ 804 res[port]['model']: 805 # either phone is not in BREW, or a model has already 806 # been found, not much we can do now 807 continue 808 p=_module.Phone(_log, commport.CommConnection(_log, port, timeout=1)) 809 if res[port]['mode_brew'] is None: 810 res[port]['mode_brew']=p.is_mode_brew() 811 if res[port]['mode_brew']: 812 p.get_detect_data(res[port]) 813 p.eval_detect_data(res[port]) 814 p.comm.close() 815 except: 816 if __debug__: 817 raise
818 819 # Calendar stuff------------------------------------------------------------
820 - def getcalendar(self,result):
821 # Read exceptions file first 822 try: 823 buf=prototypes.buffer(self.getfilecontents( 824 self.protocolclass.cal_exception_file_name)) 825 ex=self.protocolclass.scheduleexceptionfile() 826 ex.readfrombuffer(buf) 827 self.logdata("Calendar exceptions", buf.getdata(), ex) 828 exceptions={} 829 for i in ex.items: 830 exceptions.setdefault(i.pos, []).append( (i.year,i.month,i.day) ) 831 except com_brew.BrewNoSuchFileException: 832 exceptions={} 833 834 # Now read schedule 835 try: 836 buf=prototypes.buffer(self.getfilecontents( 837 self.protocolclass.cal_data_file_name)) 838 if len(buf.getdata())<2: 839 # file is empty, and hence same as non-existent 840 raise com_brew.BrewNoSuchFileException() 841 sc=self.protocolclass.schedulefile() 842 self.logdata("Calendar", buf.getdata(), sc) 843 sc.readfrombuffer(buf) 844 sc.readfrombuffer(buf) 845 res=self.get_cal(sc, exceptions, result.get('ringtone-index', {})) 846 except com_brew.BrewNoSuchFileException: 847 res={} 848 result['calendar']=res 849 return result
850
851 - def savecalendar(self, dict, merge):
852 # ::TODO:: obey merge param 853 # get the list of available voice alarm files 854 voice_files={} 855 if self._cal_has_voice_id: 856 try: 857 file_list=self.getfilesystem(self.protocolclass.cal_dir) 858 for k in file_list.keys(): 859 if k.endswith(self.protocolclass.cal_voice_ext): 860 voice_files[int(k[8:11])]=k 861 except: 862 self.log('Failed to list Calendar Voice Files') 863 # build the schedule file 864 sc=self.protocolclass.schedulefile() 865 sc_ex=self.set_cal(sc, dict.get('calendar', {}), 866 dict.get('ringtone-index', {}), 867 voice_files) 868 buf=prototypes.buffer() 869 sc.writetobuffer(buf) 870 self.writefile(self.protocolclass.cal_data_file_name, 871 buf.getvalue()) 872 # build the exceptions 873 exceptions_file=self.protocolclass.scheduleexceptionfile() 874 for k,l in sc_ex.items(): 875 for x in l: 876 _ex=self.protocolclass.scheduleexception() 877 _ex.pos=k 878 _ex.year, _ex.month, _ex.day=x 879 exceptions_file.items.append(_ex) 880 buf=prototypes.buffer() 881 exceptions_file.writetobuffer(buf) 882 self.writefile(self.protocolclass.cal_exception_file_name, 883 buf.getvalue()) 884 # clear out any alarm voice files that may have been deleted 885 if self._cal_has_voice_id: 886 for k,e in voice_files.items(): 887 try: 888 self.rmfile(e) 889 except: 890 self.log('Failed to delete file '+e) 891 return dict
892 893 _repeat_values={ 894 protocolclass.CAL_REP_DAILY: bpcalendar.RepeatEntry.daily, 895 protocolclass.CAL_REP_MONFRI: bpcalendar.RepeatEntry.daily, 896 protocolclass.CAL_REP_WEEKLY: bpcalendar.RepeatEntry.weekly, 897 protocolclass.CAL_REP_MONTHLY: bpcalendar.RepeatEntry.monthly, 898 protocolclass.CAL_REP_YEARLY: bpcalendar.RepeatEntry.yearly 899 } 900
901 - def _build_cal_repeat(self, event, exceptions):
902 rep_val=Phone._repeat_values.get(event.repeat, None) 903 if not rep_val: 904 return None 905 rep=bpcalendar.RepeatEntry(rep_val) 906 if event.repeat==self.protocolclass.CAL_REP_MONFRI: 907 rep.interval=rep.dow=0 908 elif event.repeat!=self.protocolclass.CAL_REP_YEARLY: 909 rep.interval=1 910 rep.dow=0 911 # do exceptions 912 cal_ex=exceptions.get(event.pos, []) 913 for e in cal_ex: 914 rep.add_suppressed(*e) 915 return rep
916
917 - def _get_voice_id(self, event, entry):
918 if event.hasvoice: 919 entry.voice=event.voiceid
920
921 - def _build_cal_entry(self, event, exceptions, ringtone_index):
922 # return a copy of bpcalendar object based on my values 923 # general fields 924 entry=bpcalendar.CalendarEntry() 925 entry.start=event.start 926 entry.end=event.end 927 entry.description=event.description 928 # check for allday event 929 if entry.start[3:]==(0, 0) and entry.end[3:]==(23, 59): 930 entry.allday=True 931 # alarm 932 if event.alarmtype: 933 entry.alarm=event.alarmhours*60+event.alarmminutes 934 # ringtone 935 rt_idx=event.ringtone 936 # hack to account for the VX4650 weird ringtone setup 937 if rt_idx<50: 938 # 1st part of builtin ringtones, need offset by 1 939 rt_idx+=1 940 entry.ringtone=ringtone_index.get(rt_idx, {'name': None} )['name'] 941 # voice ID if applicable 942 if self._cal_has_voice_id: 943 self._get_voice_id(event, entry) 944 # repeat info 945 entry.repeat=self._build_cal_repeat(event, exceptions) 946 return entry
947
948 - def get_cal(self, sch_file, exceptions, ringtone_index):
949 res={} 950 for event in sch_file.events: 951 if event.pos==-1: # blank entry 952 continue 953 cal_event=self._build_cal_entry(event, exceptions, ringtone_index) 954 res[cal_event.id]=cal_event 955 return res
956 957 _alarm_info={ 958 -1: (protocolclass.CAL_REMINDER_NONE, 100, 100), 959 0: (protocolclass.CAL_REMINDER_ONTIME, 0, 0), 960 5: (protocolclass.CAL_REMINDER_5MIN, 5, 0), 961 10: (protocolclass.CAL_REMINDER_10MIN, 10, 0), 962 60: (protocolclass.CAL_REMINDER_1HOUR, 0, 1), 963 1440: (protocolclass.CAL_REMINDER_1DAY, 0, 24), 964 2880: (protocolclass.CAL_REMINDER_2DAYS, 0, 48) } 965 _default_alarm=(protocolclass.CAL_REMINDER_NONE, 100, 100) # default alarm is off 966 _phone_dow={ 967 1: protocolclass.CAL_DOW_SUN, 968 2: protocolclass.CAL_DOW_MON, 969 4: protocolclass.CAL_DOW_TUE, 970 8: protocolclass.CAL_DOW_WED, 971 16: protocolclass.CAL_DOW_THU, 972 32: protocolclass.CAL_DOW_FRI, 973 64: protocolclass.CAL_DOW_SAT 974 } 975
976 - def _set_repeat_event(self, event, entry, exceptions):
977 rep_val=self.protocolclass.CAL_REP_NONE 978 day_bitmap=0 979 rep=entry.repeat 980 if rep: 981 rep_type=rep.repeat_type 982 if rep_type==bpcalendar.RepeatEntry.yearly: 983 rep_val=self.protocolclass.CAL_REP_YEARLY 984 else: 985 rep_interval=rep.interval 986 rep_dow=rep.dow 987 if rep_type==bpcalendar.RepeatEntry.daily: 988 if rep_interval==0: 989 rep_val=self.protocolclass.CAL_REP_MONFRI 990 elif rep_interval==1: 991 rep_val=self.protocolclass.CAL_REP_DAILY 992 elif rep_type==bpcalendar.RepeatEntry.weekly: 993 start_dow=1<<datetime.date(*event.start[:3]).isoweekday()%7 994 if (rep_dow==0 or rep_dow==start_dow) and rep_interval==1: 995 rep_val=self.protocolclass.CAL_REP_WEEKLY 996 day_bitmap=self._phone_dow.get(start_dow, 0) 997 elif rep_type==bpcalendar.RepeatEntry.monthly: 998 if rep_dow==0: 999 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