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

Source Code for Module phones.com_samsungspha620

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2004-2005 Stephen Wood <saw@bitpim.org> 
  4  ### Copyright (C) 2005 Todd Imboden 
  5  ### 
  6  ### This program is free software; you can redistribute it and/or modify 
  7  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  8  ### 
  9  ### $Id: com_samsungspha620.py 4371 2007-08-22 04:22:40Z sawecw $ 
 10   
 11  """Communicate with a Samsung SPH-A620""" 
 12   
 13  import sha 
 14  import re 
 15  import struct 
 16   
 17  import common 
 18  import commport 
 19  import p_samsungspha620 
 20  import p_brew 
 21  import com_brew 
 22  import com_phone 
 23  import com_samsung_packet 
 24  import prototypes 
 25  import fileinfo 
 26  import helpids 
 27   
 28  numbertypetab=('home','office','cell','pager','fax','none') 
 29   
30 -class Phone(com_samsung_packet.Phone):
31 "Talk to a Samsung SPH-A620 phone" 32 33 desc="SPH-A620" 34 helpid=helpids.ID_PHONE_SAMSUNGOTHERS 35 protocolclass=p_samsungspha620 36 serialsname='spha620' 37 38 # digital_cam/jpeg Remove first 148 characters 39 40 imagelocations=( 41 # offset, index file, files location, origin, maximumentries, header offset 42 # Offset is arbitrary. 100 is reserved for amsRegistry indexed files 43 (400, "digital_cam/wallet", "camera", 100, 148), 44 (300, "digital_cam/jpeg", "camera", 100, 148), 45 ) 46 47 ringtonelocations=( 48 # offset, index file, files location, type, maximumentries, header offset 49 ) 50 51 __audio_mimetype={ 'mid': 'audio/midi', 'qcp': 'audio/vnd.qcelp', 'pmd': 'application/x-pmd'} 52 __image_mimetype={ 'jpg': 'image/jpg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 'bmp': 'image/bmp', 'png': 'image/png'} 53
54 - def __init__(self, logtarget, commport):
55 com_samsung_packet.Phone.__init__(self, logtarget, commport) 56 self.numbertypetab=numbertypetab 57 self.mode=self.MODENONE
58
59 - def getfundamentals(self, results):
60 """Gets information fundamental to interopating with the phone and UI.""" 61 self.amsanalyze(results) 62 63 # use a hash of ESN and other stuff (being paranoid) 64 self.log("Retrieving fundamental phone information") 65 self.log("Phone serial number") 66 self.setmode(self.MODEMODEM) 67 results['uniqueserial']=sha.new(self.get_esn()).hexdigest() 68 69 self.log("Reading group information") 70 results['groups']=self.read_groups() 71 # Comment the next to out if you want to read the phonebook 72 #self.getwallpaperindices(results) 73 #self.getringtoneindices(results) 74 self.log("Fundamentals retrieved") 75 return results
76
77 - def amsanalyze(self,results):
78 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.AMSREGISTRY)) 79 ams=self.protocolclass.amsregistry() 80 ams.readfrombuffer(buf, logtitle="Read AMS registry") 81 rt={} #Todd added for ringtone index 82 nrt=0 #Todd added for ringtone index 83 wp={} 84 nwp=0 85 for i in range(ams.nfiles): 86 filetype=ams.info[i].filetype 87 if filetype: 88 dir_ptr=ams.info[i].dir_ptr 89 name_ptr=ams.info[i].name_ptr 90 mimetype_ptr=ams.info[i].mimetype_ptr 91 version_ptr=ams.info[i].version_ptr 92 vendor_ptr=ams.info[i].vendor_ptr 93 dir=self.getstring(ams.strings,dir_ptr) 94 name=self.getstring(ams.strings,name_ptr) 95 mimetype=self.getstring(ams.strings,mimetype_ptr) 96 version=self.getstring(ams.strings,version_ptr) 97 vendor=self.getstring(ams.strings,vendor_ptr) 98 99 #downloaddomain_ptr=ams.info[i].downloaddomain_ptr 100 print i, filetype, version, dir, vendor, name, mimetype 101 #if downloaddomainptr_ptr: 102 # print self.getstring(ams.strings,misc_ptr) 103 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 104 print " " 105 106 # Todd's added info 107 if filetype==12: #this will add the file extension 108 if mimetype=="audio/vnd.qcelp": 109 filetype='.qcp' 110 elif mimetype=="audio/midi": 111 filetype='.mid' 112 elif mimetype=="application/x-pmd": 113 filetype='.pmd' 114 elif mimetype=="audio/mp3": 115 filetype='.mp3' 116 else: 117 filetype='' 118 rt[100+nrt]={'name':name+filetype,'location':'ams/'+dir,'origin':'ringers'} 119 nrt+=1 120 elif filetype==13: 121 if mimetype=="image/jpeg": 122 filetype='.jpg' 123 elif mimetype=="image/png": 124 filetype='.png' 125 elif mimetype=="image/gif": 126 filetype='.gif' 127 elif mimetype=="image/bmp": 128 filetype='.bmp' 129 else: 130 filetype='' 131 wp[100+nwp]={'name':name+filetype,'location':'ams/'+dir,'origin':'images'} 132 nwp+=1 133 134 results['ringtone-index']=rt 135 results['wallpaper-index']=wp
136
137 - def pblinerepair(self, line):
138 "Extend incomplete lines" 139 # VGA1000 Firmware WG09 doesn't return a full line unless birthday 140 # is set. Add extra commas so packet decoding works 141 nfields=26 # Can we get this from packet def? 142 ncommas=self.countcommas(line) 143 if ncommas<0: # Un terminated quote 144 line+='"' 145 ncommas = -ncommas 146 if nfields-ncommas>1: 147 line=line+","*(nfields-ncommas-1) 148 return line
149
150 - def countcommas(self, line):
151 inquote=False 152 ncommas=0 153 for c in line: 154 if c == '"': 155 inquote = not inquote 156 elif not inquote and c == ',': 157 ncommas+=1 158 159 if inquote: 160 ncommas = -ncommas 161 162 return ncommas
163
164 - def getringtones(self, results):
165 self.getmediaindex(self.ringtonelocations, results,'ringtone-index') 166 return self.getmedia(self.ringtonelocations, results, 'ringtone')
167
168 - def getwallpapers(self, results):
169 self.getmediaindex(self.imagelocations, results,'wallpaper-index') 170 return self.getmedia(self.imagelocations, results, 'wallpapers')
171 172
173 - def getmedia(self, maps, results, key):
174 """Returns the contents of media as a dicxt where the key is a name 175 returned by getindex, and the value is the contents of the media""" 176 177 media={} 178 if key=="ringtone": 179 index_key="ringtone-index" 180 origin="ringers" 181 elif key=="wallpapers": 182 index_key="wallpaper-index" 183 origin="images" 184 185 # Read the files indexed in ams Registry 186 for k in results[index_key].keys(): 187 e=results[index_key][k] 188 if e['origin']==origin: 189 self.log("Reading "+e['name']) 190 contents=self.getfilecontents(e['location']) 191 media[e['name']]=contents 192 193 # Now read from the directories in the maps array 194 for offset,location,type,maxentries, headeroffset in maps: 195 for item in self.listfiles(location).values(): 196 filename=item['name'] 197 p=filename.rfind("/") 198 basefilename=filename[p+1:]+".jpg" 199 contents=self.getfilecontents(filename) 200 201 name_len=ord(contents[5]) 202 new_basefilename=filename[p+1:]+"_"+contents[6:6+name_len]+".jpg" 203 duplicate=False 204 # Fix up duplicate filenames 205 for k in results[index_key].keys(): 206 if results[index_key][k]['name']==new_basefilename: 207 duplicate=True 208 break 209 if results[index_key][k]['name']==basefilename: 210 ksave=k 211 if duplicate: 212 new_basefilename=basefilename 213 else: 214 self.log("Renaming to "+new_basefilename) 215 results[index_key][ksave]['name']=new_basefilename 216 media[new_basefilename]=contents[headeroffset:] 217 218 results[key]=media
219
220 - def getmediaindex(self, maps, results, key):
221 """Get the media (wallpaper/ringtone) index for stuff not in amsRegistry 222 223 @param results: places results in this dict 224 @param maps: the list of locations 225 @param key: key to place results in 226 """ 227 self.log("Reading "+key) 228 229 media=results[key] # Get any existing indices 230 for offset,location,origin,maxentries, headeroffset in maps: 231 i=0 232 for item in self.listfiles(location).values(): 233 print item 234 filename=item['name'] 235 p=filename.rfind("/") 236 filename=filename[p+1:]+".jpg" 237 media[offset+i]={'name': filename, 'origin': origin} 238 i+=1 239 240 results[key] = media 241 return
242
243 - def _findmediaindex(self, index, name, pbentryname, type):
244 if name is None: 245 return 0 246 for i in index: 247 if index[i]['name']==name: 248 return i 249 # Not found in index, assume Vision download 250 pos=name.find('_') 251 if(pos>=0): 252 i=int(name[pos+1:]) 253 return i 254 255 return 0
256
257 - def getstring(self, contents, start):
258 "Get a null terminated string from contents" 259 i=start 260 while contents[i:i+1]!='\0': 261 i+=1 262 return contents[start:i]
263
264 - def makegcd(self, filename,size,mimetable):
265 "Build a GCD file for filename" 266 ext=common.getext(filename.lower()) 267 try: 268 mimetype=mimetable[ext] 269 except: 270 return "" 271 272 noextname=common.stripext(filename) 273 gcdcontent="Content-Type: "+mimetype+"\nContent-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file:"+filename+"\nContent-Size: "+`size`+"\n\n\n" 274 return gcdcontent
275
276 - def saveringtones(self, result, merge):
277 dircache=self.DirCache(self) 278 media_prefix=self.protocolclass.RINGERPREFIX 279 endtransactionpath=self.protocolclass.ENDTRANSACTION 280 media=result.get('ringtone', {}) 281 media_index=result.get('ringtone-index', {}) 282 media_names=[x['name'] for x in media.values()] 283 index_names=[x['name'] for x in media_index.values()] 284 if merge: 285 del_names=[] 286 else: 287 del_names=[common.stripext(x) for x in index_names if x not in media_names] 288 gcdpattern=re.compile("[\n\r]Content-Name: +(.*?)[\n\r]") 289 new_names=[x for x in media_names if x not in index_names] 290 291 progressmax=len(del_names)+len(new_names) 292 progresscur=0 293 self.log("Writing ringers") 294 self.progress(progresscur, progressmax, "Writing ringers") 295 296 for icnt in range(1,101): 297 if not (new_names or del_names): 298 break 299 fname=media_prefix+`icnt` 300 fnamegcd=media_prefix+`icnt`+".gcd" 301 fstat=dircache.stat(fnamegcd) 302 if del_names and fstat: 303 # See if this file is in list of files to delete 304 gcdcontents=dircache.readfile(fnamegcd) 305 thisfile=gcdpattern.search(gcdcontents).groups()[0] 306 if thisfile in del_names: 307 self.log("Deleting ringer "+thisfile) 308 self.progress(progresscur, progressmax, "Deleting ringer "+thisfile) 309 progresscur+=1 310 dircache.rmfile(fname) 311 dircache.rmfile(fnamegcd) 312 del_names.remove(thisfile) 313 fstat=False 314 if new_names and not fstat: 315 newname=new_names.pop() 316 contents="" 317 for k in media.keys(): 318 if media[k]['name']==newname: 319 contents=media[k]['data'] 320 break 321 322 contentsize=len(contents) 323 if contentsize: 324 gcdcontents=self.makegcd(newname,contentsize,self.__audio_mimetype) 325 self.log("Writing ringer "+newname) 326 self.progress(progresscur, progressmax, "Deleting ringer "+newname) 327 progresscur+=1 328 dircache.writefile(fname,contents) 329 dircache.writefile(fnamegcd,gcdcontents) 330 331 fstat=dircache.stat(endtransactionpath) 332 self.log("Finished writing ringers") 333 self.progress(progressmax, progressmax, "Finished writing ringers") 334 if fstat: 335 dircache.rmfile(endtransactionpath) 336 result['rebootphone']=True 337 338 return
339
340 - def savewallpapers(self, result, merge):
341 dircache=self.DirCache(self) 342 media_prefix=self.protocolclass.WALLPAPERPREFIX 343 endtransactionpath=self.protocolclass.ENDTRANSACTION 344 media=result.get('wallpapers', {}) 345 for i in media.keys(): 346 try: 347 if media[i]['origin']=='camera': 348 del media[i] 349 except: 350 pass 351 media_index=result.get('wallpaper-index', {}) 352 media_names=[x['name'] for x in media.values()] 353 index_names=[x['name'] for x in media_index.values()] 354 if merge: 355 del_names=[] 356 else: 357 del_names=[common.stripext(x) for x in index_names if x not in media_names] 358 gcdpattern=re.compile("[\n\r]Content-Name: +(.*?)[\n\r]") 359 new_names=[x for x in media_names if x not in index_names] 360 361 progressmax=len(del_names)+len(new_names) 362 progresscur=0 363 self.log("Writing images") 364 self.progress(progresscur, progressmax, "Writing images") 365 366 for icnt in range(1,101): 367 if not (new_names or del_names): 368 break 369 fname=media_prefix+`icnt` 370 fnamegcd=media_prefix+`icnt`+".gcd" 371 fstat=dircache.stat(fnamegcd) 372 if del_names and fstat: 373 # See if this file is in list of files to delete 374 gcdcontents=dircache.readfile(fnamegcd) 375 thisfile=gcdpattern.search(gcdcontents).groups()[0] 376 if thisfile in del_names: 377 self.log("Deleting image "+thisfile) 378 self.progress(progresscur, progressmax, "Deleting image "+thisfile) 379 progresscur+=1 380 dircache.rmfile(fname) 381 dircache.rmfile(fnamegcd) 382 del_names.remove(thisfile) 383 fstat=False 384 if new_names and not fstat: 385 newname=new_names.pop() 386 contents="" 387 for k in media.keys(): 388 if media[k]['name']==newname: 389 contents=media[k]['data'] 390 break 391 392 contentsize=len(contents) 393 if contentsize: 394 gcdcontents=self.makegcd(newname,contentsize,self.__image_mimetype) 395 self.log("Writing image "+newname) 396 self.progress(progresscur, progressmax, "Deleting image "+newname) 397 progresscur+=1 398 dircache.writefile(fname,contents) 399 dircache.writefile(fnamegcd,gcdcontents) 400 401 fstat=dircache.stat(endtransactionpath) 402 self.log("Finished writing images") 403 self.progress(progressmax, progressmax, "Finished writing images") 404 if fstat: 405 dircache.rmfile(endtransactionpath) 406 result['rebootphone']=True 407 408 return
409 410
411 -class Profile(com_samsung_packet.Profile):
412 protocolclass=Phone.protocolclass 413 serialsname=Phone.serialsname 414 415 MAX_RINGTONE_BASENAME_LENGTH=19 416 RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ." 417 RINGTONE_LIMITS= { 418 'MAXSIZE': 250000 419 } 420 phone_manufacturer='SAMSUNG' 421 phone_model='SPH-A620/152' 422
423 - def __init__(self):
426 427 _supportedsyncs=( 428 ('phonebook', 'read', None), # all phonebook reading 429 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 430 ('wallpaper', 'read', None), # all wallpaper reading 431 ('wallpaper', 'write', None), # Image conversion needs work 432 ('ringtone', 'read', None), # all ringtone reading 433 ('ringtone', 'write', None), 434 ('calendar', 'read', None), # all calendar reading 435 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 436 ('todo', 'read', None), # all todo list reading 437 ('todo', 'write', 'OVERWRITE'), # all todo list writing 438 ('memo', 'read', None), # all memo list reading 439 ('memo', 'write', 'OVERWRITE'), # all memo list writing 440 ) 441 442 __audio_ext={ 'MIDI': 'mid', 'PMD': 'pmd', 'QCP': 'qcp' }
443 - def QueryAudio(self, origin, currentextension, afi):
444 # we don't modify any of these 445 print "afi.format=",afi.format 446 if afi.format in ("MIDI", "PMD", "QCP"): 447 for k,n in self.RINGTONE_LIMITS.items(): 448 setattr(afi, k, n) 449 return currentextension, afi 450 d=self.RINGTONE_LIMITS.copy() 451 d['format']='QCP' 452 return ('qcp', fileinfo.AudioFileInfo(afi, **d))
453