PyXR

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



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2006 Simon Capper <skyjunky@sbcglobal.net>
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 
0009 """Communicate with the LG PM225 (Sprint) cell phone"""
0010 
0011 # standard modules
0012 import re
0013 import time
0014 import cStringIO
0015 import sha
0016 
0017 # my modules
0018 import p_lgpm225
0019 import p_brew
0020 import common
0021 import commport
0022 import com_brew
0023 import com_phone
0024 import com_lg
0025 import com_lgvx4400
0026 import helpids
0027 import prototypes
0028 import call_history
0029 import sms
0030 import fileinfo
0031 import memo
0032 
0033 
0034 class Phone(com_lgvx4400.Phone):
0035     "Talk to the LG PM225 cell phone"
0036 
0037     desc="LG PM225"
0038     helpid=helpids.ID_PHONE_LGPM225
0039     protocolclass=p_lgpm225
0040     serialsname='lgpm225'
0041 
0042     # read only locations, for regular ringers/wallpaper this phone stores
0043     # the information in a different location
0044     imagelocations=(
0045         # offset, index file, files location, type, maximumentries
0046         (0x600, "setas/dcamIndex.map", "Dcam/Wallet", "camera", 50, 6),
0047         )
0048 
0049     ringtonelocations=(
0050         # offset, index file, files location, type, maximumentries
0051         (0x1100, "setas/voicememoRingerIndex.map", "VoiceDB/All/Memos", "voice_memo", 50, 11),
0052         )
0053 
0054     builtinimages=('Spectrum', 'Speed', 'Drops', 'Country Road', 
0055                    'Houses','Tulip', 'Flower', 'Lines', 'Network', 'Abstract')
0056 
0057     builtinringtones=( 'Tone 1', 'Tone 2', 'Tone 3', 'Tone 4', 'Tone 5', 'Tone 6',
0058                        'Alert 1', 'Alert 2', 'Alert 3', 'Alert 4', 'Alert 5', 'Alert 6',
0059                        'Moonlight', 'Bumble Bee', 'Latin', 'Baroque',
0060                        'Lovable Baby', 'LG Sound', 'Love Song', 'Badinerie',
0061                        'Follow Me', 'Head, Shoulders, Knees & Toes', 'Lake Trance', 'Beethoven',
0062                        'Let''s Play', 'Piano Concerto No.1', 'Pleasure',
0063                        'Leichte Kavallerie', 'Up & Down Melody', 'Vivaldi - Winter')
0064 
0065     def __init__(self, logtarget, commport):
0066         "Calls all the constructors and sets initial modes"
0067         com_phone.Phone.__init__(self, logtarget, commport)
0068         com_brew.BrewProtocol.__init__(self)
0069         com_lg.LGPhonebook.__init__(self)
0070         self.log("Attempting to contact phone")
0071         self._cal_has_voice_id=hasattr(self.protocolclass, 'cal_has_voice_id') \
0072                                 and self.protocolclass.cal_has_voice_id
0073         self.mode=self.MODENONE
0074 
0075 #----- PhoneBook -----------------------------------------------------------------------
0076 
0077     def getfundamentals(self, results):
0078         """Gets information fundamental to interoperating with the phone and UI.
0079 
0080         Currently this is:
0081 
0082           - 'uniqueserial'     a unique serial number representing the phone
0083           - 'groups'           the phonebook groups
0084 
0085         This method is called before we read the phonebook data or before we
0086         write phonebook data.
0087         """
0088 
0089         # use a hash of ESN and other stuff (being paranoid)
0090         self.log("Retrieving fundamental phone information")
0091         self.log("Phone serial number")
0092         results['uniqueserial']=sha.new(self.getfilecontents("nvm/$SYS.ESN")).hexdigest()
0093         self.log(results)
0094 
0095         # now read groups
0096         self.log("Reading group information")
0097         buf=prototypes.buffer(self.getfilecontents("pim/pbookgroup.dat"))
0098         g=self.protocolclass.pbgroups()
0099         g.readfrombuffer(buf, logtitle="Groups read")
0100         groups={}
0101         for i in range(len(g.groups)):
0102             if len(g.groups[i].name): # sometimes have zero length names
0103                 groups[g.groups[i].group_id]={'name': g.groups[i].name }
0104         results['groups']=groups
0105         self.getwallpaperindices(results)
0106         self.getringtoneindices(results)
0107         self.log("Fundamentals retrieved")
0108         return results
0109 
0110     def syncbegin(self):
0111        self.mode = self.MODEPHONEBOOK
0112        self.sendpbcommand(self.protocolclass.pbstartsyncrequest(), self.protocolclass.pbstartsyncresponse)
0113 
0114     def syncend(self):
0115        req=self.protocolclass.pbendsyncrequest()
0116        self.sendpbcommand(req, self.protocolclass.pbendsyncresponse)
0117 
0118     def getphonebook(self,result):
0119         """Reads the phonebook data.  The L{getfundamentals} information will
0120         already be in result."""
0121 
0122         pbook={}
0123         # Bug in the phone.  if you repeatedly read the phone book it starts
0124         # returning a random number as the number of entries.  We get around
0125         # this by switching into brew mode which clears that.
0126         self.mode=self.MODENONE
0127         self.setmode(self.MODEBREW)
0128 
0129         self.log("Reading number of phonebook entries")
0130         self.mode = self.MODEBREW
0131         res=self.sendpbcommand(self.protocolclass.pbinforequest(), self.protocolclass.pbinforesponse)
0132         numentries = res.numentries
0133         self.log("There are %d entries" % (numentries,))
0134         for i in range(0, numentries):
0135             ### Read current entry
0136             req=self.protocolclass.pbreadentryrequest()
0137             res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse)
0138             self.log("Read entry "+`i`+" - "+res.entry.name)
0139             entry=self.extractphonebookentry(res.entry, result)
0140             pbook[res.entry.entrynumber]=entry
0141             self.progress(i, numentries, res.entry.name)
0142             #### Advance to next entry
0143             req=self.protocolclass.pbnextentryrequest()
0144             self.sendpbcommand(req, self.protocolclass.pbnextentryresponse)
0145 
0146         pbook=self.get_phonebook_media(pbook, result)
0147 
0148         self.progress(numentries, numentries, "Phone book read completed")
0149         self.log("Phone book read completed")
0150 
0151         result['phonebook']=pbook
0152 
0153         cats=[]
0154         for i in result['groups']:
0155             if result['groups'][i]['name']!='No Group':
0156                 cats.append(result['groups'][i]['name'])
0157         result['categories']=cats
0158         return pbook
0159 
0160     def get_phonebook_media(self, pbook, fundamentals):
0161         """This phone does not provide ringtone and image info for contacts in 
0162         the regular packets so we have to read the filesystem directly """
0163         buf=prototypes.buffer(self.getfilecontents(self.protocolclass.phonebook_media))
0164         g=self.protocolclass.pb_contact_media_file()
0165         g.readfrombuffer(buf, logtitle="PB Media read")
0166         for i in range(len(g.contacts)):
0167             # adjust wallpaper for stock
0168             if (g.contacts[i].wallpaper & 0xFF00)==0x100:
0169                 g.contacts[i].wallpaper-=0x100
0170             if __debug__:
0171                 tone="None"
0172                 paper="None"
0173                 try:
0174                     tone=fundamentals['ringtone-index'][g.contacts[i].ringer]['name']
0175                 except:
0176                     pass
0177                 try:
0178                     paper=fundamentals['wallpaper-index'][g.contacts[i].wallpaper]['name']
0179                 except:
0180                     pass
0181                 self.log("media entry "+g.contacts[i].name+" ringer "+`tone`+" ("+`g.contacts[i].ringer`+")")
0182                 self.log("media entry "+g.contacts[i].name+" wallpaper "+`paper`+" ("+`g.contacts[i].wallpaper`+")")
0183             if g.contacts[i].index in pbook:
0184                 self.log("Index "+`g.contacts[i].index`+" found")
0185                 if g.contacts[i].ringer:
0186                     try:
0187                         tone=fundamentals['ringtone-index'][g.contacts[i].ringer]['name']
0188                         pbook[g.contacts[i].index]['ringtones']=[{'ringtone': tone, 'use': 'call'}]
0189                     except:
0190                         self.log("Exception in ringtone assignment")
0191                 if g.contacts[i].wallpaper:
0192                     try:
0193                         paper=fundamentals['wallpaper-index'][g.contacts[i].wallpaper]['name']
0194                         pbook[g.contacts[i].index]['wallpapers']=[{'wallpaper': paper, 'use': 'call'}]                
0195                     except:
0196                         self.log("Exception in wallpaper assignment")
0197             else:
0198                 self.log("Index "+`g.contacts[i].index`+" not found")
0199         return pbook
0200 
0201     def extractphonebookentry(self, entry, fundamentals):
0202         """Return a phonebook entry in BitPim format.  This is called from getphonebook."""
0203         res={}
0204         # serials
0205         res['serials']=[ {'sourcetype': self.serialsname, 'serial1': entry.serial1,
0206                           'sourceuniqueid': fundamentals['uniqueserial']} ]
0207         # only one name
0208         res['names']=[ {'full': entry.name} ]
0209         # only one category
0210         cat=fundamentals['groups'].get(entry.group, {'name': "No Group"})['name']
0211         if cat!="No Group":
0212             res['categories']=[ {'category': cat} ]
0213         # emails
0214         res['emails']=[]
0215         for i in entry.emails:
0216             if len(i.email):
0217                 res['emails'].append( {'email': i.email} )
0218         if not len(res['emails']): del res['emails'] # it was empty
0219         # urls
0220         if 'url' in entry.getfields() and len(entry.url):
0221             res['urls']=[ {'url': entry.url} ]
0222         # private
0223         if 'secret' in entry.getfields() and entry.secret:
0224             # we only supply secret if it is true
0225             res['flags']=[ {'secret': entry.secret } ]
0226         # memos
0227         if  'memo' in entry.getfields() and len(entry.memo):
0228             res['memos']=[ {'memo': entry.memo } ]
0229 
0230         # numbers
0231         res['numbers']=[]
0232         for i in range(self.protocolclass.NUMPHONENUMBERS):
0233             num=entry.numbers[i].number
0234             type=entry.numbertypes[i].numbertype
0235             speeddial=entry.numberspeeds[i].numberspeed
0236             if len(num):
0237                 t=self.protocolclass.numbertypetab[type]
0238                 if speeddial != 0xFF: # invalid entry
0239                     res['numbers'].append({'number': num, 'type': t, 'speeddial': speeddial})
0240                 else:
0241                     res['numbers'].append({'number': num, 'type': t})
0242         return res
0243 
0244     def makeentry(self, counter, entry, data):
0245         """Creates pbentry object
0246 
0247         @param counter: The new entry number
0248         @param entry:   The phonebook object (as returned from convertphonebooktophone) that we
0249                         are using as the source
0250         @param data:    The main dictionary, which we use to get access to media indices amongst
0251                         other things
0252                         """
0253         e=self.protocolclass.pbentry()
0254 
0255         e.entrynumber=counter
0256 
0257         for k in entry:
0258             # special treatment for lists
0259             if k in ('emails', 'numbers', 'numbertypes', 'numberspeeds'):
0260                 l=getattr(e,k)
0261                 for item in entry[k]:
0262                     l.append(item)
0263             elif k=='ringtone':
0264                 try:
0265                     e.ringtone=self._findmediainindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone')
0266                 except:
0267                     pass
0268             elif k=='msgringtone':
0269                 pass # not supported by phone
0270                 #e.msgringtone=self._findmediainindex(data['ringtone-index'], entry['msgringtone'], entry['name'], 'message ringtone')
0271             elif k=='wallpaper':
0272                 try:
0273                     e.wallpaper=self._findmediainindex(data['wallpaper-index'], entry['wallpaper'], entry['name'], 'wallpaper')
0274                     # adjust for stock wallpaper
0275                     if e.wallpaper < 0x100:
0276                         e.wallpaper+=0x100
0277                 except:
0278                     pass
0279             elif k in e.getfields():
0280                 # everything else we just set
0281                 setattr(e,k,entry[k])
0282 
0283         return e
0284 
0285     def save_phonebook_media(self, pb_entries):
0286         """This phone does not provide ringtone and image info for contacts in 
0287         the regular packets so we have to write the filesystem directly """
0288         # we read the file, modify it and write it back
0289         buf=prototypes.buffer(self.getfilecontents(self.protocolclass.phonebook_media))
0290         g=self.protocolclass.pb_contact_media_file()
0291         g.readfrombuffer(buf, logtitle="PB Media read")
0292         rc=False
0293         for i in range(len(g.contacts)):
0294             if g.contacts[i].index in pb_entries:
0295                 if g.contacts[i].ringer!=pb_entries[g.contacts[i].index].ringtone:
0296                     g.contacts[i].ringer=pb_entries[g.contacts[i].index].ringtone
0297                     rc=True
0298                 if g.contacts[i].wallpaper!=pb_entries[g.contacts[i].index].wallpaper:
0299                     g.contacts[i].wallpaper=pb_entries[g.contacts[i].index].wallpaper
0300                     rc=True
0301         if rc:
0302             buf=prototypes.buffer()
0303             g.writetobuffer(buf, logtitle="Writing PB media file")
0304             self.writefile(self.protocolclass.phonebook_media, buf.getvalue())
0305         else:
0306             self.log("PB media file up to date, no write required")
0307         return rc
0308 
0309     def savegroups(self, data):
0310         groups=data['groups']
0311         keys=groups.keys()
0312         keys.sort()
0313         g=self.protocolclass.pbgroups()
0314         for k in keys:
0315             e=self.protocolclass.pbgroup()
0316             e.group_id=k
0317             e.rectype = 0x30
0318             e.name=groups[k]['name']
0319             g.groups.append(e)
0320         buffer=prototypes.buffer()
0321         g.writetobuffer(buffer, logtitle="New group file")
0322         self.writefile("pim/pbookgroup.dat", buffer.getvalue())
0323 
0324     def savephonebook(self, data):
0325         "Saves out the phonebook"
0326         self.savegroups(data)
0327         
0328 #        if __debug__:
0329 #            # for testing without a real phone, the packets are stored in a 
0330 #            # file and can be examined
0331 #            counter=0
0332 #            pb_entries={}
0333 #            for i in data['phonebook'].keys():
0334 #                ii=data['phonebook'][i]
0335 #                entry=self.makeentry(counter, ii, data)
0336 #                counter+=1
0337 #                req=self.protocolclass.pbupdateentryrequest()
0338 #                req.entry=entry
0339 #               req.header.sequence=counter
0340  #               buffer=prototypes.buffer()
0341 #                req.writetobuffer(buffer, logtitle="New contents for pim/pb"+`counter`+".dat")
0342 #                self.writefile("pim/pb"+`counter`+".dat", buffer.getvalue())
0343 #                pb_entries[counter]=entry
0344 #            self.save_phonebook_media(pb_entries)
0345 
0346         # set up progress bar on main window
0347         progressmax=len(data['phonebook'].keys())
0348 
0349         # To write the phone book, we scan through all existing entries
0350         # and record their record number and serials.
0351         # We then delete any entries that aren't in data
0352         # We then write out our records, using overwrite or append
0353         # commands as necessary
0354         serialupdates=[]
0355         pb_entries={}
0356         existingpbook={} # keep track of the phonebook that is on the phone
0357         self.mode=self.MODENONE
0358         self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary
0359         self.setmode(self.MODEPHONEBOOK)
0360         # similar loop to reading
0361         req=self.protocolclass.pbinforequest()
0362         res=self.sendpbcommand(req, self.protocolclass.pbinforesponse)
0363         numexistingentries=res.numentries
0364         self.log("There are %d existing entries" % (numexistingentries,))
0365         progressmax+=numexistingentries
0366         loop=xrange(0, numexistingentries)
0367         progresscur=0
0368 
0369         # reset cursor
0370         self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse)
0371         for i in loop:
0372             ### Read current entry
0373             req=self.protocolclass.pbreadentryrequest()
0374             res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse)
0375 
0376             entry={ 'number':  res.entry.entrynumber, 'serial1':  res.entry.serial1, 'name': res.entry.name}
0377 
0378             self.log("Reading entry "+`i`+" - "+str(entry['serial1'])+" - "+entry['name'])
0379             existingpbook[i]=entry
0380             self.progress(progresscur, progressmax, "existing "+entry['name'])
0381             #### Advance to next entry
0382             req=self.protocolclass.pbnextentryrequest()
0383             res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse)
0384             progresscur+=1
0385         # we have now looped around back to begining
0386 
0387         # Find entries that have been deleted
0388         pbook=data['phonebook']
0389         dellist=[]
0390         for i in loop:
0391             ii=existingpbook[i]
0392             serial=ii['serial1']
0393             item=self._findserial(serial, pbook)
0394             if item is None:
0395                 dellist.append(i)
0396 
0397         progressmax+=len(dellist) # more work to do
0398 
0399         # Delete those entries
0400         for i in dellist:
0401             progresscur+=1
0402             numexistingentries-=1  # keep count right
0403             ii=existingpbook[i]
0404             self.log("Deleting entry "+`i`+" - "+str(ii['serial1'])+" - "+ii['name'])
0405             req=self.protocolclass.pbdeleteentryrequest()
0406             req.serial1=ii['serial1']
0407             req.serial2=ii['serial1']
0408             req.entrynumber=ii['number']
0409             self.sendpbcommand(req, self.protocolclass.pbdeleteentryresponse)
0410             self.progress(progresscur, progressmax, "Deleting "+ii['name'])
0411             # also remove them from existingpbook
0412             del existingpbook[i]
0413 
0414         # Now rewrite out existing entries
0415         self.log("Rewrite existing entries")
0416         keys=existingpbook.keys()
0417         existingserials=[]
0418         keys.sort()  # do in same order as existingpbook
0419         for i in keys:
0420             progresscur+=1
0421             ii=pbook[self._findserial(existingpbook[i]['serial1'], pbook)]
0422             self.log("Rewriting entry "+`i`+" - "+ii['name'])
0423             self.progress(progresscur, progressmax, "Rewriting "+ii['name'])
0424             entry=self.makeentry(existingpbook[i]['serial1'], ii, data)
0425             existingserials.append(existingpbook[i]['serial1'])
0426             req=self.protocolclass.pbupdateentryrequest()
0427             req.entry=entry
0428             res=self.sendpbcommand(req, self.protocolclass.pbupdateentryresponse)
0429             serialupdates.append( ( ii["bitpimserial"],
0430                                     {'sourcetype': self.serialsname,
0431                                      'serial1': res.serial1,
0432                                      'sourceuniqueid': data['uniqueserial']}))
0433             assert ii['serial1']==res.serial1 # serial should stay the same
0434             pb_entries[res.serial1]=entry
0435 
0436         # Finally write out new entries
0437         counter=0
0438         keys=pbook.keys()
0439         self.log("Write new entries")
0440         keys.sort()
0441         for i in keys:
0442             ii=pbook[i]
0443             if ii['serial1'] in existingserials:
0444                 continue # already wrote this one out
0445             # find an unused serial number
0446             while True:
0447                 if counter in existingserials:
0448                     counter+=1
0449                 else:
0450                     break
0451             progresscur+=1
0452             entry=self.makeentry(counter, ii, data)
0453             self.log("Appending entry "+ii['name'])
0454             self.progress(progresscur, progressmax, "Writing "+ii['name'])
0455             req=self.protocolclass.pbappendentryrequest()
0456             req.entry=entry
0457             res=self.sendpbcommand(req, self.protocolclass.pbappendentryresponse)
0458             serialupdates.append( ( ii["bitpimserial"],
0459                                      {'sourcetype': self.serialsname,
0460                                       'serial1': res.newserial,
0461                                      'sourceuniqueid': data['uniqueserial']}))
0462             pb_entries[res.newserial]=entry
0463             counter+=1
0464         # update the media file
0465         data['serialupdates']=serialupdates
0466 
0467         changed=self.save_phonebook_media(pb_entries)
0468         if changed:
0469             data["rebootphone"]=True
0470         return data
0471 
0472 #----- SMS  ---------------------------------------------------------------------------
0473 
0474     def _readsms(self):
0475         res={}
0476         # go through the sms directory looking for messages
0477         for item in self.listfiles("sms").values():
0478             folder=None
0479             for f,pat in self.protocolclass.SMS_PATTERNS.items():
0480                 if pat.match(item['name']):
0481                     folder=f
0482                     break
0483             if folder:
0484                 buf=prototypes.buffer(self.getfilecontents(item['name'], True))
0485                 self.logdata("SMS message file " +item['name'], buf.getdata())
0486             if folder=='Inbox':
0487                 sf=self.protocolclass.sms_in()
0488                 sf.readfrombuffer(buf, logtitle="SMS inbox item")
0489                 entry=self._getinboxmessage(sf)
0490                 res[entry.id]=entry
0491             elif folder=='Sent':
0492                 sf=self.protocolclass.sms_out()
0493                 sf.readfrombuffer(buf, logtitle="SMS sent item")
0494                 entry=self._getoutboxmessage(sf)
0495                 res[entry.id]=entry
0496         return res 
0497 
0498 
0499     def _setquicktext(self, result):
0500         sf=self.protocolclass.sms_canned_file()
0501         quicktext=result.get('canned_msg', [])
0502         count=0
0503         for entry in quicktext:
0504             if count < self.protocolclass.SMS_CANNED_MAX_ITEMS:
0505                 msg_entry=self.protocolclass.sms_quick_text()
0506                 msg_entry.msg=entry['text'][:self.protocolclass.SMS_CANNED_MAX_LENGTH-1]
0507                 sf.msgs.append(msg_entry)
0508                 count+=1
0509             else:
0510                 break
0511         if count!=0:
0512             # don't create the file if there are no entries 
0513             sf.num_active=count
0514             buf=prototypes.buffer()
0515             sf.writetobuffer(buf, logtitle="Writing quicktext")
0516             self.writefile(self.protocolclass.SMS_CANNED_FILENAME, buf.getvalue())
0517         return
0518 
0519     def _getquicktext(self):
0520         quicks=[]
0521         try:
0522             buf=prototypes.buffer(self.getfilecontents(self.protocolclass.SMS_CANNED_FILENAME))
0523             sf=self.protocolclass.sms_canned_file()
0524             sf.readfrombuffer(buf, logtitle="SMS quicktext file sms/canned_msg.dat")
0525             for rec in sf.msgs:
0526                 if rec.msg!="":
0527                     quicks.append({ 'text': rec.msg, 'type': sms.CannedMsgEntry.user_type })
0528         except com_brew.BrewNoSuchFileException:
0529             pass # do nothing if file doesn't exist
0530         return quicks
0531 
0532     def _getinboxmessage(self, sf):
0533         entry=sms.SMSEntry()
0534         entry.folder=entry.Folder_Inbox
0535         entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime)
0536         entry._from=self._getsender(sf.sender, sf.sender_length)
0537         entry.subject=sf.subject
0538         entry.locked=sf.locked
0539 #        if sf.priority==0:
0540 #            entry.priority=sms.SMSEntry.Priority_Normal
0541 #        else:
0542 #            entry.priority=sms.SMSEntry.Priority_High
0543         entry.read=sf.read
0544         entry.text=sf.msg
0545         entry.callback=sf.callback
0546         return entry
0547 
0548     def _getoutboxmessage(self, sf):
0549         entry=sms.SMSEntry()
0550         if not sf.saved:
0551             entry.folder=entry.Folder_Sent
0552         else:
0553             entry.folder=entry.Folder_Saved
0554         entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent))
0555         # add all the recipients
0556         for r in sf.recipients:
0557             if r.number:
0558                 confirmed=(r.status==2)
0559                 confirmed_date=None
0560                 if confirmed:
0561                     confirmed_date="%d%02d%02dT%02d%02d00" % r.time
0562                 entry.add_recipient(r.number, confirmed, confirmed_date)
0563         entry.subject=sf.msg[:28]
0564         entry.text=sf.msg
0565 #        if sf.priority==0:
0566 #            entry.priority=sms.SMSEntry.Priority_Normal
0567 #        else:
0568 #            entry.priority=sms.SMSEntry.Priority_High
0569         entry.locked=sf.locked
0570         entry.callback=sf.callback
0571         return entry
0572 
0573 
0574 #----- Media  -----------------------------------------------------------------------
0575 
0576     # this phone can take MP3 files, but you have to use the vnd.qcelp MIME type
0577     # this makes renaming the files (from the ##.dat format) back to a real
0578     # name difficult, we assume all vnd.qcelp files are mp3 files as this is the
0579     # most common format
0580 
0581     __mimetype={ 'mid': 'audio/midi', 'qcp': 'audio/vnd.qcelp', 'jar': 'application/java-archive',
0582                  'jpg': 'image/jpg', 'jpeg': 'image/jpeg', 'gif': 'image/gif', 
0583                  'bmp': 'image/bmp', 'png': 'image/png', 'mp3': 'audio/vnd.qcelp'}
0584 
0585     __reverse_mimetype={ 'audio/midi': 'mid', 'audio/vnd.qcelp': 'mp3', 'application/java-archive': 'jar',
0586                  'image/jpg': 'jpg', 'image/jpeg': 'jpeg', 'image/gif': 'gif', 
0587                  'image/bmp': 'bmp', 'image/png': 'png', 'audio/mp3': 'mp3'}
0588 
0589     __app_extensions={ 'jar':'' }
0590 
0591 
0592     def getwallpaperindices(self, results):
0593         return self.getmediaindex(self.builtinimages, self.imagelocations, results, 'wallpaper-index')
0594 
0595     def getringtoneindices(self, results):
0596         return self.getmediaindex(self.builtinringtones, self.ringtonelocations, results, 'ringtone-index')
0597 
0598     def getwallpapers(self, result):
0599         return self.getmedia(self.imagelocations, result, 'wallpapers')
0600 
0601     def getringtones(self, result):
0602         return self.getmedia(self.ringtonelocations, result, 'ringtone')
0603 
0604     def savewallpapers(self, results, merge):
0605         return self.savemedia('wallpapers', 'wallpaper-index', results, merge, self.getwallpaperindices)
0606 
0607     def saveringtones(self, results, merge):
0608         return self.savemedia('ringtone', 'ringtone-index', results, merge, self.getringtoneindices)
0609 
0610     def getindex(self, indexfile):
0611         "Read an index file"
0612         index={}
0613         try:
0614             buf=prototypes.buffer(self.getfilecontents(indexfile))
0615         except com_brew.BrewNoSuchFileException:
0616             # file may not exist
0617             return index
0618         g=self.protocolclass.indexfile()
0619         g.readfrombuffer(buf, logtitle="Read indexfile "+indexfile)
0620         for i in g.items:
0621             if i.index!=0xffff and len(i.name):
0622                 index[i.index]=i.name
0623         return index
0624 
0625     def get_content_file(self, key):
0626         index={}
0627         if key=='ringtone' or key=='ringtone-index':
0628             type='Ringers'
0629             index_const=self.protocolclass.ringerconst*0x100
0630             indexfile=self.getindex(self.protocolclass.ringerindex)
0631         else:
0632             type='Screen Savers'
0633             index_const=self.protocolclass.imageconst*0x100
0634             indexfile=self.getindex(self.protocolclass.imageindex)
0635         try:
0636             buf=prototypes.buffer(self.getfilecontents(self.protocolclass.content_file_name))
0637             g=self.protocolclass.content_file()
0638             g.readfrombuffer(buf, logtitle="Content file "+self.protocolclass.content_file_name)
0639             for i in g.items:
0640                 if i.type=='!C' and i.content_type==type:
0641                     try:
0642                         # construct a user friendly filename
0643                         ext=self.__reverse_mimetype[i.mime_type]
0644                         # find the "file" in the index file and get it's index
0645                         # if the index was created by bitpim it will be the same 
0646                         # as the unfriendly filename, but this is not guarenteed
0647                         found=False
0648                         for j in indexfile.keys():
0649                             # convert to int to strip leading zero
0650                             try:
0651                                 if int(common.stripext(indexfile[j]))==int(i.index1):
0652                                     index[j + index_const]=i.name1+'.'+ext
0653                                     found=True
0654                                     break;
0655                             except:
0656                                 pass
0657                         if not found:
0658                             self.log("Unable to find index entry for "+i.name1+". Index : "+`i.index1`)
0659                     except:
0660                         pass
0661         except com_brew.BrewNoSuchFileException:
0662             pass
0663         return index, indexfile, index_const
0664 
0665     def getmedia(self, maps, result, key):
0666         """Returns the contents of media as a dict where the key is a name as returned
0667         by getindex, and the value is the contents of the media"""
0668         media={}
0669         # first read the maps
0670         type=None
0671         for offset,indexfile,location,type,maxentries,const in maps:
0672             index=self.getindex(indexfile)
0673             for i in index:
0674                 try:
0675                     media[index[i]]=self.getfilecontents(location+"/"+index[i], True)
0676                 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException):
0677                     self.log("It was in the index, but not on the filesystem")
0678                     
0679         # secondly read the content file
0680         index, indexfile, index_const=self.get_content_file(key)
0681         for i in index:
0682             try:
0683                 buf=prototypes.buffer(self.getfilecontents(self.protocolclass.media_directory+'/'+indexfile[i-index_const], True))
0684                 _fname=index[i]
0685                 # This would be nice but this will read the entire file which will slow the media retrieval down
0686 #                ext=common.getext(_fname)
0687 #                if ext=='mp3':
0688 #                    # see if it is really an mp3, we will need to rename it
0689 #                    try:
0690 #                        qcp_header=self.protocolclass.qcp_media_header()
0691 #                        qcp_header.readfrombuffer(buf, logtitle="qcp header")
0692 #                    except: # exception will be thrown if header does not match
0693 #                        _fname=common.stripext(index[i])+'.'+'qcp'
0694                 media[_fname]=buf.getdata()
0695             except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException):
0696                 self.log("It was in the index, but not on the filesystem")
0697         self.log("Contents not in the filesystem")
0698 
0699         result[key]=media
0700         return result
0701 
0702     def getmediaindex(self, builtins, maps, results, key):
0703         """Gets the media (wallpaper/ringtone) index
0704 
0705         @param builtins: the builtin list on the phone
0706         @param results: places results in this dict
0707         @param maps: the list of index files and locations
0708         @param key: key to place results in
0709         """
0710 
0711         self.log("Reading "+key)
0712         media={}
0713 
0714         # builtins
0715         c=1
0716         for name in builtins:
0717             media[c]={'name': name, 'origin': 'builtin' }
0718             c+=1
0719 
0720         # the maps
0721         type=None
0722         for offset,indexfile,location,type,maxentries,const in maps:
0723             index=self.getindex(indexfile)
0724             for i in index:
0725                 media[i+offset]={'name': index[i], 'origin': type}
0726 
0727         # secondly read the content file
0728         if key=='ringtone-index':
0729             type='ringers'
0730         else:
0731             type='images'
0732         index,_,_=self.get_content_file(key)
0733         for i in index:
0734             media[i]={'name': index[i], 'origin': type}
0735         results[key]=media
0736         return media
0737 
0738     def savemedia(self, mediakey, mediaindexkey, results, merge, reindexfunction):
0739         """Actually saves out the media
0740 
0741         @param mediakey: key of the media (eg 'wallpapers' or 'ringtones')
0742         @param mediaindexkey:  index key (eg 'wallpaper-index')
0743         @param maps: list index files and locations
0744         @param results: results dict
0745         @param merge: are we merging or overwriting what is there?
0746         @param reindexfunction: the media is re-indexed at the end.  this function is called to do it
0747         """
0748         content_changed=False
0749         media=results[mediakey].copy()
0750 
0751 
0752         #figure out if the origin we are interested in
0753         if mediaindexkey=='ringtone-index':
0754             type='ringers'
0755             content_type="Ringers"
0756             indexfile=self.protocolclass.ringerindex
0757             index_const=self.protocolclass.ringerconst
0758             max_media_entries=self.protocolclass.max_ringers
0759           # remove voice_memos
0760             for i in media.keys():
0761                 try:
0762                     if media[i]['origin']=='voice_memo':
0763                         del media[i]
0764                 except:
0765                     pass
0766         else:
0767             type='images'
0768             content_type="Screen Savers"
0769             indexfile=self.protocolclass.imageindex
0770             index_const=self.protocolclass.imageconst
0771             max_media_entries=self.protocolclass.max_images
0772             # remove camera images
0773             for i in media.keys():
0774                 try:
0775                     if media[i]['origin']=='camera':
0776                         del media[i]
0777                 except:
0778                     pass
0779 
0780         #read content file off the phone
0781         content={}
0782         try:
0783             buf=prototypes.buffer(self.getfilecontents(self.protocolclass.content_file_name))
0784             g=self.protocolclass.content_file()
0785             g.readfrombuffer(buf, logtitle="Read content file")
0786             for i in g.items:
0787                 # type !C always appears first
0788                 if i.type=='!C':
0789                     content[int(i.index1)]= {'C': i}
0790                 elif i.type=='!E':
0791                     content[int(i.index2)]['E']=i
0792         except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException):
0793             pass
0794         # get a list of files in the media directory so we can figure out what to delete
0795         # and what needs to be copied onto the phone
0796         dirlisting=self.getfilesystem(self.protocolclass.media_directory)
0797         # strip path from directory listing
0798         for i in dirlisting.keys():
0799             dirlisting[i[len(self.protocolclass.media_directory)+1:]]=dirlisting[i]
0800             del dirlisting[i]
0801 
0802         # build up list of existing media items into init, remove missing items from content list
0803         init={}
0804         for k in content.keys():
0805             if content[k]['C'].content_type==content_type:
0806                 index=k
0807                 name=content[k]['C'].name1
0808                 size=int(content[k]['C'].size)
0809                 data=None
0810                 # find the media content file, check that the size matches
0811                 for w in media:
0812                     if common.stripext(media[w]['name'])==name and media[w]['data']!=None:
0813                         size_fix=((3024+len(media[w]['data']))/1008)*1008
0814                         if size_fix==size:
0815                             data=media[w]['data']
0816                             del media[w]
0817                             break
0818                 # remove unused entries from index and filesystem
0819                 if not merge and data is None:
0820                     # delete the entry
0821                     del content[k]
0822                     content_changed=True
0823                     # we have to remove the data file, the gcd file and the url file
0824                     _fname='%02d.dat' % k
0825                     if _fname in dirlisting:
0826                         self.rmfile(self.protocolclass.media_directory+'/'+_fname)
0827                         del dirlisting[_fname]
0828                     gcdname='%02d.gcd' % k
0829                     if gcdname in dirlisting:
0830                         self.rmfile(self.protocolclass.media_directory+'/'+gcdname)
0831                         del dirlisting[gcdname]
0832                     urlname='%02d.url' % k
0833                     if urlname in dirlisting:
0834                         self.rmfile(self.protocolclass.media_directory+'/'+urlname)
0835                         del dirlisting[urlname]
0836                     continue
0837                 init[index]={'name': name, 'data': data}
0838 
0839         # go through adding new media not currently on the phone
0840         # assign these item keys in the content media
0841         applications={}
0842         for sp in range(100):
0843             if len(media.keys()) == 0:
0844                 break
0845             if sp not in content:
0846                 #found a hole
0847                 for w in media.keys():
0848                     C,E,add_to_index=self.make_new_media_entry(media[w], sp, type)
0849                     if add_to_index=='index':
0850                         content[sp]= {'C': C, 'E': E}
0851                         init[sp]=media[w]
0852                         content_changed=True
0853                     elif add_to_index=='content':
0854                         content[sp]= {'C': C, 'E': E}
0855                         applications[sp]=media[w]
0856                         content_changed=True
0857                     else:
0858                         self.log("Unknown media type for "+`media[w]['name']`+". Not written to phone.")
0859                         sp-=1
0860                     del media[w]
0861                     break
0862 
0863         if len(media.keys()):
0864             self.log("Phone index full, some media not written to phone")
0865 
0866         # write out the new index, there are two files on this phone
0867         # the content file and the media index, kinda dumb having two copies
0868         # of the same thing
0869         # content_file
0870         content_count=0
0871         keys=content.keys()
0872         keys.sort()
0873         cfile=self.protocolclass.content_file()
0874         # add in the !C types first
0875         for k in keys:
0876             content_count+=1
0877             cfile.items.append(content[k]['C'])
0878         for k in keys:
0879             cfile.items.append(content[k]['E'])
0880         #terminator
0881         entry=self.protocolclass.content_entry()
0882         entry.type='!F'
0883         cfile.items.append(entry)
0884         buffer=prototypes.buffer()
0885         cfile.writetobuffer(buffer, logtitle="Updated content file "+self.protocolclass.content_file_name)
0886         self.writefile(self.protocolclass.content_file_name, buffer.getvalue())
0887 
0888         countfile=self.protocolclass.content_count()
0889         countfile.count=`content_count`            
0890         buffer=prototypes.buffer()
0891         countfile.writetobuffer(buffer, logtitle="Updated content count file "+self.protocolclass.content_count_file_name)
0892         self.writefile(self.protocolclass.content_count_file_name, buffer.getvalue())
0893 
0894         # now write out the index file (this is like the verizon LG phones)
0895         keys=init.keys()
0896         keys.sort()
0897         ifile=self.protocolclass.indexfile()
0898         ifile.numactiveitems=len(keys)
0899         for k in keys:
0900             entry=self.protocolclass.indexentry()
0901             entry.index=k
0902             entry.const=index_const
0903             entry.name='%02d.dat' % k
0904             ifile.items.append(entry)
0905         # fill the remainder of the entries with unused keys
0906         for k in range(max_media_entries):
0907             if k in keys:
0908                 continue
0909             entry=self.protocolclass.indexentry()
0910             entry.index=k
0911             entry.const=index_const
0912             ifile.items.append(entry)
0913         buffer=prototypes.buffer()
0914         ifile.writetobuffer(buffer, logtitle="Updated index file "+indexfile)
0915         self.writefile(indexfile, buffer.getvalue())
0916 
0917         # Write out files - we compare against existing dir listing and don't rewrite if they
0918         # are the same size
0919         for k in keys:
0920             entry=init[k]
0921             data=entry.get("data", None)
0922             _fname='%02d.dat' % k
0923             gcdname='%02d.gcd' % k
0924             urlname='%02d.url' % k
0925             if data is None:
0926                 if _fname not in dirlisting:
0927                     self.log("Index error.  I have no data for "+entry['name']+" and it isn't already in the filesystem")
0928                 continue
0929             contentsize=len(data)
0930             urlcontents='file://'+entry['name']
0931             gcdcontents=self.makegcd(entry['name'],contentsize)
0932             if _fname in dirlisting and len(data)==dirlisting[_fname]['size']:
0933                 self.log("Skipping writing %s/%s as there is already a file of the same length" % (self.protocolclass.media_directory,entry['name']))
0934                 # repair gcd and url incase they are missing
0935                 if gcdname not in dirlisting:
0936                     self.writefile(self.protocolclass.media_directory+"/"+gcdname, gcdcontents)
0937                 if urlname not in dirlisting:
0938                     self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents)
0939                 continue
0940             self.writefile(self.protocolclass.media_directory+"/"+_fname, data)
0941             self.writefile(self.protocolclass.media_directory+"/"+gcdname, gcdcontents)
0942             self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents)
0943 
0944         # write out applications
0945         for k in applications.keys():
0946             entry=applications[k]
0947             data=entry.get("data", None)
0948             _fname='%02d.jar' % k
0949             jadname='%02d.jad' % k
0950             urlname='%02d.url' % k
0951             if data is None:
0952                 if _fname not in dirlisting:
0953                     self.log("Index error.  I have no data for "+entry['name']+" and it isn't already in the filesystem")
0954                 print "here2"
0955                 continue
0956             contentsize=len(data)
0957             urlcontents='file://'+entry['name']
0958             jadcontents=self.makejad(entry['name'],contentsize)
0959             print "here3"
0960             if _fname in dirlisting and len(data)==dirlisting[_fname]['size']:
0961                 self.log("Skipping writing %s/%s as there is already a file of the same length" % (self.protocolclass.media_directory,entry['name']))
0962                 # repair jad and url incase they are missing
0963                 if jadname not in dirlisting:
0964                     self.writefile(self.protocolclass.media_directory+"/"+jadname, jadcontents)
0965                 if urlname not in dirlisting:
0966                     self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents)
0967                 print "here4"
0968                 continue
0969             print "here5"
0970             self.writefile(self.protocolclass.media_directory+"/"+_fname, data)
0971             self.writefile(self.protocolclass.media_directory+"/"+jadname, jadcontents)
0972             self.writefile(self.protocolclass.media_directory+"/"+urlname, urlcontents)
0973 
0974         del results[mediakey] # done with it
0975         reindexfunction(results)
0976         if content_changed:
0977             results["rebootphone"]=True
0978         return results
0979     
0980     def make_new_media_entry(self, entry, index, type):
0981         c=self.protocolclass.content_entry()
0982         e=self.protocolclass.content_entry()
0983         name=common.stripext(entry['name'])
0984         ext=common.getext(entry['name'])
0985         data=entry.get("data", None)
0986         add_to_index='index'
0987         c.type='!C'
0988         c.index1=index
0989         c.name1=name
0990         try:
0991             c.mime_type=self.__mimetype[ext]
0992         except:
0993             # bad file type
0994             add_to_index='none'
0995             return c, e, add_to_index
0996         if c.mime_type=='application/java-archive':
0997             c.content_type='Games'
0998             e.location_maybe='midlet:'+name
0999             add_to_index='content'
1000         elif type=='ringers':
1001             c.content_type='Ringers'
1002         else:
1003             c.content_type='Screen Savers'
1004         c.size=`((3024+len(data))/1008)*1008`
1005         e.type='!E'
1006         e.index2=index
1007         e.name2=name
1008         return c, e, add_to_index
1009 
1010     def makegcd(self,filename,size):
1011         "Build a GCD file for filename"
1012         ext=common.getext(filename.lower())
1013         noextname=common.stripext(filename)
1014         try:
1015             mimetype=self.__mimetype[ext]
1016             gcdcontent="Content-Type: "+mimetype+"\nContent-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file://"+filename+"\nContent-Size: "+`size`+"\nContent-Description: Content for V10044 LG PM225"+"\n\n\n"
1017         except:
1018             gcdcontent="Content-Name: "+noextname+"\nContent-Version: 1.0\nContent-Vendor: BitPim\nContent-URL: file://"+filename+"\nContent-Size: "+`size`+"\n\n\n"
1019         return gcdcontent
1020 
1021     def makejad(self,filename,size):
1022         "Build a JAD file for filename"
1023         ext=common.getext(filename.lower())
1024         noextname=common.stripext(filename)
1025         jadcontent="MIDlet-1: "+noextname+", "+noextname+".png, BitPim\nMIDlet-Jar-Size: "+`size`+"\nMIDlet-Jar-URL: "+filename+"\nMIDlet-Name: "+noextname+"\nMIDlet-Vendor: Unknown\nMIDlet-Version: 1.0\nMicroEdition-Configuration: CLDC-1.0\nMicroEdition-Profile: MIDP-1.0\nContent-Folder: Games\n\n\n"
1026         return jadcontent
1027 
1028 #----- Phone Detection -----------------------------------------------------------
1029 
1030     brew_version_file='ams/version.txt'
1031     brew_version_txt_key='ams_version.txt'
1032     my_model='PM225' 
1033 
1034     def getphoneinfo(self, phone_info):
1035         self.log('Getting Phone Info')
1036         try:
1037             s=self.getfilecontents('ams/version.txt')
1038             if s[:5]==self.my_model:
1039                 phone_info.append('Model:', self.my_model)
1040                 phone_info.append('ESN:', self.get_brew_esn())
1041                 req=p_brew.firmwarerequest()
1042                 #res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse)
1043                 #phone_info.append('Firmware Version:', res.firmware)
1044                 txt=self.getfilecontents("nvm/nvm/nvm_0000")[207:217]
1045                 phone_info.append('Phone Number:', txt)
1046         except:
1047             pass
1048         return
1049 
1050 
1051 parentprofile=com_lgvx4400.Profile
1052 class Profile(parentprofile):
1053     protocolclass=Phone.protocolclass
1054     serialsname=Phone.serialsname
1055     BP_Calendar_Version=3
1056     phone_manufacturer='LG Electronics Inc'
1057     phone_model='PM225'      # from Sprint
1058     brew_required=True
1059     RINGTONE_LIMITS= {
1060         'MAXSIZE': 250000
1061     }
1062 
1063     WALLPAPER_WIDTH=160
1064     WALLPAPER_HEIGHT=120
1065     MAX_WALLPAPER_BASENAME_LENGTH=30
1066     WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .-_"
1067     WALLPAPER_CONVERT_FORMAT="jpg"
1068     
1069     MAX_RINGTONE_BASENAME_LENGTH=30
1070     RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .-_"
1071     DIALSTRING_CHARS="[^0-9PT#*]"
1072 
1073     # our targets are the same for all origins
1074     imagetargets={}
1075     imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper",
1076                                       {'width': 160, 'height': 120, 'format': "JPEG"}))
1077     # delay auto-detection when the phone is plugged in (in seconds)
1078     autodetect_delay=3
1079 
1080     def convertphonebooktophone(self, helper, data):
1081         """Converts the data to what will be used by the phone
1082 
1083         @param data: contains the dict returned by getfundamentals
1084                      as well as where the results go"""
1085         results={}
1086 
1087         speeds={}
1088 
1089         for pbentry in data['phonebook']:
1090             if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES:
1091                 break
1092             e={} # entry out
1093             entry=data['phonebook'][pbentry] # entry in
1094             try:
1095                 # serials, can't use 0 as default as the phone uses this for the first entry
1096                 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', 0xFFFFFFFF)
1097 
1098                 e['serial1']=serial1
1099                 for ss in entry["serials"]:
1100                     if ss["sourcetype"]=="bitpim":
1101                         e['bitpimserial']=ss
1102                 assert e['bitpimserial']
1103 
1104                 # name
1105                 e['name']=helper.getfullname(entry.get('names', []),1,1,32)[0]
1106 
1107                 # categories/groups
1108                 cat=helper.makeone(helper.getcategory(entry.get('categories', []),0,1,32), None)
1109                 if cat is None:
1110                     e['group']=0
1111                 else:
1112                     key,value=self._getgroup(cat, data['groups'])
1113                     if key is not None:
1114                         e['group']=key
1115                     else:
1116                         # sorry no space for this category
1117                         e['group']=0
1118 
1119                 # email addresses
1120                 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,72)
1121                 e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "")
1122 
1123                 # url
1124                 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,74), "")
1125 
1126                 # memo (-1 is to leave space for null terminator - not all software puts it in, but we do)
1127                 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "")
1128 
1129                 # phone numbers
1130                 # there must be at least one email address or phonenumber
1131                 minnumbers=1
1132                 if len(emails): minnumbers=0
1133                 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS)
1134                 e['numberspeeds']=[]
1135                 e['numbertypes']=[]
1136                 e['numbers']=[]
1137                 for numindex in range(len(numbers)):
1138                     num=numbers[numindex]
1139                     # deal with type
1140                     b4=len(e['numbertypes'])
1141                     type=num['type']
1142                     for i,t in enumerate(self.protocolclass.numbertypetab):
1143                         if type==t:
1144                             e['numbertypes'].append(i)
1145                             break
1146                         if t=='none': # conveniently last entry
1147                             e['numbertypes'].append(i)
1148                             break
1149                     if len(e['numbertypes'])==b4:
1150                         # we couldn't find a type for the number
1151                         helper.add_error_message("%s has number %s of type %s, the phone does not support this type" % 
1152                                                 (e['name'], num['number'], num['type']))
1153                         continue
1154                     # deal with number
1155                     number=self.phonize(num['number'])
1156                     if len(number)==0:
1157                         # no actual digits in the number
1158                         continue
1159                     if len(number)>48: # get this number from somewhere sensible
1160                         # ::TODO:: number is too long and we have to either truncate it or ignore it?
1161                         number=number[:48] # truncate for moment
1162                     e['numbers'].append(number)
1163                     # deal with speed dial
1164                     sd=num.get("speeddial", 0xFF)
1165                     if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL:
1166                         e['numberspeeds'].append(sd)
1167                     else:
1168                         e['numberspeeds'].append(0xFF)
1169 
1170                 e['numberspeeds']=helper.filllist(e['numberspeeds'], 5, 0xFF)
1171                 e['numbertypes']=helper.filllist(e['numbertypes'], 5, 0)
1172                 e['numbers']=helper.filllist(e['numbers'], 5, "")
1173 
1174                 # ringtones, wallpaper
1175                 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None)
1176                 # e['msgringtone']=helper.getringtone(entry.get('ringtones', []), 'message', None)
1177                 e['wallpaper']=helper.getwallpaper(entry.get('wallpapers', []), 'call', None)
1178 
1179                 # flags
1180                 e['secret']=helper.getflag(entry.get('flags',[]), 'secret', False)
1181                 results[pbentry]=e
1182 
1183             except helper.ConversionFailed:
1184                 continue
1185 
1186         data['phonebook']=results
1187         return data
1188 
1189     _supportedsyncs=(
1190         ('phonebook', 'read', None),  # all phonebook reading
1191         ('calendar', 'read', None),   # all calendar reading
1192         ('wallpaper', 'read', None),  # all wallpaper reading
1193         ('ringtone', 'read', None),   # all ringtone reading
1194         ('call_history', 'read', None),# all call history list reading
1195         ('memo', 'read', None),        # all memo list reading
1196         ('sms', 'read', None),         # all SMS list reading
1197         ('phonebook', 'write', 'OVERWRITE'),  # only overwriting phonebook
1198         ('calendar', 'write', 'OVERWRITE'),   # only overwriting calendar
1199         ('wallpaper', 'write', 'MERGE'),      # merge and overwrite wallpaper
1200         ('wallpaper', 'write', 'OVERWRITE'),
1201         ('ringtone', 'write', 'MERGE'),      # merge and overwrite ringtone
1202         ('ringtone', 'write', 'OVERWRITE'),
1203         ('memo', 'write', 'OVERWRITE'),       # all memo list writing
1204         ('sms', 'write', 'OVERWRITE'),        # all SMS list writing
1205         )
1206 
1207     def QueryAudio(self, origin, currentextension, afi):
1208         # we don't modify any of these
1209         if afi.format in ("MIDI", "QCP"):
1210             return currentextension, afi
1211         # examine mp3
1212         if afi.format=="MP3":
1213             if afi.channels==1 and 8<=afi.bitrate<=128 and 16000<=afi.samplerate<=44100:
1214                 return currentextension, afi
1215         # convert it
1216         return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 'channels': 1, 'bitrate': 32, 'samplerate': 22050}))
1217 
1218 
1219     field_color_data={
1220         'phonebook': {
1221             'name': {
1222                 'first': 0, 'middle': 0, 'last': 0, 'full': 1,
1223                 'nickname': 0, 'details': 1 },
1224             'number': {
1225                 'type': 5, 'speeddial': 5, 'number': 5, 'details': 5 },
1226             'email': 3,
1227             'address': {
1228                 'type': 0, 'company': 0, 'street': 0, 'street2': 0,
1229                 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0,
1230                 'details': 0 },
1231             'url': 1,
1232             'memo': 1,
1233             'category': 1,
1234             'wallpaper': 1,
1235             'ringtone': 1,
1236             'storage': 0,
1237             },
1238         'calendar': {
1239             'description': True, 'location': False, 'allday': True,
1240             'start': True, 'end': True, 'priority': False,
1241             'alarm': True, 'vibrate': False,
1242             'repeat': True,
1243             'memo': False,
1244             'category': False,
1245             'wallpaper': False,
1246             'ringtone': True,
1247             },
1248         'memo': {
1249             'subject': True,
1250             'date': False,
1251             'secret': False,
1252             'category': False,
1253             'memo': True,
1254             },
1255         'todo': {
1256             'summary': False,
1257             'status': False,
1258             'due_date': False,
1259             'percent_complete': False,
1260             'completion_date': False,
1261             'private': False,
1262             'priority': False,
1263             'category': False,
1264             'memo': False,
1265             },
1266         }
1267 

Generated by PyXR 0.9.4