PyXR

c:\projects\bitpim\src \ phones \ com_samsungspha620.py



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