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

Source Code for Module phones.com_samsungscha650

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2004 Joe Pham <djpham@netzero.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_samsungscha650.py 4365 2007-08-17 21:11:59Z djpham $ 
   9   
  10   
  11  """Communicate with a Samsung SCH-A650""" 
  12   
  13  # lib modules 
  14  import copy 
  15  import re 
  16  import sha 
  17   
  18  # my modules 
  19   
  20  import common 
  21  import commport 
  22  import com_brew 
  23  import com_samsung 
  24  import com_phone 
  25  import conversions 
  26  import fileinfo 
  27  import memo 
  28  import nameparser 
  29  import p_samsungscha650 
  30  import prototypes 
  31   
  32  import call_history 
  33   
  34  #------------------------------------------------------------------------------- 
35 -class Phone(com_samsung.Phone):
36 37 "Talk to the Samsung SCH-A650 Cell Phone" 38 39 desc="SCH-A650" 40 serialsname='scha650' 41 protocolclass=p_samsungscha650 42 parent_phone=com_samsung.Phone 43 44 __groups_range=xrange(5) 45 __phone_entries_range=xrange(1,501) 46 __pb_max_entries=25 47 __pb_max_speeddials=500 48 __pb_entry=0 49 __pb_mem_loc=1 50 __pb_group=2 51 __pb_ringtone=3 52 __pb_name=4 53 __pb_speed_dial=5 54 __pb_home_num=7 55 __pb_office_num=9 56 __pb_mobile_num=11 57 __pb_pager_num=13 58 __pb_fax_num=15 59 __pb_alias=17 60 __pb_email=21 61 __pb_four=22 62 __pb_blanks=(19, 20) 63 __pb_date_time_stamp=24 64 __pb_numbers= ({'home': __pb_home_num}, 65 {'office': __pb_office_num}, 66 {'cell': __pb_mobile_num}, 67 {'pager': __pb_pager_num}, 68 {'fax': __pb_fax_num}) 69 __pb_max_name_len=22 70 __pb_max_number_len=32 71 __pb_max_emails=1 72 73 # 'type name', 'type index name', 'origin', 'dir path', 'max file name length', 'max file name count' 74 __ringtone_info=('ringtone', 'ringtone-index', 'ringtone', 'user/sound/ringer', 17, 20) 75 __wallpaper_info=('wallpapers', 'wallpaper-index', 'images', 'nvm/brew/shared', 17, 10) 76
77 - def __init__(self, logtarget, commport):
78 79 "Calls all the constructors and sets initial modes" 80 com_samsung.Phone.__init__(self, logtarget, commport) 81 self.mode=self.MODENONE
82
83 - def getfundamentals(self, results):
84 85 """Gets information fundamental to interopating with the phone and UI. 86 87 Currently this is: 88 89 - 'uniqueserial' a unique serial number representing the phone 90 - 'groups' the phonebook groups 91 - 'wallpaper-index' map index numbers to names 92 - 'ringtone-index' map index numbers to ringtone names 93 94 This method is called before we read the phonebook data or before we 95 write phonebook data. 96 """ 97 98 self.setmode(self.MODEPHONEBOOK) 99 100 # use a hash of ESN and other stuff (being paranoid) 101 102 self.log("Retrieving fundamental phone information") 103 self.log("Reading phone serial number") 104 results['uniqueserial']=sha.new(self.get_esn()).hexdigest() 105 106 # now read groups 107 108 self.log("Reading group information") 109 g=self.get_groups(self.__groups_range) 110 groups={} 111 for i, g_i in enumerate(g): 112 if len(g_i): 113 groups[i]={ 'name': g_i } 114 results['groups']=groups 115 116 # getting rintone-index 117 self.setmode(self.MODEBREW) 118 rt_index=RingtoneIndex(self) 119 results['ringtone-index']=rt_index.get() 120 121 # getting wallpaper-index 122 img_index=ImageIndex(self) 123 results['wallpaper-index']=img_index.get() 124 125 self.setmode(self.MODEMODEM) 126 self.log("Fundamentals retrieved") 127 128 return results
129
130 - def getphonebook(self,result):
131 """Reads the phonebook data. The L{getfundamentals} information will 132 already be in result.""" 133 self.setmode(self.MODEBREW) 134 pb=PhoneBook(self) 135 pb.read() 136 pb_book=pb.get_dict(result) 137 result['phonebook']=pb_book 138 self.setmode(self.MODEMODEM) 139 return pb_book
140
141 - def savephonebook(self, data):
142 "Saves out the phonebook" 143 144 pb_book=data['phonebook'] 145 pb_groups=data['groups'] 146 ringtone_index=data.get('ringtone-index', {}) 147 self.log('Validating phonebook entries.') 148 del_entries=[] 149 for k in pb_book: 150 if not self.__validate_entry(pb_book[k], pb_groups, ringtone_index): 151 self.log('Invalid entry, entry will be not be sent.') 152 del_entries.append(k) 153 for k in del_entries: 154 self.log('Deleting entry '+\ 155 nameparser.getfullname(pb_book[k]['names'][0])) 156 del pb_book[k] 157 self._has_duplicate_speeddial(pb_book) 158 self.log('All entries validated') 159 160 pb_locs=[False]*(len(self.__phone_entries_range)+1) 161 pb_mem=[False]*len(pb_locs) 162 163 # get existing phonebook from the phone 164 self.log("Getting current phonebook from the phone") 165 self.setmode(self.MODEBREW) 166 phone_book=PhoneBook(self) 167 phone_book.read() 168 current_pb=phone_book.get_dict(data) 169 self.setmode(self.MODEMODEM) 170 171 # check and adjust for speeddial changes 172 self.log("Processing speeddial data") 173 for k in pb_book: 174 self._update_speeddial(pb_book[k]) 175 176 # check for deleted entries and delete them 177 self.setmode(self.MODEPHONEBOOK) 178 self.log("Processing deleted entries") 179 180 for k1 in current_pb: 181 s1=current_pb[k1]['serials'][0]['serial1'] 182 found=False 183 for k2 in pb_book: 184 if self._same_serial1(s1, pb_book[k2]): 185 found=True 186 break 187 if found: 188 pb_locs[int(current_pb[k1]['serials'][0]['serial1'])]=True 189 pb_mem[int(current_pb[k1]['serials'][0]['serial2'])]=True 190 else: 191 self.log("Deleted item: "+\ 192 nameparser.getfullname(current_pb[k1]['names'][0])) 193 # delete the entries from data and the phone 194 self.progress(0, 10, "Deleting "+\ 195 nameparser.getfullname(\ 196 current_pb[k1]['names'][0])) 197 self._del_phone_entry(current_pb[k1]) 198 mem_idx, loc_idx = self.__pb_max_speeddials, 1 199 200 # check for new entries & update serials 201 self.log("Processing new & updated entries") 202 serials_update=[] 203 progresscur, progressmax=1,len(pb_book) 204 for k in pb_book: 205 if progresscur>len(self.__phone_entries_range): 206 self.log('Max phone entries exceeded: '+str(progresscur)) 207 break 208 e=pb_book[k] 209 if not self._has_serial1(e): 210 while pb_locs[loc_idx]: 211 loc_idx += 1 212 pb_locs[loc_idx]=True 213 sd=self._get_speeddial(e) 214 if sd: 215 mem_index=sd 216 pb_mem[sd]=True 217 else: 218 while pb_mem[mem_idx]: 219 mem_idx -= 1 220 pb_mem[mem_idx]=True 221 mem_index=mem_idx 222 self._set_speeddial(e, mem_idx) 223 s1={ 'sourcetype': self.serialsname, 224 'sourceuniqueid': data['uniqueserial'], 225 'serial1': `loc_idx`, 226 'serial2': `mem_index` } 227 e['serials'].append(s1) 228 self.log("New entries: Name: "+\ 229 nameparser.getfullname(e['names'][0])+\ 230 ", s1: "+`loc_idx`+", s2: "+`mem_index`) 231 serials_update.append((self._bitpim_serials(e), s1)) 232 self.progress(progresscur, progressmax, "Updating "+\ 233 nameparser.getfullname(e['names'][0])) 234 if not self._write_phone_entry(e, pb_groups, ringtone_index, 235 phone_book): 236 self.log("Failed to save entry: "+\ 237 nameparser.getfullname(e['names'][0])) 238 progresscur += 1 239 240 data["serialupdates"]=serials_update 241 self.log("Done") 242 self.setmode(self.MODEMODEM) 243 return data
244 245 # validate a phonebook entry, return True if good, False otherwise
246 - def __validate_entry(self, pb_entry, pb_groups, ringtone_index):
247 try: 248 # validate name & alias 249 name=nameparser.getfullname(pb_entry['names'][0]).replace('"', '') 250 if len(name)>self.__pb_max_name_len: 251 name=name[:self.__pb_max_name_len] 252 pb_entry['names'][0].setdefault('full', name) 253 if pb_entry['names'][0].has_key('nickname'): 254 name=re.sub('[,"]', '', pb_entry['names'][0]['nickname']) 255 if len(name)>self.__pb_max_name_len: 256 name=name[:self.__pb_max_name_len] 257 if pb_entry['names'][0]['nickname']!=name: 258 pb_entry['names'][0]['nickname']=name 259 # validate numbers 260 has_number_or_email=False 261 if pb_entry.has_key('numbers'): 262 for n in pb_entry['numbers']: 263 num=self.phonize(n['number']) 264 if len(num)>self.__pb_max_number_len: 265 num=num[:self.__pb_max_number_len] 266 if num != n['number']: 267 self.log('Updating number from '+n['number']+' to '+num) 268 n['number']=num 269 try: 270 self._get_number_type(n['type']) 271 except: 272 self.log(n['number']+': setting type to home.') 273 n['type']='home' 274 has_number_or_email=True 275 # validate emails 276 if pb_entry.has_key('emails'): 277 if len(pb_entry['emails'])>self.__pb_max_emails: 278 self.log(name+': Each entry can only have %s emails. The rest will be ignored.'%str(self.__pb_max_emails)) 279 email=pb_entry['emails'][0]['email'].replace('"', '') 280 if len(email)>self.__pb_max_number_len: 281 email=email[:self.__pb_max_number_len] 282 if email!=pb_entry['emails'][0]['email']: 283 pb_entry['emails'][0]['email']=email 284 has_number_or_email=True 285 if not has_number_or_email: 286 self.log(name+': Entry has no numbers or emails') 287 # return False so this entry can be deleted from the dict 288 return False 289 # validate groups 290 found=False 291 if pb_entry.has_key('categories') and len(pb_entry['categories']): 292 pb_cat=pb_entry['categories'][0]['category'] 293 for k in pb_groups: 294 if pb_groups[k]['name']==pb_cat: 295 found=True 296 break 297 if not found: 298 self.log(name+': category set to '+pb_groups[0]['name']) 299 pb_entry['categories']=[{'category': pb_groups[0]['name']}] 300 # validate ringtones 301 found=False 302 if pb_entry.has_key('ringtones') and len(pb_entry['ringtones']): 303 pb_rt=pb_entry['ringtones'][0]['ringtone'] 304 # can only set to builtin-ringtone 305 for k, rt in ringtone_index.items(): 306 if pb_rt==rt['name']: 307 found=True 308 break 309 if not found: 310 rt=ringtone_index[0]['name'] 311 self.log(name+': ringtone set to '+rt) 312 pb_entry['ringtones']=[{'ringtone': rt, 313 'use': 'call' }] 314 # everything's cool 315 return True 316 except: 317 raise
318
319 - def _has_duplicate_speeddial(self, pb_book):
320 b=[False]*(self.__pb_max_speeddials+1) 321 for k in pb_book: 322 try: 323 for k1, kk in enumerate(pb_book[k]['numbers']): 324 sd=kk['speeddial'] 325 if sd and b[sd]: 326 # speed dial is in used, remove this one 327 del pb_book[k]['numbers'][k1]['speeddial'] 328 self.log('speeddial %d exists, deleted'%sd) 329 else: 330 b[sd]=True 331 except: 332 pass 333 return False
334
335 - def _update_speeddial(self, pb_entry):
336 try: 337 s=self._my_serials(pb_entry) 338 s1=int(s['serial2']) 339 sd=self._get_speeddial(pb_entry) 340 if not sd: 341 # speed dial not set, set it to current mem slot 342 self._set_speeddial(pb_entry, s1) 343 elif sd!=s1: 344 # speed dial set to a different slot, mark it 345 self._del_my_serials(pb_entry) 346 except: 347 pass
348
349 - def _get_speeddial(self, pb_entry):
350 n=pb_entry.get('numbers', []) 351 for k in n: 352 try: 353 if k['speeddial']: 354 return k['speeddial'] 355 except: 356 pass 357 return 0
358
359 - def _set_speeddial(self, pb_entry, sd):
360 if not pb_entry.has_key('numbers'): 361 # no numbers key, just return 362 return 363 for k in pb_entry['numbers']: 364 if k.has_key('speeddial'): 365 k['speeddial']=sd 366 return 367 pb_entry['numbers'][0]['speeddial']=sd
368
369 - def _del_phone_entry(self, pb_entry):
370 try: 371 return self.save_phone_entry(self._my_serials(pb_entry)['serial1']) 372 except: 373 return False
374
375 - def _same_serial1(self, s1, pb_entry):
376 for k in pb_entry['serials']: 377 if k['sourcetype']==self.serialsname and k.has_key('serial1'): 378 return k['serial1']==s1 379 return False
380
381 - def _has_serial1(self, pb_entry):
382 for k in pb_entry['serials']: 383 if k['sourcetype']==self.serialsname and k.has_key('serial1'): 384 return True 385 return False
386
387 - def _bitpim_serials(self, pb_entry):
388 for k in pb_entry['serials']: 389 if k['sourcetype']=="bitpim": 390 return k 391 return {}
392
393 - def _del_my_serials(self, pb_entry):
394 for k in range(len(pb_entry['serials'])): 395 if pb_entry['serials'][k]['sourcetype']==self.serialsname: 396 del pb_entry['serials'][k] 397 return
398
399 - def _my_serials(self, pb_entry):
400 for k in pb_entry['serials']: 401 if k['sourcetype']==self.serialsname: 402 return k 403 return {}
404
405 - def _get_number_type(self, type):
406 n=self.__pb_numbers 407 for k in range(len(n)): 408 if n[k].has_key(type): 409 return k, n[k][type] 410 raise common.IntegrityCheckFailed(self.desc, "Invalid Number Type")
411 412
413 - def _write_phone_entry(self, pb_entry, groups, ringtone_index, phone_book):
414 415 # setting up a list to send to the phone, all fields preset to '0' 416 e=['0']*self.__pb_max_entries 417 418 # setting the entry # and memory location # 419 serials=self._my_serials(pb_entry) 420 e[self.__pb_entry]=serials['serial1'] 421 e[self.__pb_mem_loc]=serials['serial2'] 422 423 # groups/categories 424 grp=0 425 try: 426 grp_name=pb_entry['categories'][0]['category'] 427 for k in range(len(groups)): 428 if groups[k]['name']==grp_name: 429 grp=k 430 break 431 432 except: 433 # invalid group or no group specified, default to group 0 434 grp, pb_entry['categories']=0, [{'category': groups[0]['name']}] 435 e[self.__pb_group]=`grp` 436 437 # ringtones 438 e[self.__pb_ringtone]='0' # default to Inactive 439 try: 440 rt=pb_entry['ringtones'][0]['ringtone'] 441 for k, n in ringtone_index.items(): 442 if rt==n['name']: 443 e[self.__pb_ringtone]=`k` 444 break 445 except: 446 pass 447 448 # name & alias 449 e[self.__pb_name]='"'+nameparser.getfullname(pb_entry['names'][0])+'"' 450 nick_name='' 451 try: 452 nick_name=pb_entry['names'][0]['nickname'] 453 except: 454 pass 455 456 e[self.__pb_alias]=nick_name 457 if len(nick_name): 458 e[self.__pb_alias+1]='0' 459 else: 460 e[self.__pb_alias+1]='' 461 462 # numbers & speed dial 463 464 # preset to empty 465 466 for k in range(len(self.__pb_numbers)): 467 for kk in self.__pb_numbers[k]: 468 e[self.__pb_numbers[k][kk]]='' 469 e[self.__pb_numbers[k][kk]+1]='' 470 speed_dial='0' 471 n=pb_entry.get('numbers', []) 472 for k in range(len(n)): 473 try: 474 nk=n[k] 475 kkk, kk=self._get_number_type(nk['type']) 476 except: 477 # invalid type, default to 'home' 478 nk['type']='home' 479 kkk, kk=0, self.__pb_home_num 480 e[kk],e[kk+1]=self.phonize(nk['number']),'0' 481 try: 482 if nk['speeddial']: 483 speed_dial=`kkk` 484 except: 485 pass 486 e[self.__pb_speed_dial]=speed_dial 487 488 # email 489 email='' 490 try: 491 email=pb_entry['emails'][0]['email'] 492 except: 493 pass 494 495 e[self.__pb_email]='"'+email+'"' 496 e[self.__pb_four]='4' 497 for k in self.__pb_blanks: 498 e[k]='' 499 500 e[self.__pb_date_time_stamp]=self.get_time_stamp() 501 502 # final check to determine if this entry has changed. 503 # if it has not then do nothing and just return 504 ee=self.get_phone_entry(int(e[self.__pb_entry]), 505 self.__pb_alias, self.__pb_max_entries) 506 if len(ee)==self.__pb_max_entries: 507 # DSV took the " out, need to put them back in for comparison 508 ee[self.__pb_name]='"'+ee[self.__pb_name]+'"' 509 ee[self.__pb_email]='"'+ee[self.__pb_email]+'"' 510 # set the correct ringtone index 511 ee[self.__pb_ringtone]=str(phone_book.get_ringtone(\ 512 int(e[self.__pb_mem_loc]))) 513 k=self.__pb_max_entries-2 514 if e[0:k]==ee[0:k]: 515 return True 516 return self.save_phone_entry('0,'+','.join(e))
517
518 - def getringtones(self, result):
519 self.setmode(self.MODEBREW) 520 m=FileEntries(self, self.__ringtone_info) 521 rt_info=RingtoneIndex(self).get_download_info() 522 r=m.get_media(result, rt_info) 523 self.setmode(self.MODEMODEM) 524 return r
525
526 - def saveringtones(self, result, merge):
527 self.setmode(self.MODEBREW) 528 m=FileEntries(self, self.__ringtone_info) 529 result['rebootphone']=1 # So we end up back in AT mode 530 r=m.save_media(result, RingtoneIndex(self).get_download_info()) 531 self.setmode(self.MODEMODEM) 532 return r
533
534 - def getwallpapers(self, result):
535 self.setmode(self.MODEBREW) 536 m=FileEntries(self, self.__wallpaper_info) 537 img_info=ImageIndex(self).get_download_info() 538 r=m.get_media(result, img_info) 539 self.setmode(self.MODEMODEM) 540 return r
541
542 - def savewallpapers(self, result, merge):
543 self.setmode(self.MODEBREW) 544 m=FileEntries(self, self.__wallpaper_info) 545 r=m.save_media(result, ImageIndex(self).get_download_info()) 546 result['rebootphone']=1 547 self.setmode(self.MODEMODEM) 548 return r
549 550 getmemo=parent_phone._getmemo 551 savememo=parent_phone._savememo 552 553 gettodo=parent_phone._gettodo 554 savetodo=parent_phone._savetodo 555 556 getsms=parent_phone._getsms 557 savesms=parent_phone._savesms 558 559 getphoneinfo=parent_phone._getphoneinfo 560 561 getmedia=None
562 563 #-------------------------------------------------------------------------------
564 -class Profile(com_samsung.Profile):
565 566 serialsname='scha650' 567 568 WALLPAPER_WIDTH=128 569 WALLPAPER_HEIGHT=160 570 MAX_WALLPAPER_BASENAME_LENGTH=17 571 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_*[]=<>;|?:% ." 572 WALLPAPER_CONVERT_FORMAT="png" 573 574 MAX_RINGTONE_BASENAME_LENGTH=17 575 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_*[]=<>;|?:% ." 576 RINGTONE_LIMITS= { 577 'MAXSIZE': 30000 578 } 579 # use for auto-detection 580 phone_manufacturer='SAMSUNG ELECTRONICS' 581 phone_model='SCH-A650/163' 582
583 - def __init__(self):
585 586 _supportedsyncs=( 587 ('phonebook', 'read', None), # all phonebook reading 588 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 589 ('calendar', 'read', None), # all calendar reading 590 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 591 ('ringtone', 'read', None), # all ringtone reading 592 ('ringtone', 'write', 'OVERWRITE'), 593 ('wallpaper', 'read', None), # all wallpaper reading 594 ('wallpaper', 'write', 'OVERWRITE'), 595 ('memo', 'read', None), # all memo list reading DJP 596 ('memo', 'write', 'OVERWRITE'), # all memo list writing DJP 597 ('todo', 'read', None), # all todo list reading DJP 598 ('todo', 'write', 'OVERWRITE'), # all todo list writing DJP 599 ('sms', 'read', None), # all SMS list reading DJP 600 ) 601 602 if __debug__: 603 _supportedsyncs+=(('sms', 'write', 'OVERWRITE'),) 604
605 - def convertphonebooktophone(self, helper, data):
606 return data
607 608 __audio_ext={ 'MIDI': 'mid', 'QCP': 'qcp', 'PMD': 'pmd' }
609 - def QueryAudio(self, origin, currentextension, afi):
610 # we don't modify any of these 611 if afi.format in ("MIDI", "QCP", "PMD"): 612 for k,n in self.RINGTONE_LIMITS.items(): 613 setattr(afi, k, n) 614 return currentextension, afi 615 d=self.RINGTONE_LIMITS.copy() 616 d['format']='QCP' 617 return ('qcp', fileinfo.AudioFileInfo(afi, **d))
618 619 # fill in your own image origins using these 620 # use this temorarily until the stock one is finalized 621 imageorigins={} 622 imageorigins.update(common.getkv(com_samsung.Profile.stockimageorigins, "images")) 623 624 imagetargets={} 625 imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "wallpaper", 626 {'width': 128, 'height': 128, 'format': "PNG"})) 627 imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "fullscreen", 628 {'width': 128, 'height': 160, 'format': "PNG"})) 629
630 - def GetImageOrigins(self):
631 # Note: only return origins that you can write back to the phone 632 return self.imageorigins
633
634 - def GetTargetsForImageOrigin(self, origin):
635 if origin=='images': 636 return self.imagetargets
637 638 #-------------------------------------------------------------------------------
639 -class FileEntries:
640 - def __init__(self, phone, info):
641 self.__phone=phone 642 self.__file_type, self.__index_type, self.__origin, self.__path, self.__max_file_len, self.__max_file_count=info
643
644 - def get_media(self, result, download_info=None):
645 self.__phone.log('Getting media for type '+self.__file_type) 646 if download_info is None: 647 return self.__get_media_by_dir(result) 648 else: 649 return self.__get_media_by_index(result, download_info)
650
651 - def __get_media_by_dir(self, result):
652 media=result.get(self.__file_type, {}) 653 idx=result.get(self.__index_type, {}) 654 file_cnt, idx_k=0, len(idx) 655 path_len=len(self.__path)+1 656 try: 657 file_list=self.__phone.getfilesystem(self.__path, 0) 658 for k in file_list: 659 try: 660 index=k[path_len:] 661 # print k, index 662 media[index]=self.__phone.getfilecontents(k, True) 663 idx[idx_k]={ 'name': index, 'origin': self.__origin } 664 idx_k+=1 665 file_cnt += 1 666 except: 667 self.__phone.log('Failed to read file '+k) 668 except: 669 self.__phone.log('Failed to read dir '+self.__path) 670 result[self.__file_type]=media 671 result[self.__index_type]=idx 672 if file_cnt > self.__max_file_count: 673 self.__phone.log('This phone only supports %d %s. %d %s read, weird things may happen.' % \ 674 (self.__max_file_count, self.__file_type, 675 file_cnt, self.__file_type)) 676 return result
677
678 - def __get_media_by_index(self, result, rt_info):
679 media=result.get(self.__file_type, {}) 680 media_index=result.get(self.__index_type, {}) 681 for k, m in media_index.items(): 682 file_key=m.get('name', None) 683 file_name=rt_info.get(file_key, None) 684 if file_key is not None and file_name is not None: 685 try : 686 media[file_key]=self.__phone.getfilecontents(file_name, True) 687 except: 688 self.__phone.log('Failed to read file '+file_name) 689 result[self.__file_type]=media 690 return result
691
692 - def save_media(self, result, dl_info):
693 self.__phone.log('Saving media for type '+self.__file_type) 694 media, idx=result[self.__file_type], result[self.__index_type] 695 # check for files selected for deletion 696 media_names=[media[k]['name'] for k in media] 697 dl_info_keys=dl_info.keys() 698 deleted_keys=[k for k in dl_info_keys if k not in media_names] 699 new_keys=[k for k in media if media[k]['name'] not in dl_info_keys] 700 # deleting files 701 for k in deleted_keys: 702 file_name=dl_info[k] 703 self.__phone.log('Deleting file: '+file_name) 704 try: 705 self.__phone.rmfile(file_name) 706 except: 707 self.__phone.log('Failed to delete file: '+file_name) 708 # writing new files 709 # make sure dir exists to write new files 710 if len(new_keys): 711 try: 712 self.__phone.mkdirs(self.__path) 713 except: 714 pass 715 file_count=0 716 for k in new_keys: 717 n=media[k] 718 origin=n.get('origin', None) 719 if origin is not None and origin != self.__origin: 720 continue 721 if len(n['name']) > self.__max_file_len: 722 self.__phone.log('%s %s name is too long and not sent to phone'% \ 723 (self.__file_type, n['name'])) 724 continue 725 file_count+=1 726 if file_count>self.__max_file_count: 727 # max # of files reached, bailing out 728 self.__phone.log('This phone only supports %d %s. Save operation stopped.'%\ 729 (self.__max_file_count, self.__file_type)) 730 break 731 file_name=self.__path+'/'+n['name'] 732 if self.__origin=='images': 733 # try to optimize it if it's a png image file 734 file_contents=conversions.convertto8bitpng_joe(n['data']) 735 else: 736 file_contents=n['data'] 737 self.__phone.log('Writing file: '+file_name) 738 try: 739 self.__phone.writefile(file_name, file_contents) 740 except: 741 self.__phone.log('Failed to write file: '+file_name) 742 media[k]['origin']=self.__origin 743 744 return result
745 746 #-------------------------------------------------------------------------------
747 -class RingtoneIndex:
748 __builtin_ringtones=( 'Inactive', 749 'Bell 1', 'Bell 2', 'Bell 3', 'Bell 4', 'Bell 5', 750 'Melody 1', 'Melody 2', 'Melody 3', 'Melody 4', 'Melody 5', 751 'Melody 6', 'Melody 7', 'Melody 8', 'Melody 9', 'Melody 10') 752
753 - def __init__(self, phone):
754 self.__phone=phone
755
756 - def get_builtin_index(self):
757 r={} 758 for k, n in enumerate(self.__builtin_ringtones): 759 r[k]={ 'name': n, 'origin': 'builtin' } 760 return r
761
762 - def get_download_index(self):
763 r={} 764 try: 765 rt_idx=self.__phone.protocolclass.ringtones() 766 buf=prototypes.buffer(self.__phone.getfilecontents( \ 767 self.__phone.protocolclass.ringtone_index_file_name)) 768 rt_idx.readfrombuffer(buf, logtitle="Read ringtone index file") 769 idx=len(self.__builtin_ringtones) 770 l=len(self.__phone.protocolclass.ringtone_file_path)+1 771 for i in range(self.__phone.protocolclass.max_ringtone_entries): 772 e=rt_idx.entry[i] 773 if e.name_len: 774 r[idx+i]={ 'name': e.file_name[l:e.file_name_len], 775 'origin': 'ringtone' } 776 except: 777 pass 778 return r
779
780 - def get(self):
781 r=self.get_builtin_index() 782 r.update(self.get_download_index()) 783 return r
784
785 - def get_download_info(self):
786 r={} 787 try: 788 rt_idx=self.__phone.protocolclass.ringtones() 789 buf=prototypes.buffer(self.__phone.getfilecontents( \ 790 self.__phone.protocolclass.ringtone_index_file_name)) 791 rt_idx.readfrombuffer(buf, logtitle="Read ringtone download index") 792 l=len(self.__phone.protocolclass.ringtone_file_path)+1 793 for i in range(self.__phone.protocolclass.max_ringtone_entries): 794 e=rt_idx.entry[i] 795 if e.name_len: 796 r[e.file_name[l:e.file_name_len]]=e.file_name[:e.file_name_len] 797 except: 798 pass 799 return r
800 801 #-------------------------------------------------------------------------------
802 -class ImageIndex:
803 __builtin_images=( 'Clock1', 'Dual Clock', 'Calendar', 'Aquarium', 804 'Landscape', 'Water Drop' ) 805
806 - def __init__(self, phone):
807 self.__phone=phone
808
809 - def get_builtin_index(self):
810 r={} 811 for k, n in enumerate(self.__builtin_images): 812 r[k]={ 'name': n, 'origin': 'builtin' } 813 return r
814
815 - def get_download_index(self):
816 r={} 817 try: 818 img_idx=self.__phone.protocolclass.images() 819 buf=prototypes.buffer(self.__phone.getfilecontents( \ 820 self.__phone.protocolclass.image_index_file_name)) 821 img_idx.readfrombuffer(buf, logtitle="Read image download index") 822 idx=len(self.__builtin_images) 823 l=len(self.__phone.protocolclass.image_file_path)+1 824 for i in range(self.__phone.protocolclass.max_image_entries): 825 e=img_idx.entry[i] 826 if e.name_len: 827 r[idx+i]={ 'name': e.file_name[l:e.file_name_len], 828 'origin': 'images' } 829 except: 830 raise 831 return r
832
833 - def get(self):
834 r=self.get_builtin_index() 835 r.update(self.get_download_index()) 836 return r
837
838 - def get_download_info(self):
839 r={} 840 try: 841 img_idx=self.__phone.protocolclass.images() 842 buf=prototypes.buffer(self.__phone.getfilecontents( \ 843 self.__phone.protocolclass.image_index_file_name)) 844 img_idx.readfrombuffer(buf, logtitle="Read image download index") 845 l=len(self.__phone.protocolclass.image_file_path)+1 846 for i in range(self.__phone.protocolclass.max_image_entries): 847 e=img_idx.entry[i] 848 if e.name_len: 849 r[e.file_name[l:e.file_name_len]]=e.file_name[:e.file_name_len] 850 except: 851 pass 852 return r
853 854 #-------------------------------------------------------------------------------
855 -class PhoneNumbers:
856 - def __init__(self, phone):
857 self.__phone=phone 858 self.__numbers=None
859
860 - def read(self):
861 try: 862 buf=prototypes.buffer(self.__phone.getfilecontents(\ 863 self.__phone.protocolclass.number_file_name)) 864 self.__numbers=self.__phone.protocolclass.numbers() 865 self.__numbers.readfrombuffer(buf, logtitle="Read number file "+self.__phone.protocolclass.number_file_name) 866 except: 867 self.__phone.log('Failed to read numbers file') 868 self.__numbers=[]
869
870 - def get(self, index, default=None):
871 if index>=self.__phone.protocolclass.max_number_entries: 872 return default 873 e=self.__numbers.entry[index] 874 if e.valid: 875 return e.name[:e.length] 876 return default
877 878 #-------------------------------------------------------------------------------
879 -class PhoneBook:
880 881 __pb_numbers= ({'home': 'home_num_index' }, 882 {'office': 'office_num_index' }, 883 {'cell': 'mobile_num_index' }, 884 {'pager': 'pager_num_index' }, 885 {'fax': 'fax_num_index' })
886 - def __init__(self, phone):
887 self.__phone=phone 888 self.__pb=None 889 self.__numbers=None 890 self.__groups=None 891 self.__rt_index=None 892 self.__id=None 893 self.__slots=None
894
895 - def read(self):
896 try: 897 buf=prototypes.buffer(self.__phone.getfilecontents(\ 898 self.__phone.protocolclass.pb_file_name)) 899 self.__pb=self.__phone.protocolclass.pbbook() 900 self.__pb.readfrombuffer(buf, logtitle="Read "+self.__phone.protocolclass.pb_file_name) 901 except: 902 self.__pb=[] 903 self.__phone.log('Failed to read phonebook')
904
905 - def get_ringtone(self, index):
906 """ 907 Return the ringtone index of this entry. 908 """ 909 if self.__pb is None: 910 self.read() 911 rt=self.__pb.entry[index].ringer_type 912 if rt: 913 rt-=71 914 return rt
915
916 - def __extract_entry(self, e, pb_cnt, mem_index):
917 res={} 918 # serials 919 res['serials']=[ {'sourcetype': self.__phone.serialsname, 920 'sourceuniqueid': self.__id, 921 'serial1': `pb_cnt`, 922 'serial2': `mem_index` }] 923 # only one name 924 res['names']=[ {'full': e.name } ] 925 if e.alias_num_index: 926 res['names'][0]['nickname']=self.__numbers.get(e.alias_num_index, '') 927 928 # only one category 929 res['categories']=[ {'category': self.__groups[e.group_num]['name'] } ] 930 931 # emails 932 if e.email_index: 933 res['emails']=[ { 'email': self.__numbers.get(e.email_index, '') } ] 934 935 # urls: N/A 936 # private: N/A 937 # memos: N/A 938 # wallpapers: N/A 939 940 # ringtones 941 rt=e.ringer_type 942 if rt: 943 rt-=71 944 res['ringtones']=[ { 'ringtone': self.__rt_index[rt]['name'], 945 'use': 'call' } ] 946 947 # numbers 948 speed_dial=e.speed_dial_index 949 res['numbers']=[] 950 for k, a in enumerate(self.__pb_numbers): 951 for key, attr in a.items(): 952 idx=getattr(e, attr, 0) 953 if idx: 954 num=self.__numbers.get(idx, '') 955 if idx==speed_dial: 956 res['numbers'].append({ 'number': num, 957 'type': key, 958 'speeddial': mem_index } ) 959 else: 960 res['numbers'].append({ 'number': num, 961 'type': key }) 962 # done 963 return res
964
965 - def get_dict(self, result):
966 # read the phonebook if not already done so 967 if self.__pb is None: 968 self.read() 969 # read the phone numbers 970 if self.__numbers is None: 971 self.__numbers=PhoneNumbers(self.__phone) 972 self.__numbers.read() 973 # read the slot entries 974 if self.__slots is None: 975 self.__slots=PBSlot(self.__phone) 976 self.__slots.read() 977 # get fundamental info 978 self.__groups=result.get('groups', {}) 979 self.__rt_index=result.get('ringtone-index', {}) 980 self.__id=result.get('uniqueserial', '') 981 # loop through the phonebook and extract each entry 982 r={} 983 for pb_cnt, i in enumerate(self.__slots): 984 if i==0: 985 # empty slot 986 continue 987 e=self.__pb.entry[i] 988 if e.mem_index: 989 if i != e.mem_index: 990 self.__phone.log('i: %d, mem_index: %d'%(i, e.mem_index)) 991 r[pb_cnt]=self.__extract_entry(e, pb_cnt, i) 992 return r
993 994 #-------------------------------------------------------------------------------
995 -class PBSlot:
996 """ Class to handle Phonebook entry slot -> memory slot """
997 - def __init__(self, phone):
998 self.__phone=phone 999 self.__slots=None
1000
1001 - def read(self):
1002 try: 1003 buf=prototypes.buffer(self.__phone.getfilecontents(\ 1004 self.__phone.protocolclass.slot_file_name)) 1005 self.__slots=self.__phone.protocolclass.pbslots() 1006 self.__slots.readfrombuffer(buf, logtitle="Read slots file "+self.__phone.protocolclass.slot_file_name) 1007 except: 1008 self.__slots=[] 1009 self.__phone.log('Failed to read slot file')
1010
1011 - def __getitem__(self, key):
1012 if type(key) is not int: 1013 raise KeyError 1014 if key<0 or key>=self.__phone.protocolclass.max_pb_slots: 1015 raise IndexError 1016 if self.__slots is None: 1017 self.read() 1018 return self.__slots.slot[key].pbbook_index
1019