| Trees | Indices | Help |
|
|---|
|
|
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
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
55 com_samsung_packet.Phone.__init__(self, logtarget, commport)
56 self.numbertypetab=numbertypetab
57 self.mode=self.MODENONE
58
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
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
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
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
165 self.getmediaindex(self.ringtonelocations, results,'ringtone-index')
166 return self.getmedia(self.ringtonelocations, results, 'ringtone')
167
169 self.getmediaindex(self.imagelocations, results,'wallpaper-index')
170 return self.getmedia(self.imagelocations, results, 'wallpapers')
171
172
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
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
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
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
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
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
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
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
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' }
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sun Jan 24 16:22:01 2010 | http://epydoc.sourceforge.net |