0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2004-2005 Stephen Wood <saw@bitpim.org> 0004 ### Copyright (C) 2005 Todd Imboden 0005 ### 0006 ### This program is free software; you can redistribute it and/or modify 0007 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0008 ### 0009 ### $Id: com_samsungspha620.py 4371 2007-08-22 04:22:40Z sawecw $ 0010 0011 """Communicate with a Samsung SPH-A620""" 0012 0013 import sha 0014 import re 0015 import struct 0016 0017 import common 0018 import commport 0019 import p_samsungspha620 0020 import p_brew 0021 import com_brew 0022 import com_phone 0023 import com_samsung_packet 0024 import prototypes 0025 import fileinfo 0026 import helpids 0027 0028 numbertypetab=('home','office','cell','pager','fax','none') 0029 0030 class Phone(com_samsung_packet.Phone): 0031 "Talk to a Samsung SPH-A620 phone" 0032 0033 desc="SPH-A620" 0034 helpid=helpids.ID_PHONE_SAMSUNGOTHERS 0035 protocolclass=p_samsungspha620 0036 serialsname='spha620' 0037 0038 # digital_cam/jpeg Remove first 148 characters 0039 0040 imagelocations=( 0041 # offset, index file, files location, origin, maximumentries, header offset 0042 # Offset is arbitrary. 100 is reserved for amsRegistry indexed files 0043 (400, "digital_cam/wallet", "camera", 100, 148), 0044 (300, "digital_cam/jpeg", "camera", 100, 148), 0045 ) 0046 0047 ringtonelocations=( 0048 # offset, index file, files location, type, maximumentries, header offset 0049 ) 0050 0051 __audio_mimetype={ 'mid': 'audio/midi', 'qcp': 'audio/vnd.qcelp', 'pmd': 'application/x-pmd'} 0052 __image_mimetype={ 'jpg': 'image/jpg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 'bmp': 'image/bmp', 'png': 'image/png'} 0053 0054 def __init__(self, logtarget, commport): 0055 com_samsung_packet.Phone.__init__(self, logtarget, commport) 0056 self.numbertypetab=numbertypetab 0057 self.mode=self.MODENONE 0058 0059 def getfundamentals(self, results): 0060 """Gets information fundamental to interopating with the phone and UI.""" 0061 self.amsanalyze(results) 0062 0063 # use a hash of ESN and other stuff (being paranoid) 0064 self.log("Retrieving fundamental phone information") 0065 self.log("Phone serial number") 0066 self.setmode(self.MODEMODEM) 0067 results['uniqueserial']=sha.new(self.get_esn()).hexdigest() 0068 0069 self.log("Reading group information") 0070 results['groups']=self.read_groups() 0071 # Comment the next to out if you want to read the phonebook 0072 #self.getwallpaperindices(results) 0073 #self.getringtoneindices(results) 0074 self.log("Fundamentals retrieved") 0075 return results 0076 0077 def amsanalyze(self,results): 0078 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.AMSREGISTRY)) 0079 ams=self.protocolclass.amsregistry() 0080 ams.readfrombuffer(buf, logtitle="Read AMS registry") 0081 rt={} #Todd added for ringtone index 0082 nrt=0 #Todd added for ringtone index 0083 wp={} 0084 nwp=0 0085 for i in range(ams.nfiles): 0086 filetype=ams.info[i].filetype 0087 if filetype: 0088 dir_ptr=ams.info[i].dir_ptr 0089 name_ptr=ams.info[i].name_ptr 0090 mimetype_ptr=ams.info[i].mimetype_ptr 0091 version_ptr=ams.info[i].version_ptr 0092 vendor_ptr=ams.info[i].vendor_ptr 0093 dir=self.getstring(ams.strings,dir_ptr) 0094 name=self.getstring(ams.strings,name_ptr) 0095 mimetype=self.getstring(ams.strings,mimetype_ptr) 0096 version=self.getstring(ams.strings,version_ptr) 0097 vendor=self.getstring(ams.strings,vendor_ptr) 0098 0099 #downloaddomain_ptr=ams.info[i].downloaddomain_ptr 0100 print i, filetype, version, dir, vendor, name, mimetype 0101 #if downloaddomainptr_ptr: 0102 # print self.getstring(ams.strings,misc_ptr) 0103 print ams.info[i].num2, ams.info[i].num7, ams.info[i].num8, ams.info[i].num9, ams.info[i].num12, ams.info[i].num13, ams.info[i].num14, ams.info[i].num15, ams.info[i].num16, ams.info[i].num17 0104 print " " 0105 0106 # Todd's added info 0107 if filetype==12: #this will add the file extension 0108 if mimetype=="audio/vnd.qcelp": 0109 filetype='.qcp' 0110 elif mimetype=="audio/midi": 0111 filetype='.mid' 0112 elif mimetype=="application/x-pmd": 0113 filetype='.pmd' 0114 elif mimetype=="audio/mp3": 0115 filetype='.mp3' 0116 else: 0117 filetype='' 0118 rt[100+nrt]={'name':name+filetype,'location':'ams/'+dir,'origin':'ringers'} 0119 nrt+=1 0120 elif filetype==13: 0121 if mimetype=="image/jpeg": 0122 filetype='.jpg' 0123 elif mimetype=="image/png": 0124 filetype='.png' 0125 elif mimetype=="image/gif": 0126 filetype='.gif' 0127 elif mimetype=="image/bmp": 0128 filetype='.bmp' 0129 else: 0130 filetype='' 0131 wp[100+nwp]={'name':name+filetype,'location':'ams/'+dir,'origin':'images'} 0132 nwp+=1 0133 0134 results['ringtone-index']=rt 0135 results['wallpaper-index']=wp 0136 0137 def pblinerepair(self, line): 0138 "Extend incomplete lines" 0139 # VGA1000 Firmware WG09 doesn't return a full line unless birthday 0140 # is set. Add extra commas so packet decoding works 0141 nfields=26 # Can we get this from packet def? 0142 ncommas=self.countcommas(line) 0143 if ncommas<0: # Un terminated quote 0144 line+='"' 0145 ncommas = -ncommas 0146 if nfields-ncommas>1: 0147 line=line+","*(nfields-ncommas-1) 0148 return line 0149 0150 def countcommas(self, line): 0151 inquote=False 0152 ncommas=0 0153 for c in line: 0154 if c == '"': 0155 inquote = not inquote 0156 elif not inquote and c == ',': 0157 ncommas+=1 0158 0159 if inquote: 0160 ncommas = -ncommas 0161 0162 return ncommas 0163 0164 def getringtones(self, results): 0165 self.getmediaindex(self.ringtonelocations, results,'ringtone-index') 0166 return self.getmedia(self.ringtonelocations, results, 'ringtone') 0167 0168 def getwallpapers(self, results): 0169 self.getmediaindex(self.imagelocations, results,'wallpaper-index') 0170 return self.getmedia(self.imagelocations, results, 'wallpapers') 0171 0172 0173 def getmedia(self, maps, results, key): 0174 """Returns the contents of media as a dicxt where the key is a name 0175 returned by getindex, and the value is the contents of the media""" 0176 0177 media={} 0178 if key=="ringtone": 0179 index_key="ringtone-index" 0180 origin="ringers" 0181 elif key=="wallpapers": 0182 index_key="wallpaper-index" 0183 origin="images" 0184 0185 # Read the files indexed in ams Registry 0186 for k in results[index_key].keys(): 0187 e=results[index_key][k] 0188 if e['origin']==origin: 0189 self.log("Reading "+e['name']) 0190 contents=self.getfilecontents(e['location']) 0191 media[e['name']]=contents 0192 0193 # Now read from the directories in the maps array 0194 for offset,location,type,maxentries, headeroffset in maps: 0195 for item in self.listfiles(location).values(): 0196 filename=item['name'] 0197 p=filename.rfind("/") 0198 basefilename=filename[p+1:]+".jpg" 0199 contents=self.getfilecontents(filename) 0200 0201 name_len=ord(contents[5]) 0202 new_basefilename=filename[p+1:]+"_"+contents[6:6+name_len]+".jpg" 0203 duplicate=False 0204 # Fix up duplicate filenames 0205 for k in results[index_key].keys(): 0206 if results[index_key][k]['name']==new_basefilename: 0207 duplicate=True 0208 break 0209 if results[index_key][k]['name']==basefilename: 0210 ksave=k 0211 if duplicate: 0212 new_basefilename=basefilename 0213 else: 0214 self.log("Renaming to "+new_basefilename) 0215 results[index_key][ksave]['name']=new_basefilename 0216 media[new_basefilename]=contents[headeroffset:] 0217 0218 results[key]=media 0219 0220 def getmediaindex(self, maps, results, key): 0221 """Get the media (wallpaper/ringtone) index for stuff not in amsRegistry 0222 0223 @param results: places results in this dict 0224 @param maps: the list of locations 0225 @param key: key to place results in 0226 """ 0227 self.log("Reading "+key) 0228 0229 media=results[key] # Get any existing indices 0230 for offset,location,origin,maxentries, headeroffset in maps: 0231 i=0 0232 for item in self.listfiles(location).values(): 0233 print item 0234 filename=item['name'] 0235 p=filename.rfind("/") 0236 filename=filename[p+1:]+".jpg" 0237 media[offset+i]={'name': filename, 'origin': origin} 0238 i+=1 0239 0240 results[key] = media 0241 return 0242 0243 def _findmediaindex(self, index, name, pbentryname, type): 0244 if name is None: 0245 return 0 0246 for i in index: 0247 if index[i]['name']==name: 0248 return i 0249 # Not found in index, assume Vision download 0250 pos=name.find('_') 0251 if(pos>=0): 0252 i=int(name[pos+1:]) 0253 return i 0254 0255 return 0 0256 0257 def getstring(self, contents, start): 0258 "Get a null terminated string from contents" 0259 i=start 0260 while contents[i:i+1]!='\0': 0261 i+=1 0262 return contents[start:i] 0263 0264 def makegcd(self, filename,size,mimetable): 0265 "Build a GCD file for filename" 0266 ext=common.getext(filename.lower()) 0267 try: 0268 mimetype=mimetable[ext] 0269 except: 0270 return "" 0271 0272 noextname=common.stripext(filename) 0273 gcdcontent="Content-Type: "+mimetype+"\nContent-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file:"+filename+"\nContent-Size: "+`size`+"\n\n\n" 0274 return gcdcontent 0275 0276 def saveringtones(self, result, merge): 0277 dircache=self.DirCache(self) 0278 media_prefix=self.protocolclass.RINGERPREFIX 0279 endtransactionpath=self.protocolclass.ENDTRANSACTION 0280 media=result.get('ringtone', {}) 0281 media_index=result.get('ringtone-index', {}) 0282 media_names=[x['name'] for x in media.values()] 0283 index_names=[x['name'] for x in media_index.values()] 0284 if merge: 0285 del_names=[] 0286 else: 0287 del_names=[common.stripext(x) for x in index_names if x not in media_names] 0288 gcdpattern=re.compile("[\n\r]Content-Name: +(.*?)[\n\r]") 0289 new_names=[x for x in media_names if x not in index_names] 0290 0291 progressmax=len(del_names)+len(new_names) 0292 progresscur=0 0293 self.log("Writing ringers") 0294 self.progress(progresscur, progressmax, "Writing ringers") 0295 0296 for icnt in range(1,101): 0297 if not (new_names or del_names): 0298 break 0299 fname=media_prefix+`icnt` 0300 fnamegcd=media_prefix+`icnt`+".gcd" 0301 fstat=dircache.stat(fnamegcd) 0302 if del_names and fstat: 0303 # See if this file is in list of files to delete 0304 gcdcontents=dircache.readfile(fnamegcd) 0305 thisfile=gcdpattern.search(gcdcontents).groups()[0] 0306 if thisfile in del_names: 0307 self.log("Deleting ringer "+thisfile) 0308 self.progress(progresscur, progressmax, "Deleting ringer "+thisfile) 0309 progresscur+=1 0310 dircache.rmfile(fname) 0311 dircache.rmfile(fnamegcd) 0312 del_names.remove(thisfile) 0313 fstat=False 0314 if new_names and not fstat: 0315 newname=new_names.pop() 0316 contents="" 0317 for k in media.keys(): 0318 if media[k]['name']==newname: 0319 contents=media[k]['data'] 0320 break 0321 0322 contentsize=len(contents) 0323 if contentsize: 0324 gcdcontents=self.makegcd(newname,contentsize,self.__audio_mimetype) 0325 self.log("Writing ringer "+newname) 0326 self.progress(progresscur, progressmax, "Deleting ringer "+newname) 0327 progresscur+=1 0328 dircache.writefile(fname,contents) 0329 dircache.writefile(fnamegcd,gcdcontents) 0330 0331 fstat=dircache.stat(endtransactionpath) 0332 self.log("Finished writing ringers") 0333 self.progress(progressmax, progressmax, "Finished writing ringers") 0334 if fstat: 0335 dircache.rmfile(endtransactionpath) 0336 result['rebootphone']=True 0337 0338 return 0339 0340 def savewallpapers(self, result, merge): 0341 dircache=self.DirCache(self) 0342 media_prefix=self.protocolclass.WALLPAPERPREFIX 0343 endtransactionpath=self.protocolclass.ENDTRANSACTION 0344 media=result.get('wallpapers', {}) 0345 for i in media.keys(): 0346 try: 0347 if media[i]['origin']=='camera': 0348 del media[i] 0349 except: 0350 pass 0351 media_index=result.get('wallpaper-index', {}) 0352 media_names=[x['name'] for x in media.values()] 0353 index_names=[x['name'] for x in media_index.values()] 0354 if merge: 0355 del_names=[] 0356 else: 0357 del_names=[common.stripext(x) for x in index_names if x not in media_names] 0358 gcdpattern=re.compile("[\n\r]Content-Name: +(.*?)[\n\r]") 0359 new_names=[x for x in media_names if x not in index_names] 0360 0361 progressmax=len(del_names)+len(new_names) 0362 progresscur=0 0363 self.log("Writing images") 0364 self.progress(progresscur, progressmax, "Writing images") 0365 0366 for icnt in range(1,101): 0367 if not (new_names or del_names): 0368 break 0369 fname=media_prefix+`icnt` 0370 fnamegcd=media_prefix+`icnt`+".gcd" 0371 fstat=dircache.stat(fnamegcd) 0372 if del_names and fstat: 0373 # See if this file is in list of files to delete 0374 gcdcontents=dircache.readfile(fnamegcd) 0375 thisfile=gcdpattern.search(gcdcontents).groups()[0] 0376 if thisfile in del_names: 0377 self.log("Deleting image "+thisfile) 0378 self.progress(progresscur, progressmax, "Deleting image "+thisfile) 0379 progresscur+=1 0380 dircache.rmfile(fname) 0381 dircache.rmfile(fnamegcd) 0382 del_names.remove(thisfile) 0383 fstat=False 0384 if new_names and not fstat: 0385 newname=new_names.pop() 0386 contents="" 0387 for k in media.keys(): 0388 if media[k]['name']==newname: 0389 contents=media[k]['data'] 0390 break 0391 0392 contentsize=len(contents) 0393 if contentsize: 0394 gcdcontents=self.makegcd(newname,contentsize,self.__image_mimetype) 0395 self.log("Writing image "+newname) 0396 self.progress(progresscur, progressmax, "Deleting image "+newname) 0397 progresscur+=1 0398 dircache.writefile(fname,contents) 0399 dircache.writefile(fnamegcd,gcdcontents) 0400 0401 fstat=dircache.stat(endtransactionpath) 0402 self.log("Finished writing images") 0403 self.progress(progressmax, progressmax, "Finished writing images") 0404 if fstat: 0405 dircache.rmfile(endtransactionpath) 0406 result['rebootphone']=True 0407 0408 return 0409 0410 0411 class Profile(com_samsung_packet.Profile): 0412 protocolclass=Phone.protocolclass 0413 serialsname=Phone.serialsname 0414 0415 MAX_RINGTONE_BASENAME_LENGTH=19 0416 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ." 0417 RINGTONE_LIMITS= { 0418 'MAXSIZE': 250000 0419 } 0420 phone_manufacturer='SAMSUNG' 0421 phone_model='SPH-A620/152' 0422 0423 def __init__(self): 0424 com_samsung_packet.Profile.__init__(self) 0425 self.numbertypetab=numbertypetab 0426 0427 _supportedsyncs=( 0428 ('phonebook', 'read', None), # all phonebook reading 0429 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 0430 ('wallpaper', 'read', None), # all wallpaper reading 0431 ('wallpaper', 'write', None), # Image conversion needs work 0432 ('ringtone', 'read', None), # all ringtone reading 0433 ('ringtone', 'write', None), 0434 ('calendar', 'read', None), # all calendar reading 0435 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 0436 ('todo', 'read', None), # all todo list reading 0437 ('todo', 'write', 'OVERWRITE'), # all todo list writing 0438 ('memo', 'read', None), # all memo list reading 0439 ('memo', 'write', 'OVERWRITE'), # all memo list writing 0440 ) 0441 0442 __audio_ext={ 'MIDI': 'mid', 'PMD': 'pmd', 'QCP': 'qcp' } 0443 def QueryAudio(self, origin, currentextension, afi): 0444 # we don't modify any of these 0445 print "afi.format=",afi.format 0446 if afi.format in ("MIDI", "PMD", "QCP"): 0447 for k,n in self.RINGTONE_LIMITS.items(): 0448 setattr(afi, k, n) 0449 return currentextension, afi 0450 d=self.RINGTONE_LIMITS.copy() 0451 d['format']='QCP' 0452 return ('qcp', fileinfo.AudioFileInfo(afi, **d)) 0453 0454
Generated by PyXR 0.9.4