0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2004 Joe Pham <djpham@netzero.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_samsungscha650.py 4365 2007-08-17 21:11:59Z djpham $ 0009 0010 0011 """Communicate with a Samsung SCH-A650""" 0012 0013 # lib modules 0014 import copy 0015 import re 0016 import sha 0017 0018 # my modules 0019 0020 import common 0021 import commport 0022 import com_brew 0023 import com_samsung 0024 import com_phone 0025 import conversions 0026 import fileinfo 0027 import memo 0028 import nameparser 0029 import p_samsungscha650 0030 import prototypes 0031 0032 import call_history 0033 0034 #------------------------------------------------------------------------------- 0035 class Phone(com_samsung.Phone): 0036 0037 "Talk to the Samsung SCH-A650 Cell Phone" 0038 0039 desc="SCH-A650" 0040 serialsname='scha650' 0041 protocolclass=p_samsungscha650 0042 parent_phone=com_samsung.Phone 0043 0044 __groups_range=xrange(5) 0045 __phone_entries_range=xrange(1,501) 0046 __pb_max_entries=25 0047 __pb_max_speeddials=500 0048 __pb_entry=0 0049 __pb_mem_loc=1 0050 __pb_group=2 0051 __pb_ringtone=3 0052 __pb_name=4 0053 __pb_speed_dial=5 0054 __pb_home_num=7 0055 __pb_office_num=9 0056 __pb_mobile_num=11 0057 __pb_pager_num=13 0058 __pb_fax_num=15 0059 __pb_alias=17 0060 __pb_email=21 0061 __pb_four=22 0062 __pb_blanks=(19, 20) 0063 __pb_date_time_stamp=24 0064 __pb_numbers= ({'home': __pb_home_num}, 0065 {'office': __pb_office_num}, 0066 {'cell': __pb_mobile_num}, 0067 {'pager': __pb_pager_num}, 0068 {'fax': __pb_fax_num}) 0069 __pb_max_name_len=22 0070 __pb_max_number_len=32 0071 __pb_max_emails=1 0072 0073 # 'type name', 'type index name', 'origin', 'dir path', 'max file name length', 'max file name count' 0074 __ringtone_info=('ringtone', 'ringtone-index', 'ringtone', 'user/sound/ringer', 17, 20) 0075 __wallpaper_info=('wallpapers', 'wallpaper-index', 'images', 'nvm/brew/shared', 17, 10) 0076 0077 def __init__(self, logtarget, commport): 0078 0079 "Calls all the constructors and sets initial modes" 0080 com_samsung.Phone.__init__(self, logtarget, commport) 0081 self.mode=self.MODENONE 0082 0083 def getfundamentals(self, results): 0084 0085 """Gets information fundamental to interopating with the phone and UI. 0086 0087 Currently this is: 0088 0089 - 'uniqueserial' a unique serial number representing the phone 0090 - 'groups' the phonebook groups 0091 - 'wallpaper-index' map index numbers to names 0092 - 'ringtone-index' map index numbers to ringtone names 0093 0094 This method is called before we read the phonebook data or before we 0095 write phonebook data. 0096 """ 0097 0098 self.setmode(self.MODEPHONEBOOK) 0099 0100 # use a hash of ESN and other stuff (being paranoid) 0101 0102 self.log("Retrieving fundamental phone information") 0103 self.log("Reading phone serial number") 0104 results['uniqueserial']=sha.new(self.get_esn()).hexdigest() 0105 0106 # now read groups 0107 0108 self.log("Reading group information") 0109 g=self.get_groups(self.__groups_range) 0110 groups={} 0111 for i, g_i in enumerate(g): 0112 if len(g_i): 0113 groups[i]={ 'name': g_i } 0114 results['groups']=groups 0115 0116 # getting rintone-index 0117 self.setmode(self.MODEBREW) 0118 rt_index=RingtoneIndex(self) 0119 results['ringtone-index']=rt_index.get() 0120 0121 # getting wallpaper-index 0122 img_index=ImageIndex(self) 0123 results['wallpaper-index']=img_index.get() 0124 0125 self.setmode(self.MODEMODEM) 0126 self.log("Fundamentals retrieved") 0127 0128 return results 0129 0130 def getphonebook(self,result): 0131 """Reads the phonebook data. The L{getfundamentals} information will 0132 already be in result.""" 0133 self.setmode(self.MODEBREW) 0134 pb=PhoneBook(self) 0135 pb.read() 0136 pb_book=pb.get_dict(result) 0137 result['phonebook']=pb_book 0138 self.setmode(self.MODEMODEM) 0139 return pb_book 0140 0141 def savephonebook(self, data): 0142 "Saves out the phonebook" 0143 0144 pb_book=data['phonebook'] 0145 pb_groups=data['groups'] 0146 ringtone_index=data.get('ringtone-index', {}) 0147 self.log('Validating phonebook entries.') 0148 del_entries=[] 0149 for k in pb_book: 0150 if not self.__validate_entry(pb_book[k], pb_groups, ringtone_index): 0151 self.log('Invalid entry, entry will be not be sent.') 0152 del_entries.append(k) 0153 for k in del_entries: 0154 self.log('Deleting entry '+\ 0155 nameparser.getfullname(pb_book[k]['names'][0])) 0156 del pb_book[k] 0157 self._has_duplicate_speeddial(pb_book) 0158 self.log('All entries validated') 0159 0160 pb_locs=[False]*(len(self.__phone_entries_range)+1) 0161 pb_mem=[False]*len(pb_locs) 0162 0163 # get existing phonebook from the phone 0164 self.log("Getting current phonebook from the phone") 0165 self.setmode(self.MODEBREW) 0166 phone_book=PhoneBook(self) 0167 phone_book.read() 0168 current_pb=phone_book.get_dict(data) 0169 self.setmode(self.MODEMODEM) 0170 0171 # check and adjust for speeddial changes 0172 self.log("Processing speeddial data") 0173 for k in pb_book: 0174 self._update_speeddial(pb_book[k]) 0175 0176 # check for deleted entries and delete them 0177 self.setmode(self.MODEPHONEBOOK) 0178 self.log("Processing deleted entries") 0179 0180 for k1 in current_pb: 0181 s1=current_pb[k1]['serials'][0]['serial1'] 0182 found=False 0183 for k2 in pb_book: 0184 if self._same_serial1(s1, pb_book[k2]): 0185 found=True 0186 break 0187 if found: 0188 pb_locs[int(current_pb[k1]['serials'][0]['serial1'])]=True 0189 pb_mem[int(current_pb[k1]['serials'][0]['serial2'])]=True 0190 else: 0191 self.log("Deleted item: "+\ 0192 nameparser.getfullname(current_pb[k1]['names'][0])) 0193 # delete the entries from data and the phone 0194 self.progress(0, 10, "Deleting "+\ 0195 nameparser.getfullname(\ 0196 current_pb[k1]['names'][0])) 0197 self._del_phone_entry(current_pb[k1]) 0198 mem_idx, loc_idx = self.__pb_max_speeddials, 1 0199 0200 # check for new entries & update serials 0201 self.log("Processing new & updated entries") 0202 serials_update=[] 0203 progresscur, progressmax=1,len(pb_book) 0204 for k in pb_book: 0205 if progresscur>len(self.__phone_entries_range): 0206 self.log('Max phone entries exceeded: '+str(progresscur)) 0207 break 0208 e=pb_book[k] 0209 if not self._has_serial1(e): 0210 while pb_locs[loc_idx]: 0211 loc_idx += 1 0212 pb_locs[loc_idx]=True 0213 sd=self._get_speeddial(e) 0214 if sd: 0215 mem_index=sd 0216 pb_mem[sd]=True 0217 else: 0218 while pb_mem[mem_idx]: 0219 mem_idx -= 1 0220 pb_mem[mem_idx]=True 0221 mem_index=mem_idx 0222 self._set_speeddial(e, mem_idx) 0223 s1={ 'sourcetype': self.serialsname, 0224 'sourceuniqueid': data['uniqueserial'], 0225 'serial1': `loc_idx`, 0226 'serial2': `mem_index` } 0227 e['serials'].append(s1) 0228 self.log("New entries: Name: "+\ 0229 nameparser.getfullname(e['names'][0])+\ 0230 ", s1: "+`loc_idx`+", s2: "+`mem_index`) 0231 serials_update.append((self._bitpim_serials(e), s1)) 0232 self.progress(progresscur, progressmax, "Updating "+\ 0233 nameparser.getfullname(e['names'][0])) 0234 if not self._write_phone_entry(e, pb_groups, ringtone_index, 0235 phone_book): 0236 self.log("Failed to save entry: "+\ 0237 nameparser.getfullname(e['names'][0])) 0238 progresscur += 1 0239 0240 data["serialupdates"]=serials_update 0241 self.log("Done") 0242 self.setmode(self.MODEMODEM) 0243 return data 0244 0245 # validate a phonebook entry, return True if good, False otherwise 0246 def __validate_entry(self, pb_entry, pb_groups, ringtone_index): 0247 try: 0248 # validate name & alias 0249 name=nameparser.getfullname(pb_entry['names'][0]).replace('"', '') 0250 if len(name)>self.__pb_max_name_len: 0251 name=name[:self.__pb_max_name_len] 0252 pb_entry['names'][0].setdefault('full', name) 0253 if pb_entry['names'][0].has_key('nickname'): 0254 name=re.sub('[,"]', '', pb_entry['names'][0]['nickname']) 0255 if len(name)>self.__pb_max_name_len: 0256 name=name[:self.__pb_max_name_len] 0257 if pb_entry['names'][0]['nickname']!=name: 0258 pb_entry['names'][0]['nickname']=name 0259 # validate numbers 0260 has_number_or_email=False 0261 if pb_entry.has_key('numbers'): 0262 for n in pb_entry['numbers']: 0263 num=self.phonize(n['number']) 0264 if len(num)>self.__pb_max_number_len: 0265 num=num[:self.__pb_max_number_len] 0266 if num != n['number']: 0267 self.log('Updating number from '+n['number']+' to '+num) 0268 n['number']=num 0269 try: 0270 self._get_number_type(n['type']) 0271 except: 0272 self.log(n['number']+': setting type to home.') 0273 n['type']='home' 0274 has_number_or_email=True 0275 # validate emails 0276 if pb_entry.has_key('emails'): 0277 if len(pb_entry['emails'])>self.__pb_max_emails: 0278 self.log(name+': Each entry can only have %s emails. The rest will be ignored.'%str(self.__pb_max_emails)) 0279 email=pb_entry['emails'][0]['email'].replace('"', '') 0280 if len(email)>self.__pb_max_number_len: 0281 email=email[:self.__pb_max_number_len] 0282 if email!=pb_entry['emails'][0]['email']: 0283 pb_entry['emails'][0]['email']=email 0284 has_number_or_email=True 0285 if not has_number_or_email: 0286 self.log(name+': Entry has no numbers or emails') 0287 # return False so this entry can be deleted from the dict 0288 return False 0289 # validate groups 0290 found=False 0291 if pb_entry.has_key('categories') and len(pb_entry['categories']): 0292 pb_cat=pb_entry['categories'][0]['category'] 0293 for k in pb_groups: 0294 if pb_groups[k]['name']==pb_cat: 0295 found=True 0296 break 0297 if not found: 0298 self.log(name+': category set to '+pb_groups[0]['name']) 0299 pb_entry['categories']=[{'category': pb_groups[0]['name']}] 0300 # validate ringtones 0301 found=False 0302 if pb_entry.has_key('ringtones') and len(pb_entry['ringtones']): 0303 pb_rt=pb_entry['ringtones'][0]['ringtone'] 0304 # can only set to builtin-ringtone 0305 for k, rt in ringtone_index.items(): 0306 if pb_rt==rt['name']: 0307 found=True 0308 break 0309 if not found: 0310 rt=ringtone_index[0]['name'] 0311 self.log(name+': ringtone set to '+rt) 0312 pb_entry['ringtones']=[{'ringtone': rt, 0313 'use': 'call' }] 0314 # everything's cool 0315 return True 0316 except: 0317 raise 0318 0319 def _has_duplicate_speeddial(self, pb_book): 0320 b=[False]*(self.__pb_max_speeddials+1) 0321 for k in pb_book: 0322 try: 0323 for k1, kk in enumerate(pb_book[k]['numbers']): 0324 sd=kk['speeddial'] 0325 if sd and b[sd]: 0326 # speed dial is in used, remove this one 0327 del pb_book[k]['numbers'][k1]['speeddial'] 0328 self.log('speeddial %d exists, deleted'%sd) 0329 else: 0330 b[sd]=True 0331 except: 0332 pass 0333 return False 0334 0335 def _update_speeddial(self, pb_entry): 0336 try: 0337 s=self._my_serials(pb_entry) 0338 s1=int(s['serial2']) 0339 sd=self._get_speeddial(pb_entry) 0340 if not sd: 0341 # speed dial not set, set it to current mem slot 0342 self._set_speeddial(pb_entry, s1) 0343 elif sd!=s1: 0344 # speed dial set to a different slot, mark it 0345 self._del_my_serials(pb_entry) 0346 except: 0347 pass 0348 0349 def _get_speeddial(self, pb_entry): 0350 n=pb_entry.get('numbers', []) 0351 for k in n: 0352 try: 0353 if k['speeddial']: 0354 return k['speeddial'] 0355 except: 0356 pass 0357 return 0 0358 0359 def _set_speeddial(self, pb_entry, sd): 0360 if not pb_entry.has_key('numbers'): 0361 # no numbers key, just return 0362 return 0363 for k in pb_entry['numbers']: 0364 if k.has_key('speeddial'): 0365 k['speeddial']=sd 0366 return 0367 pb_entry['numbers'][0]['speeddial']=sd 0368 0369 def _del_phone_entry(self, pb_entry): 0370 try: 0371 return self.save_phone_entry(self._my_serials(pb_entry)['serial1']) 0372 except: 0373 return False 0374 0375 def _same_serial1(self, s1, pb_entry): 0376 for k in pb_entry['serials']: 0377 if k['sourcetype']==self.serialsname and k.has_key('serial1'): 0378 return k['serial1']==s1 0379 return False 0380 0381 def _has_serial1(self, pb_entry): 0382 for k in pb_entry['serials']: 0383 if k['sourcetype']==self.serialsname and k.has_key('serial1'): 0384 return True 0385 return False 0386 0387 def _bitpim_serials(self, pb_entry): 0388 for k in pb_entry['serials']: 0389 if k['sourcetype']=="bitpim": 0390 return k 0391 return {} 0392 0393 def _del_my_serials(self, pb_entry): 0394 for k in range(len(pb_entry['serials'])): 0395 if pb_entry['serials'][k]['sourcetype']==self.serialsname: 0396 del pb_entry['serials'][k] 0397 return 0398 0399 def _my_serials(self, pb_entry): 0400 for k in pb_entry['serials']: 0401 if k['sourcetype']==self.serialsname: 0402 return k 0403 return {} 0404 0405 def _get_number_type(self, type): 0406 n=self.__pb_numbers 0407 for k in range(len(n)): 0408 if n[k].has_key(type): 0409 return k, n[k][type] 0410 raise common.IntegrityCheckFailed(self.desc, "Invalid Number Type") 0411 0412 0413 def _write_phone_entry(self, pb_entry, groups, ringtone_index, phone_book): 0414 0415 # setting up a list to send to the phone, all fields preset to '0' 0416 e=['0']*self.__pb_max_entries 0417 0418 # setting the entry # and memory location # 0419 serials=self._my_serials(pb_entry) 0420 e[self.__pb_entry]=serials['serial1'] 0421 e[self.__pb_mem_loc]=serials['serial2'] 0422 0423 # groups/categories 0424 grp=0 0425 try: 0426 grp_name=pb_entry['categories'][0]['category'] 0427 for k in range(len(groups)): 0428 if groups[k]['name']==grp_name: 0429 grp=k 0430 break 0431 0432 except: 0433 # invalid group or no group specified, default to group 0 0434 grp, pb_entry['categories']=0, [{'category': groups[0]['name']}] 0435 e[self.__pb_group]=`grp` 0436 0437 # ringtones 0438 e[self.__pb_ringtone]='0' # default to Inactive 0439 try: 0440 rt=pb_entry['ringtones'][0]['ringtone'] 0441 for k, n in ringtone_index.items(): 0442 if rt==n['name']: 0443 e[self.__pb_ringtone]=`k` 0444 break 0445 except: 0446 pass 0447 0448 # name & alias 0449 e[self.__pb_name]='"'+nameparser.getfullname(pb_entry['names'][0])+'"' 0450 nick_name='' 0451 try: 0452 nick_name=pb_entry['names'][0]['nickname'] 0453 except: 0454 pass 0455 0456 e[self.__pb_alias]=nick_name 0457 if len(nick_name): 0458 e[self.__pb_alias+1]='0' 0459 else: 0460 e[self.__pb_alias+1]='' 0461 0462 # numbers & speed dial 0463 0464 # preset to empty 0465 0466 for k in range(len(self.__pb_numbers)): 0467 for kk in self.__pb_numbers[k]: 0468 e[self.__pb_numbers[k][kk]]='' 0469 e[self.__pb_numbers[k][kk]+1]='' 0470 speed_dial='0' 0471 n=pb_entry.get('numbers', []) 0472 for k in range(len(n)): 0473 try: 0474 nk=n[k] 0475 kkk, kk=self._get_number_type(nk['type']) 0476 except: 0477 # invalid type, default to 'home' 0478 nk['type']='home' 0479 kkk, kk=0, self.__pb_home_num 0480 e[kk],e[kk+1]=self.phonize(nk['number']),'0' 0481 try: 0482 if nk['speeddial']: 0483 speed_dial=`kkk` 0484 except: 0485 pass 0486 e[self.__pb_speed_dial]=speed_dial 0487 0488 # email 0489 email='' 0490 try: 0491 email=pb_entry['emails'][0]['email'] 0492 except: 0493 pass 0494 0495 e[self.__pb_email]='"'+email+'"' 0496 e[self.__pb_four]='4' 0497 for k in self.__pb_blanks: 0498 e[k]='' 0499 0500 e[self.__pb_date_time_stamp]=self.get_time_stamp() 0501 0502 # final check to determine if this entry has changed. 0503 # if it has not then do nothing and just return 0504 ee=self.get_phone_entry(int(e[self.__pb_entry]), 0505 self.__pb_alias, self.__pb_max_entries) 0506 if len(ee)==self.__pb_max_entries: 0507 # DSV took the " out, need to put them back in for comparison 0508 ee[self.__pb_name]='"'+ee[self.__pb_name]+'"' 0509 ee[self.__pb_email]='"'+ee[self.__pb_email]+'"' 0510 # set the correct ringtone index 0511 ee[self.__pb_ringtone]=str(phone_book.get_ringtone(\ 0512 int(e[self.__pb_mem_loc]))) 0513 k=self.__pb_max_entries-2 0514 if e[0:k]==ee[0:k]: 0515 return True 0516 return self.save_phone_entry('0,'+','.join(e)) 0517 0518 def getringtones(self, result): 0519 self.setmode(self.MODEBREW) 0520 m=FileEntries(self, self.__ringtone_info) 0521 rt_info=RingtoneIndex(self).get_download_info() 0522 r=m.get_media(result, rt_info) 0523 self.setmode(self.MODEMODEM) 0524 return r 0525 0526 def saveringtones(self, result, merge): 0527 self.setmode(self.MODEBREW) 0528 m=FileEntries(self, self.__ringtone_info) 0529 result['rebootphone']=1 # So we end up back in AT mode 0530 r=m.save_media(result, RingtoneIndex(self).get_download_info()) 0531 self.setmode(self.MODEMODEM) 0532 return r 0533 0534 def getwallpapers(self, result): 0535 self.setmode(self.MODEBREW) 0536 m=FileEntries(self, self.__wallpaper_info) 0537 img_info=ImageIndex(self).get_download_info() 0538 r=m.get_media(result, img_info) 0539 self.setmode(self.MODEMODEM) 0540 return r 0541 0542 def savewallpapers(self, result, merge): 0543 self.setmode(self.MODEBREW) 0544 m=FileEntries(self, self.__wallpaper_info) 0545 r=m.save_media(result, ImageIndex(self).get_download_info()) 0546 result['rebootphone']=1 0547 self.setmode(self.MODEMODEM) 0548 return r 0549 0550 getmemo=parent_phone._getmemo 0551 savememo=parent_phone._savememo 0552 0553 gettodo=parent_phone._gettodo 0554 savetodo=parent_phone._savetodo 0555 0556 getsms=parent_phone._getsms 0557 savesms=parent_phone._savesms 0558 0559 getphoneinfo=parent_phone._getphoneinfo 0560 0561 getmedia=None 0562 0563 #------------------------------------------------------------------------------- 0564 class Profile(com_samsung.Profile): 0565 0566 serialsname='scha650' 0567 0568 WALLPAPER_WIDTH=128 0569 WALLPAPER_HEIGHT=160 0570 MAX_WALLPAPER_BASENAME_LENGTH=17 0571 WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_*[]=<>;|?:% ." 0572 WALLPAPER_CONVERT_FORMAT="png" 0573 0574 MAX_RINGTONE_BASENAME_LENGTH=17 0575 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_*[]=<>;|?:% ." 0576 RINGTONE_LIMITS= { 0577 'MAXSIZE': 30000 0578 } 0579 # use for auto-detection 0580 phone_manufacturer='SAMSUNG ELECTRONICS' 0581 phone_model='SCH-A650/163' 0582 0583 def __init__(self): 0584 com_samsung.Profile.__init__(self) 0585 0586 _supportedsyncs=( 0587 ('phonebook', 'read', None), # all phonebook reading 0588 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 0589 ('calendar', 'read', None), # all calendar reading 0590 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 0591 ('ringtone', 'read', None), # all ringtone reading 0592 ('ringtone', 'write', 'OVERWRITE'), 0593 ('wallpaper', 'read', None), # all wallpaper reading 0594 ('wallpaper', 'write', 'OVERWRITE'), 0595 ('memo', 'read', None), # all memo list reading DJP 0596 ('memo', 'write', 'OVERWRITE'), # all memo list writing DJP 0597 ('todo', 'read', None), # all todo list reading DJP 0598 ('todo', 'write', 'OVERWRITE'), # all todo list writing DJP 0599 ('sms', 'read', None), # all SMS list reading DJP 0600 ) 0601 0602 if __debug__: 0603 _supportedsyncs+=(('sms', 'write', 'OVERWRITE'),) 0604 0605 def convertphonebooktophone(self, helper, data): 0606 return data 0607 0608 __audio_ext={ 'MIDI': 'mid', 'QCP': 'qcp', 'PMD': 'pmd' } 0609 def QueryAudio(self, origin, currentextension, afi): 0610 # we don't modify any of these 0611 if afi.format in ("MIDI", "QCP", "PMD"): 0612 for k,n in self.RINGTONE_LIMITS.items(): 0613 setattr(afi, k, n) 0614 return currentextension, afi 0615 d=self.RINGTONE_LIMITS.copy() 0616 d['format']='QCP' 0617 return ('qcp', fileinfo.AudioFileInfo(afi, **d)) 0618 0619 # fill in your own image origins using these 0620 # use this temorarily until the stock one is finalized 0621 imageorigins={} 0622 imageorigins.update(common.getkv(com_samsung.Profile.stockimageorigins, "images")) 0623 0624 imagetargets={} 0625 imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "wallpaper", 0626 {'width': 128, 'height': 128, 'format': "PNG"})) 0627 imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "fullscreen", 0628 {'width': 128, 'height': 160, 'format': "PNG"})) 0629 0630 def GetImageOrigins(self): 0631 # Note: only return origins that you can write back to the phone 0632 return self.imageorigins 0633 0634 def GetTargetsForImageOrigin(self, origin): 0635 if origin=='images': 0636 return self.imagetargets 0637 0638 #------------------------------------------------------------------------------- 0639 class FileEntries: 0640 def __init__(self, phone, info): 0641 self.__phone=phone 0642 self.__file_type, self.__index_type, self.__origin, self.__path, self.__max_file_len, self.__max_file_count=info 0643 0644 def get_media(self, result, download_info=None): 0645 self.__phone.log('Getting media for type '+self.__file_type) 0646 if download_info is None: 0647 return self.__get_media_by_dir(result) 0648 else: 0649 return self.__get_media_by_index(result, download_info) 0650 0651 def __get_media_by_dir(self, result): 0652 media=result.get(self.__file_type, {}) 0653 idx=result.get(self.__index_type, {}) 0654 file_cnt, idx_k=0, len(idx) 0655 path_len=len(self.__path)+1 0656 try: 0657 file_list=self.__phone.getfilesystem(self.__path, 0) 0658 for k in file_list: 0659 try: 0660 index=k[path_len:] 0661 # print k, index 0662 media[index]=self.__phone.getfilecontents(k, True) 0663 idx[idx_k]={ 'name': index, 'origin': self.__origin } 0664 idx_k+=1 0665 file_cnt += 1 0666 except: 0667 self.__phone.log('Failed to read file '+k) 0668 except: 0669 self.__phone.log('Failed to read dir '+self.__path) 0670 result[self.__file_type]=media 0671 result[self.__index_type]=idx 0672 if file_cnt > self.__max_file_count: 0673 self.__phone.log('This phone only supports %d %s. %d %s read, weird things may happen.' % \ 0674 (self.__max_file_count, self.__file_type, 0675 file_cnt, self.__file_type)) 0676 return result 0677 0678 def __get_media_by_index(self, result, rt_info): 0679 media=result.get(self.__file_type, {}) 0680 media_index=result.get(self.__index_type, {}) 0681 for k, m in media_index.items(): 0682 file_key=m.get('name', None) 0683 file_name=rt_info.get(file_key, None) 0684 if file_key is not None and file_name is not None: 0685 try : 0686 media[file_key]=self.__phone.getfilecontents(file_name, True) 0687 except: 0688 self.__phone.log('Failed to read file '+file_name) 0689 result[self.__file_type]=media 0690 return result 0691 0692 def save_media(self, result, dl_info): 0693 self.__phone.log('Saving media for type '+self.__file_type) 0694 media, idx=result[self.__file_type], result[self.__index_type] 0695 # check for files selected for deletion 0696 media_names=[media[k]['name'] for k in media] 0697 dl_info_keys=dl_info.keys() 0698 deleted_keys=[k for k in dl_info_keys if k not in media_names] 0699 new_keys=[k for k in media if media[k]['name'] not in dl_info_keys] 0700 # deleting files 0701 for k in deleted_keys: 0702 file_name=dl_info[k] 0703 self.__phone.log('Deleting file: '+file_name) 0704 try: 0705 self.__phone.rmfile(file_name) 0706 except: 0707 self.__phone.log('Failed to delete file: '+file_name) 0708 # writing new files 0709 # make sure dir exists to write new files 0710 if len(new_keys): 0711 try: 0712 self.__phone.mkdirs(self.__path) 0713 except: 0714 pass 0715 file_count=0 0716 for k in new_keys: 0717 n=media[k] 0718 origin=n.get('origin', None) 0719 if origin is not None and origin != self.__origin: 0720 continue 0721 if len(n['name']) > self.__max_file_len: 0722 self.__phone.log('%s %s name is too long and not sent to phone'% \ 0723 (self.__file_type, n['name'])) 0724 continue 0725 file_count+=1 0726 if file_count>self.__max_file_count: 0727 # max # of files reached, bailing out 0728 self.__phone.log('This phone only supports %d %s. Save operation stopped.'%\ 0729 (self.__max_file_count, self.__file_type)) 0730 break 0731 file_name=self.__path+'/'+n['name'] 0732 if self.__origin=='images': 0733 # try to optimize it if it's a png image file 0734 file_contents=conversions.convertto8bitpng_joe(n['data']) 0735 else: 0736 file_contents=n['data'] 0737 self.__phone.log('Writing file: '+file_name) 0738 try: 0739 self.__phone.writefile(file_name, file_contents) 0740 except: 0741 self.__phone.log('Failed to write file: '+file_name) 0742 media[k]['origin']=self.__origin 0743 0744 return result 0745 0746 #------------------------------------------------------------------------------- 0747 class RingtoneIndex: 0748 __builtin_ringtones=( 'Inactive', 0749 'Bell 1', 'Bell 2', 'Bell 3', 'Bell 4', 'Bell 5', 0750 'Melody 1', 'Melody 2', 'Melody 3', 'Melody 4', 'Melody 5', 0751 'Melody 6', 'Melody 7', 'Melody 8', 'Melody 9', 'Melody 10') 0752 0753 def __init__(self, phone): 0754 self.__phone=phone 0755 0756 def get_builtin_index(self): 0757 r={} 0758 for k, n in enumerate(self.__builtin_ringtones): 0759 r[k]={ 'name': n, 'origin': 'builtin' } 0760 return r 0761 0762 def get_download_index(self): 0763 r={} 0764 try: 0765 rt_idx=self.__phone.protocolclass.ringtones() 0766 buf=prototypes.buffer(self.__phone.getfilecontents( \ 0767 self.__phone.protocolclass.ringtone_index_file_name)) 0768 rt_idx.readfrombuffer(buf, logtitle="Read ringtone index file") 0769 idx=len(self.__builtin_ringtones) 0770 l=len(self.__phone.protocolclass.ringtone_file_path)+1 0771 for i in range(self.__phone.protocolclass.max_ringtone_entries): 0772 e=rt_idx.entry[i] 0773 if e.name_len: 0774 r[idx+i]={ 'name': e.file_name[l:e.file_name_len], 0775 'origin': 'ringtone' } 0776 except: 0777 pass 0778 return r 0779 0780 def get(self): 0781 r=self.get_builtin_index() 0782 r.update(self.get_download_index()) 0783 return r 0784 0785 def get_download_info(self): 0786 r={} 0787 try: 0788 rt_idx=self.__phone.protocolclass.ringtones() 0789 buf=prototypes.buffer(self.__phone.getfilecontents( \ 0790 self.__phone.protocolclass.ringtone_index_file_name)) 0791 rt_idx.readfrombuffer(buf, logtitle="Read ringtone download index") 0792 l=len(self.__phone.protocolclass.ringtone_file_path)+1 0793 for i in range(self.__phone.protocolclass.max_ringtone_entries): 0794 e=rt_idx.entry[i] 0795 if e.name_len: 0796 r[e.file_name[l:e.file_name_len]]=e.file_name[:e.file_name_len] 0797 except: 0798 pass 0799 return r 0800 0801 #------------------------------------------------------------------------------- 0802 class ImageIndex: 0803 __builtin_images=( 'Clock1', 'Dual Clock', 'Calendar', 'Aquarium', 0804 'Landscape', 'Water Drop' ) 0805 0806 def __init__(self, phone): 0807 self.__phone=phone 0808 0809 def get_builtin_index(self): 0810 r={} 0811 for k, n in enumerate(self.__builtin_images): 0812 r[k]={ 'name': n, 'origin': 'builtin' } 0813 return r 0814 0815 def get_download_index(self): 0816 r={} 0817 try: 0818 img_idx=self.__phone.protocolclass.images() 0819 buf=prototypes.buffer(self.__phone.getfilecontents( \ 0820 self.__phone.protocolclass.image_index_file_name)) 0821 img_idx.readfrombuffer(buf, logtitle="Read image download index") 0822 idx=len(self.__builtin_images) 0823 l=len(self.__phone.protocolclass.image_file_path)+1 0824 for i in range(self.__phone.protocolclass.max_image_entries): 0825 e=img_idx.entry[i] 0826 if e.name_len: 0827 r[idx+i]={ 'name': e.file_name[l:e.file_name_len], 0828 'origin': 'images' } 0829 except: 0830 raise 0831 return r 0832 0833 def get(self): 0834 r=self.get_builtin_index() 0835 r.update(self.get_download_index()) 0836 return r 0837 0838 def get_download_info(self): 0839 r={} 0840 try: 0841 img_idx=self.__phone.protocolclass.images() 0842 buf=prototypes.buffer(self.__phone.getfilecontents( \ 0843 self.__phone.protocolclass.image_index_file_name)) 0844 img_idx.readfrombuffer(buf, logtitle="Read image download index") 0845 l=len(self.__phone.protocolclass.image_file_path)+1 0846 for i in range(self.__phone.protocolclass.max_image_entries): 0847 e=img_idx.entry[i] 0848 if e.name_len: 0849 r[e.file_name[l:e.file_name_len]]=e.file_name[:e.file_name_len] 0850 except: 0851 pass 0852 return r 0853 0854 #------------------------------------------------------------------------------- 0855 class PhoneNumbers: 0856 def __init__(self, phone): 0857 self.__phone=phone 0858 self.__numbers=None 0859 0860 def read(self): 0861 try: 0862 buf=prototypes.buffer(self.__phone.getfilecontents(\ 0863 self.__phone.protocolclass.number_file_name)) 0864 self.__numbers=self.__phone.protocolclass.numbers() 0865 self.__numbers.readfrombuffer(buf, logtitle="Read number file "+self.__phone.protocolclass.number_file_name) 0866 except: 0867 self.__phone.log('Failed to read numbers file') 0868 self.__numbers=[] 0869 0870 def get(self, index, default=None): 0871 if index>=self.__phone.protocolclass.max_number_entries: 0872 return default 0873 e=self.__numbers.entry[index] 0874 if e.valid: 0875 return e.name[:e.length] 0876 return default 0877 0878 #------------------------------------------------------------------------------- 0879 class PhoneBook: 0880 0881 __pb_numbers= ({'home': 'home_num_index' }, 0882 {'office': 'office_num_index' }, 0883 {'cell': 'mobile_num_index' }, 0884 {'pager': 'pager_num_index' }, 0885 {'fax': 'fax_num_index' }) 0886 def __init__(self, phone): 0887 self.__phone=phone 0888 self.__pb=None 0889 self.__numbers=None 0890 self.__groups=None 0891 self.__rt_index=None 0892 self.__id=None 0893 self.__slots=None 0894 0895 def read(self): 0896 try: 0897 buf=prototypes.buffer(self.__phone.getfilecontents(\ 0898 self.__phone.protocolclass.pb_file_name)) 0899 self.__pb=self.__phone.protocolclass.pbbook() 0900 self.__pb.readfrombuffer(buf, logtitle="Read "+self.__phone.protocolclass.pb_file_name) 0901 except: 0902 self.__pb=[] 0903 self.__phone.log('Failed to read phonebook') 0904 0905 def get_ringtone(self, index): 0906 """ 0907 Return the ringtone index of this entry. 0908 """ 0909 if self.__pb is None: 0910 self.read() 0911 rt=self.__pb.entry[index].ringer_type 0912 if rt: 0913 rt-=71 0914 return rt 0915 0916 def __extract_entry(self, e, pb_cnt, mem_index): 0917 res={} 0918 # serials 0919 res['serials']=[ {'sourcetype': self.__phone.serialsname, 0920 'sourceuniqueid': self.__id, 0921 'serial1': `pb_cnt`, 0922 'serial2': `mem_index` }] 0923 # only one name 0924 res['names']=[ {'full': e.name } ] 0925 if e.alias_num_index: 0926 res['names'][0]['nickname']=self.__numbers.get(e.alias_num_index, '') 0927 0928 # only one category 0929 res['categories']=[ {'category': self.__groups[e.group_num]['name'] } ] 0930 0931 # emails 0932 if e.email_index: 0933 res['emails']=[ { 'email': self.__numbers.get(e.email_index, '') } ] 0934 0935 # urls: N/A 0936 # private: N/A 0937 # memos: N/A 0938 # wallpapers: N/A 0939 0940 # ringtones 0941 rt=e.ringer_type 0942 if rt: 0943 rt-=71 0944 res['ringtones']=[ { 'ringtone': self.__rt_index[rt]['name'], 0945 'use': 'call' } ] 0946 0947 # numbers 0948 speed_dial=e.speed_dial_index 0949 res['numbers']=[] 0950 for k, a in enumerate(self.__pb_numbers): 0951 for key, attr in a.items(): 0952 idx=getattr(e, attr, 0) 0953 if idx: 0954 num=self.__numbers.get(idx, '') 0955 if idx==speed_dial: 0956 res['numbers'].append({ 'number': num, 0957 'type': key, 0958 'speeddial': mem_index } ) 0959 else: 0960 res['numbers'].append({ 'number': num, 0961 'type': key }) 0962 # done 0963 return res 0964 0965 def get_dict(self, result): 0966 # read the phonebook if not already done so 0967 if self.__pb is None: 0968 self.read() 0969 # read the phone numbers 0970 if self.__numbers is None: 0971 self.__numbers=PhoneNumbers(self.__phone) 0972 self.__numbers.read() 0973 # read the slot entries 0974 if self.__slots is None: 0975 self.__slots=PBSlot(self.__phone) 0976 self.__slots.read() 0977 # get fundamental info 0978 self.__groups=result.get('groups', {}) 0979 self.__rt_index=result.get('ringtone-index', {}) 0980 self.__id=result.get('uniqueserial', '') 0981 # loop through the phonebook and extract each entry 0982 r={} 0983 for pb_cnt, i in enumerate(self.__slots): 0984 if i==0: 0985 # empty slot 0986 continue 0987 e=self.__pb.entry[i] 0988 if e.mem_index: 0989 if i != e.mem_index: 0990 self.__phone.log('i: %d, mem_index: %d'%(i, e.mem_index)) 0991 r[pb_cnt]=self.__extract_entry(e, pb_cnt, i) 0992 return r 0993 0994 #------------------------------------------------------------------------------- 0995 class PBSlot: 0996 """ Class to handle Phonebook entry slot -> memory slot """ 0997 def __init__(self, phone): 0998 self.__phone=phone 0999 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
Generated by PyXR 0.9.4