PyXR

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



0001 ### BITPIM
0002 ###
0003 ### Copyright (C) 2004 Vic Heintz <vheintz@rochester.rr.com>
0004 ### Copyright (C) 2004 Joe Pham <djpham@netzero.com>
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 
0010 ### $Id: com_samsungscha670.py 4365 2007-08-17 21:11:59Z djpham $
0011 
0012 ### The bulk of this code was borrowed from com_samsungscha650.py
0013 ### and modified so that operations work with the sch-a670.
0014 
0015 """Communicate with a Samsung SCH-A670"""
0016 
0017 # lib modules
0018 import re
0019 import sha
0020 import time
0021 
0022 # my modules
0023 import common
0024 import commport
0025 import com_brew
0026 import com_samsung
0027 import com_phone
0028 import conversions
0029 import fileinfo
0030 import nameparser
0031 import p_samsungscha670
0032 import prototypes
0033 
0034 
0035 class Phone(com_samsung.Phone):
0036     "Talk to the Samsung SCH-A670 Cell Phone"
0037 
0038     desc="SCH-A670"
0039     serialsname='scha670'
0040     protocolclass=p_samsungscha670
0041     parent_phone=com_samsung.Phone
0042 
0043     __groups_range=xrange(5)
0044     __phone_entries_range=xrange(1,501)
0045     __pb_atpbokw_field_count=26                # This is a rename from __pb_max_entries in a650 code
0046                                         # which I found confusing
0047     __pb_max_speeddials=500
0048     __pb_entry=0
0049     __pb_mem_loc=1
0050     __pb_group=2
0051     __pb_ringtone=3
0052     __pb_name=4
0053     __pb_speed_dial=5
0054     __pb_home_num=7
0055     __pb_office_num=9
0056     __pb_mobile_num=11
0057     __pb_pager_num=13
0058     __pb_fax_num=15
0059     __pb_alias=17
0060     __pb_blanks=(19, 20)
0061     __pb_email=21
0062     __pb_image_assign=22                # 3, 4 or 5 depending on if picture ID assigned
0063     __pb_image_id=23                        # when above is 4, has caller-id wallpaper id
0064     __pb_contact_image=24                # Path to "Picture ID" image
0065     __pb_date_time_stamp=25
0066     __pb_numbers= ({'home': __pb_home_num},
0067                     {'office': __pb_office_num},
0068                     {'cell': __pb_mobile_num},
0069                     {'pager': __pb_pager_num},
0070                     {'fax': __pb_fax_num})
0071     __pb_max_name_len=22
0072     __pb_max_number_len=32
0073     __pb_max_emails=1
0074     __pb_max_email_chars=48        # Not currently used, but shouldn't it be?
0075     __pb_max_alias_chars=48        # Not currently used, but shouldn't it be?
0076     __wp_photo_dir="digital_cam"
0077     __wp_header_bytes=96        # Extra bytes in front of jpg
0078     __wp_ts_offset=47                # Timestamp in yyyymmddHHMMSS format starts here
0079     __wp_index_file = "nvm/nvm/brew_image"
0080     __rt_index_file = "nvm/nvm/brew_melody"
0081     __rt_dir = "brew/ringer"
0082     __wp_dir = "brew/shared"
0083 
0084     # The extra "User" entries are a temporary fix to allow round trip integrity of user
0085     # assigned ringtones for caller ID. Need to read /nvm/nvm/brew_melody to get true names
0086     # of ringtones.   
0087     builtinringtones=( 'Inactive',
0088                        'Bell 1', 'Bell 2', 'Bell 3', 'Bell 4', 'Bell 5',
0089                        'Melody 1', 'Melody 2', 'Melody 3', 'Melody 4', 'Melody 5',
0090                        'Melody 6', 'Melody 7', 'Melody 8', 'Melody 9', 'Melody 10',        
0091                        'User 00', 'User 01', 'User 02', 'User 03', 'User 04', 'User 05',
0092                        'User 06', 'User 07', 'User 08', 'User 09', 'User 10', 'User 11',
0093                        'User 12', 'User 13', 'User 14', 'User 15', 'User 16', 'User 17',
0094                        'User 18', 'User 19')
0095     allringtones={}
0096 
0097     # 'type name', 'type index name', 'origin', 'dir path', 'max file name length', 'max file name count'
0098     __ringtone_info=('ringtone', 'ringtone-index', 'ringtone', 'brew/ringer', 19, 20)
0099     __wallpaper_info=('wallpapers', 'wallpaper-index', 'images', 'mms_image', 19, 10)
0100 ##    __wallpaper_info=('wallpapers', 'wallpaper-index', 'wallpapers', 'brew/shared', 19, 10)
0101     __camerapix_info=('wallpapers', 'wallpaper-index', 'camera', 'digital_cam', 19, 40)
0102     __video0_info=('wallpapers', 'wallpaper-index', 'video', 'camcoder0', None, None)
0103     __video1_info=('wallpapers', 'wallpaper-index', 'video', 'camcoder1', None, None)
0104        
0105     def __init__(self, logtarget, commport):
0106 
0107         "Calls all the constructors and sets initial modes"
0108         com_samsung.Phone.__init__(self, logtarget, commport)
0109         self.mode=self.MODENONE
0110 
0111     def getfundamentals(self, results):
0112 
0113         """Gets information fundamental to interoperating with the phone and UI.
0114 
0115         Currently this is:
0116 
0117           - 'uniqueserial'     a unique serial number representing the phone
0118           - 'groups'           the phonebook groups
0119           - 'wallpaper-index'  map index numbers to names
0120           - 'ringtone-index'   map index numbers to ringtone names
0121 
0122         This method is called before we read the phonebook data or before we
0123         write phonebook data.
0124         """
0125         
0126         self.setmode(self.MODEPHONEBOOK)
0127 
0128         # use a hash of ESN and other stuff (being paranoid)
0129 
0130         self.log("Retrieving fundamental phone information")
0131         self.log("Reading phone serial number")
0132         results['uniqueserial']=sha.new(self.get_esn()).hexdigest()
0133 
0134         # now read groups
0135 
0136         self.log("Reading group information")
0137         g=self.get_groups(self.__groups_range)
0138         groups={}
0139         for i, g_i in enumerate(g):
0140             if len(g_i):
0141                 groups[i]={ 'name': g_i }
0142         results['groups']=groups
0143 
0144         # getting rintone-index
0145         self.setmode(self.MODEBREW)
0146         rt_index=RingtoneIndex(self)
0147         results['ringtone-index']=rt_index.get()
0148         
0149         # getting wallpaper-index
0150         img_index=ImageIndex(self)
0151         results['wallpaper-index']=img_index.get()
0152         
0153         self.setmode(self.MODEMODEM)
0154         self.log("Fundamentals retrieved")
0155 
0156         return results
0157 
0158     def get_builtin_ringtone_index(self):        # Joe's version
0159         r={}
0160         for k, n in enumerate(self.builtinringtones):
0161             r[k]={ 'name': n, 'origin': 'builtin' }
0162         return r
0163 
0164     def ATget_builtin_ringtone_index(self):        # Vic's version using AT command
0165         r={}
0166         s=self.comm.sendatcommand("#PUGSN?")
0167         for rt in s[1:]:
0168             this_r = rt.split(",")
0169             r[int(this_r[0])] = { 'name': this_r[2], 'origin': 'builtin' }
0170             self.allringtones[int(this_r[0])] = this_r[2]
0171         return r
0172 
0173     def get_user_rt_index(self, r):                # IDs on phone needed for caller-ID
0174         bi_cnt = len(r)
0175         rtlist = self.getfilecontents(self.__rt_index_file)
0176         offset=0
0177         while offset < (40 * 77):
0178             rtid = ord(rtlist[offset+1])
0179             rtlen = ord(rtlist[offset+74])
0180             rtname = rtlist[offset+23:offset+23+rtlen][len(self.__rt_dir)+1:]
0181             if rtlen > 0:
0182                 r[rtid + bi_cnt] = { 'name': rtname, 'origin': 'ringtone' }
0183                 self.allringtones[rtid + bi_cnt] = rtname
0184             offset+=77
0185         return r
0186 
0187     def get_wallpaper_index(self):                # IDs on phone needed for caller-ID
0188         wpi = {}
0189         imglist = self.getfilecontents(self.__wp_index_file)
0190         offset=0
0191         while offset < (30 * 76):
0192             imgid = ord(imglist[offset+1])
0193             imglen = ord(imglist[offset+73])
0194             imgname = imglist[offset+22:offset+22+imglen][len(self.__wp_dir)+1:]
0195             if imglen > 0:
0196                 wpi[imgid] = { 'name': imgname, 'origin': 'wallpaper' }            
0197             offset+=76
0198         return wpi
0199 
0200     def _get_phonebook(self, result, show_progress=True):
0201         """Reads the phonebook data.  The L{getfundamentals} information will
0202         already be in result."""
0203 
0204         self.setmode(self.MODEPHONEBOOK)
0205         c=len(self.__phone_entries_range)
0206         k=0
0207         pb_book={}
0208         for j in self.__phone_entries_range:
0209             # print "Getting entry: ", j
0210             pb_entry=self.get_phone_entry(j)
0211             if len(pb_entry)==self.__pb_atpbokw_field_count:
0212                 pb_book[k]=self._extract_phone_entry(pb_entry, result)
0213                 k+=1
0214                 # print pb_book[k], i
0215                 if show_progress:
0216                     self.progress(j, c, 'Reading '+pb_entry[self.__pb_name])
0217             else:
0218                 if show_progress:
0219                     self.progress(j, c, 'Blank entry: %d' % j)
0220         self.setmode(self.MODEMODEM)
0221 
0222         return pb_book
0223 
0224     def getphonebook(self,result):
0225         """Reads the phonebook data.  The L{getfundamentals} information will
0226         already be in result."""
0227         pb_book=self._get_phonebook(result)
0228         result['phonebook']=pb_book
0229         return pb_book
0230 
0231     def _extract_phone_entry(self, entry, fundamentals):
0232 
0233         res={}
0234         # serials
0235         res['serials']=[ {'sourcetype': self.serialsname,
0236                           'sourceuniqueid': fundamentals['uniqueserial'],
0237                           'serial1': entry[self.__pb_entry],
0238                           'serial2': entry[self.__pb_mem_loc] }]
0239         # only one name
0240         res['names']=[ {'full': unicode(entry[self.__pb_name].replace('"', ''),
0241                                         errors='ignore') } ]
0242         if len(entry[self.__pb_alias]):
0243             res['urls']=[ {'url': entry[self.__pb_alias] } ]
0244 
0245         # only one category
0246         g=fundamentals['groups']
0247         i=int(entry[self.__pb_group])
0248         res['categories']=[ {'category': g[i]['name'] } ]
0249 
0250         # emails
0251         s=entry[self.__pb_email].replace('"', '')
0252         if len(s):
0253                res['emails']=[ { 'email': s } ]
0254 
0255         # urls
0256         # private
0257         # memos
0258         
0259         # wallpapers
0260         if entry[self.__pb_image_assign]=='3':
0261             # the image is 'Gallery' with the string 'digital_cam/Imagexxx'
0262             res['wallpapers']=[ { 'wallpaper': entry[self.__pb_contact_image].split( "/")[1]+".jpg",
0263                                   'use': 'call' } ]
0264         elif entry[self.__pb_image_assign]=='4':
0265             # the entry is an index into wallpaper-index
0266             wp_index=fundamentals.get('wallpaper-index', {})
0267             try:
0268                 res['wallpapers']=[{ 'wallpaper': wp_index[int(entry[self.__pb_image_id])]['name'],
0269                                     'use': 'call'} ]
0270             except:
0271                 pass
0272             
0273         # ringtones
0274         try:
0275             rt_index=fundamentals.get('ringtone-index', {})
0276             res['ringtones']=[ { 'ringtone': rt_index[int(entry[self.__pb_ringtone])]['name'],
0277                              'use': 'call' } ]
0278         except:
0279             res['ringtones']=[ { 'ringtone': self.builtinringtones[0],
0280                                 'use': 'call' } ]
0281 
0282 
0283         # numbers
0284         speed_dial=int(entry[self.__pb_speed_dial])
0285         res['numbers']=[]
0286         for k, n in enumerate(self.__pb_numbers):
0287             for key in n:
0288                 if len(entry[n[key]]):
0289                     if speed_dial==k:
0290                         res['numbers'].append({ 'number': entry[n[key]],
0291                                         'type': key,
0292                                         'speeddial': int(entry[self.__pb_mem_loc])})
0293                     else:
0294                         res['numbers'].append({ 'number': entry[n[key]],
0295                                         'type': key })        
0296         # done
0297         return res
0298 
0299     def savephonebook(self, data):
0300         "Saves out the phonebook"
0301 
0302         pb_book=data['phonebook']
0303         pb_groups=data['groups']
0304         ringtone_index=data.get('ringtone-index', {})
0305         self.log('Validating phonebook entries.')
0306         del_entries=[]
0307         for k in pb_book:
0308             if not self.__validate_entry(pb_book[k], pb_groups, ringtone_index):
0309                 self.log('Invalid entry, entry will be not be sent.')
0310                 del_entries.append(k)
0311         for k in del_entries:
0312             self.log('Deleting entry '+\
0313                      nameparser.getfullname(pb_book[k]['names'][0]))
0314             del pb_book[k]
0315         self._has_duplicate_speeddial(pb_book)
0316         self.log('All entries validated')
0317 
0318         pb_locs=[False]*(len(self.__phone_entries_range)+1)
0319         pb_mem=[False]*len(pb_locs)
0320 
0321         # get existing phonebook from the phone
0322         self.log("Getting current phonebook from the phone")
0323         current_pb=self._get_phonebook(data)
0324     
0325         # check and adjust for speeddial changes
0326         self.log("Processing speeddial data")
0327         for k in pb_book:
0328             self._update_speeddial(pb_book[k])
0329 
0330         # check for deleted entries and delete them
0331         self.setmode(self.MODEPHONEBOOK)
0332         self.log("Processing deleted entries")
0333 
0334         for k1 in current_pb:
0335             s1=current_pb[k1]['serials'][0]['serial1']
0336             found=False
0337             for k2 in pb_book:
0338                 if self._same_serial1(s1, pb_book[k2]):
0339                     found=True
0340                     break
0341             if found:
0342                 pb_locs[int(current_pb[k1]['serials'][0]['serial1'])]=True
0343                 pb_mem[int(current_pb[k1]['serials'][0]['serial2'])]=True
0344             else:
0345                 self.log("Deleted item: "+\
0346                          nameparser.getfullname(current_pb[k1]['names'][0]))
0347                 # delete the entries from data and the phone
0348                 self.progress(0, 10, "Deleting "+\
0349                               nameparser.getfullname(\
0350                                   current_pb[k1]['names'][0]))
0351                 self._del_phone_entry(current_pb[k1])
0352         mem_idx, loc_idx = self.__pb_max_speeddials, 1
0353         
0354         # check for new entries & update serials
0355         self.log("Processing new & updated entries")
0356         serials_update=[]
0357         progresscur, progressmax=1,len(pb_book)
0358         for k in pb_book:
0359             if progresscur>len(self.__phone_entries_range):
0360                 self.log('Max phone entries exceeded: '+str(progresscur))
0361                 break
0362             e=pb_book[k]
0363             if not self._has_serial1(e):
0364                 while pb_locs[loc_idx]:
0365                     loc_idx += 1
0366                 pb_locs[loc_idx]=True
0367                 sd=self._get_speeddial(e)
0368                 if sd:
0369                     mem_index=sd
0370                     pb_mem[sd]=True
0371                 else:
0372                     while pb_mem[mem_idx]:
0373                         mem_idx -= 1
0374                     pb_mem[mem_idx]=True
0375                     mem_index=mem_idx
0376                     self._set_speeddial(e, mem_idx)
0377                 s1={ 'sourcetype': self.serialsname,
0378                           'sourceuniqueid': data['uniqueserial'],
0379                           'serial1': `loc_idx`,
0380                           'serial2': `mem_index` }
0381                 e['serials'].append(s1)
0382                 self.log("New entries: Name: "+\
0383                          nameparser.getfullname(e['names'][0])+", s1: "+`loc_idx`+", s2: "+`mem_index`)
0384                 serials_update.append((self._bitpim_serials(e), s1))
0385             self.progress(progresscur, progressmax, "Updating "+\
0386                           nameparser.getfullname(e['names'][0]))
0387             if not self._write_phone_entry(e, data):
0388                 self.log("Failed to save entry: "+\
0389                          nameparser.getfullname(e['names'][0]))
0390             progresscur += 1
0391 
0392         data["serialupdates"]=serials_update
0393         self.log("Done")
0394         self.setmode(self.MODEMODEM)
0395         return data
0396 
0397     # validate a phonebook entry, return True if good, False otherwise
0398     def __validate_entry(self, pb_entry, pb_groups, ringtone_index):
0399         try:
0400             # validate name
0401             name=nameparser.getfullname(pb_entry['names'][0]).replace('"', '')
0402             if len(name)>self.__pb_max_name_len:
0403                 name=name[:self.__pb_max_name_len]
0404             pb_entry['names'][0].setdefault('full', name)
0405             # validate url/alias
0406             url=pb_entry.get('urls', [{}])[0].get('url', None)
0407             if url is not None:
0408                 url=re.sub('[,"]', '', url)
0409                 if len(url)>self.__pb_max_alias_chars:
0410                     url=url[:self.__pb_max_alias_chars]
0411                 pb_entry['urls']=[ { 'url': url } ]
0412             # validate numbers
0413             has_number_or_email=False
0414             if pb_entry.has_key('numbers'):
0415                 for n in pb_entry['numbers']:
0416                     num=self.phonize(n['number'])
0417                     if len(num)>self.__pb_max_number_len:
0418                         num=num[:self.__pb_max_number_len]
0419                     if num != n['number']:
0420                         self.log('Updating number from '+n['number']+' to '+num)
0421                         n['number']=num
0422                     try:
0423                         self._get_number_type(n['type'])
0424                     except:
0425                         self.log(n['number']+': setting type to home.')
0426                         n['type']='home'
0427                     has_number_or_email=True
0428             # validate emails
0429             if pb_entry.has_key('emails'):
0430                 if len(pb_entry['emails'])>self.__pb_max_emails:
0431                     self.log(name+': Each entry can only have %s emails.  The rest will be ignored.'%str(self.__pb_max_emails))
0432                 email=pb_entry['emails'][0]['email'].replace('"', '')
0433                 if len(email)>self.__pb_max_email_chars:
0434                     email=email[:self.__pb_max_email_chars]
0435                 if email!=pb_entry['emails'][0]['email']:
0436                     pb_entry['emails'][0]['email']=email
0437                 has_number_or_email=True
0438             if not has_number_or_email:
0439                 self.log(name+': Entry has no numbers or emails')
0440                 # return False so this entry can be deleted from the dict
0441                 return False
0442             # validate groups
0443             found=False
0444             if pb_entry.has_key('categories') and len(pb_entry['categories']):
0445                 pb_cat=pb_entry['categories'][0]['category']
0446                 for k in pb_groups:
0447                     if pb_groups[k]['name']==pb_cat:
0448                         found=True
0449                         break
0450             if not found:
0451                 self.log(name+': category set to '+pb_groups[0]['name'])
0452                 pb_entry['categories']=[{'category': pb_groups[0]['name']}]
0453             # validate ringtones
0454             found=False
0455             if pb_entry.has_key('ringtones') and len(pb_entry['ringtones']):
0456                 pb_rt=pb_entry['ringtones'][0]['ringtone']
0457                 for k, rt in ringtone_index.items():
0458                     if pb_rt==rt['name']:
0459                         found=True
0460                         break
0461             if not found:
0462                 rt=ringtone_index[0]['name']
0463                 self.log(name+': ringtone set to '+rt)
0464                 pb_entry['ringtones']=[{'ringtone': rt,
0465                                         'use': 'call' }]
0466             # to to: validate wallpaper
0467                 
0468             # everything's cool
0469             return True
0470         except:
0471             raise
0472         
0473     def _has_duplicate_speeddial(self, pb_book):
0474         b=[False]*(self.__pb_max_speeddials+1)
0475         for k in pb_book:
0476             try:
0477                 for  k1, kk in enumerate(pb_book[k]['numbers']):
0478                     sd=kk['speeddial']
0479                     if sd and b[sd]:
0480                         # speed dial is in used, remove this one
0481                         del pb_book[k]['numbers'][k1]['speeddial']
0482                         self.log('speeddial %d exists, deleted'%sd)
0483                     else:
0484                         b[sd]=True
0485             except:
0486                 pass
0487         return False
0488 
0489     def _update_speeddial(self, pb_entry):
0490         try:
0491             s=self._my_serials(pb_entry)
0492             s1=int(s['serial2'])
0493             sd=self._get_speeddial(pb_entry)
0494             if not sd:
0495                 # speed dial not set, set it to current mem slot
0496                 self._set_speeddial(pb_entry, s1)
0497             elif sd!=s1:
0498                 # speed dial set to a different slot, mark it
0499                 self._del_my_serials(pb_entry)
0500         except:
0501             pass
0502 
0503     def _get_speeddial(self, pb_entry):
0504         n=pb_entry.get('numbers', [])
0505         for k in n:
0506             try:
0507                if k['speeddial']:
0508                    return k['speeddial']
0509             except:
0510                 pass
0511         return 0
0512 
0513     def _set_speeddial(self, pb_entry, sd):
0514         if not pb_entry.has_key('numbers'):
0515             # no numbers key, just return
0516             return
0517         for k in pb_entry['numbers']:
0518             if k.has_key('speeddial'):
0519                 k['speeddial']=sd
0520                 return
0521         pb_entry['numbers'][0]['speeddial']=sd
0522 
0523     def _del_phone_entry(self, pb_entry):
0524         try:
0525             return self.save_phone_entry(self._my_serials(pb_entry)['serial1'])
0526         except:
0527             return False
0528 
0529     def _same_serial1(self, s1, pb_entry):
0530         for k in pb_entry['serials']:
0531             if k['sourcetype']==self.serialsname and k.has_key('serial1'):
0532                 return k['serial1']==s1
0533         return False
0534 
0535     def _has_serial1(self, pb_entry):
0536         for k in pb_entry['serials']:
0537             if k['sourcetype']==self.serialsname and k.has_key('serial1'):
0538                 return True
0539         return False
0540 
0541     def _bitpim_serials(self, pb_entry):
0542         for k in pb_entry['serials']:
0543             if k['sourcetype']=="bitpim":
0544                 return k
0545         return {}
0546 
0547     def _del_my_serials(self, pb_entry):
0548         for k in range(len(pb_entry['serials'])):
0549             if pb_entry['serials'][k]['sourcetype']==self.serialsname:
0550                 del pb_entry['serials'][k]
0551                 return
0552 
0553     def _my_serials(self, pb_entry):
0554         for k in pb_entry['serials']:
0555             if k['sourcetype']==self.serialsname:
0556                 return k
0557         return {}
0558 
0559     def _get_number_type(self, type):
0560         n=self.__pb_numbers
0561         for k in range(len(n)):
0562             if n[k].has_key(type):
0563                 return k, n[k][type]
0564         raise common.IntegrityCheckFailed(self.desc, "Invalid Number Type")
0565 
0566     def _write_phone_entry(self, pb_entry, data):
0567 
0568         # setting up a list to send to the phone, all fields preset to '0'
0569         e=['0']*self.__pb_atpbokw_field_count        
0570 
0571         # setting the entry # and memory location #
0572         serials=self._my_serials(pb_entry)
0573         e[self.__pb_entry]=serials['serial1']
0574         e[self.__pb_mem_loc]=serials['serial2']
0575 
0576         # groups/categories
0577         groups=data.get('groups', {})
0578         e[self.__pb_group]='0'
0579         try:
0580             grp_name=pb_entry['categories'][0]['category']
0581             for k, n in groups.items():
0582                 if n.get('name', None)==grp_name:
0583                     e[self.__pb_group]=`k`
0584                     break
0585         except:
0586             pass
0587 
0588         # ringtones
0589         ringtone_index=data.get('ringtone-index', {})
0590         e[self.__pb_ringtone]='0'
0591         try:
0592             rt=pb_entry['ringtones'][0]['ringtone']
0593             for k, n in ringtone_index.items():
0594                 if rt==n.get('name', None):
0595                     e[self.__pb_ringtone]=`k`
0596                     break
0597         except:
0598             pass
0599 
0600         # name & alias/url
0601         e[self.__pb_name]='"'+nameparser.getfullname(\
0602             pb_entry['names'][0])+'"'
0603         url=''
0604         try:
0605             url=pb_entry['urls'][0]['url']
0606         except:
0607             pass
0608         e[self.__pb_alias]=url
0609         if len(url):
0610             e[self.__pb_alias+1]='0'
0611         else:
0612             e[self.__pb_alias+1]=''
0613 
0614         # numbers & speed dial
0615         # preset to empty
0616         for k in range(len(self.__pb_numbers)):
0617             for kk in self.__pb_numbers[k]:
0618                 e[self.__pb_numbers[k][kk]]=''
0619                 e[self.__pb_numbers[k][kk]+1]=''
0620         speed_dial='0'
0621         n=pb_entry.get('numbers', [])
0622         for k in range(len(n)):
0623             try:
0624                 nk=n[k]
0625                 kkk, kk=self._get_number_type(nk['type'])
0626             except:
0627                 # invalid type, default to 'home'
0628                 nk['type']='home'
0629                 kkk, kk=0, self.__pb_home_num
0630             e[kk],e[kk+1]=self.phonize(nk['number']),'0'
0631             try:
0632                 if nk['speeddial']:
0633                     speed_dial=`kkk`
0634             except:
0635                 pass
0636         e[self.__pb_speed_dial]=speed_dial
0637 
0638         # email
0639         email=''
0640         try:
0641             email=pb_entry['emails'][0]['email']
0642         except:
0643             pass
0644         e[self.__pb_email]='"'+email+'"'
0645 
0646         # wallpaper: phonebook entry "caller id" image
0647         # Default: No image assigned
0648         e[self.__pb_image_assign]='5'
0649         e[self.__pb_image_id]='0'
0650         e[self.__pb_contact_image]='""'
0651         try:
0652             imgName = pb_entry['wallpapers'][0]['wallpaper']
0653             image_index=data.get('wallpaper-index', {})
0654             for k, n in image_index.items():
0655                 if imgName==n.get('name', None):
0656                     if k>self.protocolclass.max_image_entries:
0657                         # this is a 'Gallery' entry
0658                         e[self.__pb_image_assign]='3'
0659                         e[self.__pb_contact_image]='"'+self.__wp_photo_dir+'/'+imgName.split('.')[0]+'"'
0660                         e[self.__pb_image_id]='0'
0661                     else:
0662                         # 'My Image' entry
0663                         e[self.__pb_image_assign]='4'
0664                         e[self.__pb_image_id]=`k`
0665                         e[self.__pb_contact_image]='""'
0666                     break
0667         except:
0668             pass
0669 
0670         for k in self.__pb_blanks:
0671             e[k]=''
0672 
0673         e[self.__pb_date_time_stamp]=self.get_time_stamp()
0674 
0675         # final check to determine if this entry has changed.
0676         # if it has not then do nothing an just return
0677         ee=self.get_phone_entry(int(e[self.__pb_entry]))
0678         if len(ee)==self.__pb_atpbokw_field_count:
0679             # DSV took the " out, need to put them back in for comparison
0680             ee[self.__pb_name]='"'+ee[self.__pb_name]+'"'
0681             ee[self.__pb_email]='"'+ee[self.__pb_email]+'"'
0682             ee[self.__pb_contact_image]='"'+ee[self.__pb_contact_image]+'"'
0683             # exclude the timestamp field
0684             k=self.__pb_atpbokw_field_count-1
0685             if e[:k]==ee[:k]:
0686                 return True
0687         return self.save_phone_entry('0,'+','.join(e))
0688 
0689     def getringtones(self, result):
0690         self.setmode(self.MODEBREW)
0691         m=FileEntries(self, self.__ringtone_info)
0692         rt_info=RingtoneIndex(self).get_download_info()
0693         r=m.get_media(result, rt_info)
0694         self.setmode(self.MODEMODEM)
0695         return r
0696 
0697     def saveringtones(self, result, merge):
0698         self.setmode(self.MODEBREW)
0699         m=FileEntries(self, self.__ringtone_info)
0700         result['rebootphone']=1 # So we end up back in AT mode
0701         r=m.save_media(result, RingtoneIndex(self).get_download_info())
0702         self.setmode(self.MODEMODEM)
0703         return r
0704 
0705     def getwallpapers(self, result):
0706         self.setmode(self.MODEBREW)
0707         m=FileEntries(self, self.__wallpaper_info)
0708         img_info=ImageIndex(self).get_download_info()
0709         m.get_media(result, img_info)
0710         FileEntries(self, self.__video0_info).get_video(result, 'Video001.avi')
0711         r=FileEntries(self, self.__video1_info).get_video(result, 'Video002.avi')
0712         self.setmode(self.MODEMODEM)
0713         return r
0714 
0715     def savewallpapers(self, result, merge):
0716         self.setmode(self.MODEBREW)
0717         m=FileEntries(self, self.__wallpaper_info)
0718         r=m.save_media(result, ImageIndex(self).get_download_info())
0719         result['rebootphone']=1
0720         self.setmode(self.MODEMODEM)
0721         return r
0722 
0723     getmemo=parent_phone._getmemo
0724     savememo=parent_phone._savememo
0725 
0726     gettodo=parent_phone._gettodo
0727     savetodo=parent_phone._savetodo
0728     
0729     getsms=parent_phone._getsms
0730     savesms=parent_phone._savesms
0731 
0732     getphoneinfo=parent_phone._getphoneinfo
0733 
0734     getmedia=None
0735 
0736 class Profile(com_samsung.Profile):
0737 
0738     serialsname='scha670'
0739 
0740     WALLPAPER_WIDTH=128
0741     WALLPAPER_HEIGHT=128
0742     MAX_WALLPAPER_BASENAME_LENGTH=19
0743     WALLPAPER_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ."
0744 ##    WALLPAPER_CONVERT_FORMAT="bmp"
0745     WALLPAPER_CONVERT_FORMAT="jpg"
0746     MAX_RINGTONE_BASENAME_LENGTH=19
0747     RINGTONE_FILENAME_CHARS="abcdefghijklmnopqrstuvwxyz0123456789_ ."
0748     RINGTONE_LIMITS= {
0749         'MAXSIZE': 30000
0750     }
0751     # use for auto-detection
0752     phone_manufacturer='SAMSUNG ELECTRONICS'
0753     phone_model='SCH-A670/164'
0754 
0755     def __init__(self):
0756         com_samsung.Profile.__init__(self)
0757 
0758     _supportedsyncs=(
0759         ('phonebook', 'read', None),  # all phonebook reading
0760         ('phonebook', 'write', 'OVERWRITE'),  # only overwriting phonebook
0761         ('calendar', 'read', None),   # all calendar reading
0762         ('calendar', 'write', 'OVERWRITE'),   # only overwriting calendar
0763         ('ringtone', 'read', None),   # all ringtone reading
0764         ('ringtone', 'write', 'OVERWRITE'),
0765         ('wallpaper', 'read', None),  # all wallpaper reading
0766         ('wallpaper', 'write', 'OVERWRITE'),
0767         ('memo', 'read', None),     # all memo list reading DJP
0768         ('memo', 'write', 'OVERWRITE'),  # all memo list writing DJP
0769         ('todo', 'read', None),     # all todo list reading DJP
0770         ('todo', 'write', 'OVERWRITE'),  # all todo list writing DJP
0771         ('sms', 'read', None),     # all SMS list reading DJP
0772         )
0773 
0774     if __debug__:
0775         _supportedsyncs+=(('sms', 'write', 'OVERWRITE'),)
0776 
0777     def convertphonebooktophone(self, helper, data):
0778         return data
0779     
0780     __audio_ext={ 'MIDI': 'mid', 'PMD': 'pmd', 'QCP': 'pmd' }
0781     def QueryAudio(self, origin, currentextension, afi):
0782         # we don't modify any of these
0783         if afi.format in ("MIDI", "PMD", "QCP"):
0784             for k,n in self.RINGTONE_LIMITS.items():
0785                 setattr(afi, k, n)
0786             return currentextension, afi
0787         d=self.RINGTONE_LIMITS.copy()
0788         d['format']='QCP'
0789         return ('pmd', fileinfo.AudioFileInfo(afi, **d))
0790 
0791     imageorigins={}
0792     imageorigins.update(common.getkv(com_samsung.Profile.stockimageorigins, "images"))
0793   
0794     imagetargets={}
0795     imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "wallpaper",
0796                                       {'width': 128, 'height': 128, 'format': "PNG"}))
0797     imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "fullscreen",
0798                                       {'width': 128, 'height': 160, 'format': "PNG"}))
0799     imagetargets.update(common.getkv(com_samsung.Profile.stockimagetargets, "pictureid",
0800                                       {'width': 96, 'height': 96, 'format': "JPEG"}))
0801 
0802     def GetImageOrigins(self):
0803         # Note: only return origins that you can write back to the phone
0804         return self.imageorigins
0805 
0806     def GetTargetsForImageOrigin(self, origin):
0807         # right now, supporting just 'images' origin
0808         if origin=='images':
0809             return self.imagetargets
0810 
0811 class FileEntries:
0812     def __init__(self, phone, info):
0813         self.__phone=phone
0814         self.__file_type, self.__index_type, self.__origin, self.__path, self.__max_file_len, self.__max_file_count=info
0815 
0816     def get_media(self, result, download_info=None):
0817         self.__phone.log('Getting media for type '+self.__file_type)
0818         if download_info is None:
0819             return self.__get_media_by_dir(result)
0820         else:
0821             return self.__get_media_by_index(result, download_info)
0822 
0823     def __get_media_by_dir(self, result):
0824         media=result.get(self.__file_type, {})
0825         idx=result.get(self.__index_type, {})
0826         file_cnt, idx_k=0, len(idx)
0827         path_len=len(self.__path)+1
0828         try:
0829             file_list=self.__phone.listfiles(self.__path)
0830             for k in file_list:
0831                 try:
0832                     index=k[path_len:]
0833                     # print k, index
0834                     media[index]=self.__phone.getfilecontents(k, True)
0835                     idx[idx_k]={ 'name': index, 'origin': self.__origin }
0836                     idx_k+=1
0837                     file_cnt += 1
0838                 except:
0839                     self.__phone.log('Failed to read file '+k)
0840         except:
0841             self.__phone.log('Failed to read dir '+self.__path)
0842         result[self.__file_type]=media
0843         result[self.__index_type]=idx
0844         if file_cnt > self.__max_file_count:
0845             self.__phone.log('This phone only supports %d %s.  %d %s read, weird things may happen.' % \
0846                                 (self.__max_file_count, self.__file_type,
0847                                  file_cnt, self.__file_type))
0848         return result
0849 
0850     def __get_media_by_index(self, result, rt_info):
0851         media=result.get(self.__file_type, {})
0852         media_index=result.get(self.__index_type, {})
0853         mms_img_path=self.__phone.protocolclass.mms_image_path
0854         mms_img_len=len(mms_img_path)
0855         for k, m in media_index.items():
0856             file_key=m.get('name', None)
0857             file_name=rt_info.get(file_key, None)
0858             if file_key is not None and file_name is not None:
0859                 try :
0860                     contents=self.__phone.getfilecontents(file_name, True)
0861                     img_origin=m.get('origin', None)
0862                     if img_origin=='images':
0863                         if file_name[:mms_img_len]==mms_img_path:
0864                             # mms image file, skip the header
0865                             contents=contents[52:]
0866                     elif img_origin=='camera':
0867                         # Camera pix file, need to be touched up
0868                         # patch code to make it work right now, need to recode
0869                         m['date']=\
0870                         ( int(contents[47:51]), int(contents[51:53]),
0871                           int(contents[53:55]), int(contents[55:57]),
0872                           int(contents[57:59]))
0873                         contents=contents[96:]
0874                         if contents[-1]=='\xff':
0875                             contents+='\x00'
0876                     media[file_key]=contents
0877                 except:
0878                     self.__phone.log('Failed to read file '+file_name)
0879                     if __debug__: raise
0880         result[self.__file_type]=media
0881         return result
0882 
0883     def get_video(self, result, video_file_name):
0884         if not conversions.helperavailable('bmp2avi'):
0885             # helper not available , just bail
0886             self.__phone.log('Helper bmp2avi not found, cannot retrieve '+\
0887                              video_file_name)
0888             return result
0889         self.__phone.log('Getting video file '+video_file_name)
0890         media=result.get(self.__file_type, {})
0891         idx=result.get(self.__index_type, {})
0892         tmp_avi_name=common.gettempfilename("avi")
0893         try:
0894             file_list=self.__phone.listfiles(self.__path)
0895         except com_brew.BrewNoSuchDirectoryException:
0896             file_list={}
0897         except:
0898             file_list={}
0899             if __debug__: raise
0900 
0901         if not len(file_list):
0902             # empty directory
0903             return result
0904         file_keys=file_list.keys()
0905         file_keys.sort();
0906         for k in file_keys:
0907             try:
0908                 conversions.convertjpgtoavi(self.__phone.getfilecontents(k, True)[96:],
0909                                             tmp_avi_name)
0910             except:
0911                 self.__phone.log('Failed to read video files')
0912                 if __debug__: raise
0913         # got the avi file, now prep to send it back
0914         if len(idx):
0915             idx_k=max(idx.keys())+1
0916         else:
0917             idx_k=0
0918         media[video_file_name]=open(tmp_avi_name, 'rb').read()
0919         idx[idx_k]={ 'name': video_file_name, 'origin': 'video' }
0920         result[self.__file_type]=media
0921         result[self.__index_type]=idx
0922         try:
0923             os.remove(tmp_avi_name)
0924         except:
0925             pass
0926         return result
0927                 
0928     __mms_max_file_name_len=35
0929     def __mms_header(self, img_name, img_type, img_data_len):
0930         if len(img_name)>=self.__mms_max_file_name_len:
0931             name=img_name[:self.__mms_max_file_name_len-1]
0932         else:
0933             name=img_name
0934         return str('\x06'+str(name)+\
0935                    '\x00'*(self.__mms_max_file_name_len-len(name))+\
0936                    common.LSBstr32(img_data_len)+chr(img_type)+\
0937                    '\x00'*11)
0938     def __mms_file_name(self):
0939         now = time.localtime(time.time())
0940         return '%02d%02d%02d%02d%02d%02d'%((now[0]%2000,)+now[1:6])
0941     def _to_mms_JPEG(self, img_name, img_data_len):
0942         return (self.__mms_file_name(),
0943                 self.__mms_header(img_name, 0, img_data_len))
0944     def _to_mms_BMP(self, img_name, img_data_len):
0945         return (self.__mms_file_name(),
0946                 self.__mms_header(img_name, 1, img_data_len))
0947     def _to_mms_PNG(self, img_name, img_data_len):
0948         return (self.__mms_file_name(),
0949                 self.__mms_header(img_name, 2, img_data_len))
0950     def _to_mms_GIF(self, img_name, img_data_len):
0951         return (self.__mms_file_name(),
0952                 self.__mms_header(img_name, 3, img_data_len))
0953        
0954     def save_media(self, result, dl_info):
0955         self.__phone.log('Saving media for type '+self.__file_type)
0956         media, idx=result[self.__file_type], result[self.__index_type]
0957         # check for files selected for deletion
0958         media_names=[media[k]['name'] for k in media]
0959         dl_info_keys=dl_info.keys()
0960         deleted_keys=[k for k in dl_info_keys if k not in media_names]
0961         new_keys=[k for k in media if media[k]['name'] not in dl_info_keys]
0962         # deleting files
0963         campix_file_path=self.__phone.protocolclass.cam_pix_file_path
0964         campix_file_len=len(campix_file_path)
0965         for k in deleted_keys:
0966             file_name=dl_info[k]
0967             if file_name[:campix_file_len]==campix_file_path:
0968                 # campera pix, do nothing
0969                 continue
0970             self.__phone.log('Deleting file: '+file_name)
0971             try:
0972                 self.__phone.rmfile(file_name)
0973             except:
0974                 self.__phone.log('Failed to delete file: '+file_name)
0975         # writing new files
0976         # make sure dir exists to write new files
0977         if len(new_keys):
0978             try:
0979                 self.__phone.mkdirs(self.__path)
0980                 # yet another hack
0981                 self.__phone.mkdirs('brew/shared')
0982             except:
0983                 pass
0984         file_count=0
0985         for k in new_keys:
0986             n=media[k]
0987             origin=n.get('origin', None)
0988             if origin is not None and origin != self.__origin:
0989                 continue
0990             if len(n['name']) > self.__max_file_len:
0991                 self.__phone.log('%s %s name is too long and not sent to phone'% \
0992                                  (self.__file_type, n['name']))
0993                 continue
0994             file_count+=1
0995             if file_count>self.__max_file_count:
0996                 # max # of files reached, bailing out
0997                 self.__phone.log('This phone only supports %d %s.  Save operation stopped.'%\
0998                                     (self.__max_file_count, self.__file_type))
0999                 break
1000             if self.__origin=='images':
1001                 file_info=fileinfo.identify_imagestring(n['data'])
1002                 if file_info.format in ('JPEG', 'GIF', 'BMP'):
1003                     # jpeg/gif/bmp file/picture ID files
1004                     (mms_file_name, file_hdr)=\
1005                                     getattr(self, '_to_mms_'+file_info.format)(
1006                                         n['name'], len(n['data']))
1007                     file_name=self.__path+'/'+mms_file_name
1008                     file_contents=file_hdr+n['data']
1009                 elif file_info.format=='PNG':
1010                     # wallpaper files
1011                     file_name='brew/shared/'+n['name']
1012                     # try to optimize png image files
1013                     file_contents=conversions.convertto8bitpng_joe(n['data'])
1014                 else:
1015                     # unknown file type, skip it
1016                     continue
1017             else:
1018                 file_name=self.__path+'/'+n['name']
1019                 file_contents=n['data']
1020             self.__phone.log('Writing file: '+file_name)
1021             try:
1022                 self.__phone.writefile(file_name, file_contents)
1023             except:
1024                 self.__phone.log('Failed to write file: '+file_name)
1025             media[k]['origin']=self.__origin
1026 
1027         return result
1028 
1029 class RingtoneIndex:
1030     __builtin_ringtones=( 'Inactive',
1031                        'Bell 1', 'Bell 2', 'Bell 3', 'Bell 4', 'Bell 5',
1032                        'Melody 1', 'Melody 2', 'Melody 3', 'Melody 4', 'Melody 5',
1033                        'Melody 6', 'Melody 7', 'Melody 8', 'Melody 9', 'Melody 10')
1034 
1035     def __init__(self, phone):
1036         self.__phone=phone
1037 
1038     def get_builtin_index(self):
1039         r={}
1040         for k, n in enumerate(self.__builtin_ringtones):
1041             r[k]={ 'name': n, 'origin': 'builtin' }
1042         return r
1043 
1044     def get_download_index(self):
1045         r={}
1046         try:
1047             rt_idx=self.__phone.protocolclass.ringtones()
1048             buf=prototypes.buffer(self.__phone.getfilecontents( \
1049                 self.__phone.protocolclass.ringtone_index_file_name))
1050             rt_idx.readfrombuffer(buf, logtitle="Read ringtone download index")
1051             idx=len(self.__builtin_ringtones)
1052             l=len(self.__phone.protocolclass.ringtone_file_path)+1
1053             for i in range(self.__phone.protocolclass.max_ringtone_entries):
1054                 e=rt_idx.entry[i]
1055                 if e.name_len:
1056                     r[idx+i]={ 'name': e.file_name[l:e.file_name_len],
1057                                'origin': 'ringtone' }
1058         except:
1059             pass
1060         return r
1061 
1062     def get(self):
1063         r=self.get_builtin_index()
1064         r.update(self.get_download_index())
1065         return r
1066 
1067     def get_download_info(self):
1068         r={}
1069         try:
1070             rt_idx=self.__phone.protocolclass.ringtones()
1071             buf=prototypes.buffer(self.__phone.getfilecontents( \
1072                 self.__phone.protocolclass.ringtone_index_file_name))
1073             rt_idx.readfrombuffer(buf, logtitle="Read ringtone download index")
1074             l=len(self.__phone.protocolclass.ringtone_file_path)+1
1075             for i in range(self.__phone.protocolclass.max_ringtone_entries):
1076                 e=rt_idx.entry[i]
1077                 if e.name_len:
1078                     r[e.file_name[l:e.file_name_len]]=e.file_name[:e.file_name_len]
1079         except:
1080             pass
1081         return r
1082 
1083 class ImageIndex:
1084 
1085     def __init__(self, phone):
1086         self.__phone=phone
1087 
1088     def get_download_index(self):
1089         r={}
1090         try:
1091             # first, read 'My Image' index
1092             img_idx=self.__phone.protocolclass.images()
1093             buf=prototypes.buffer(self.__phone.getfilecontents( \
1094                 self.__phone.protocolclass.image_index_file_name))
1095             img_idx.readfrombuffer(buf, logtitle="Read image download index")
1096             l=len(self.__phone.protocolclass.image_file_path)+1
1097             mms_img_path=self.__phone.protocolclass.mms_image_path
1098             mms_img_len=len(mms_img_path)
1099             for i in range(self.__phone.protocolclass.max_image_entries):
1100                 e=img_idx.entry[i]
1101                 if e.name_len and e.file_name_len:
1102                     if e.file_name[:mms_img_len]==mms_img_path:
1103                         # this is an mms_image file
1104                         idx_name=e.name[:e.name_len]
1105                     else:
1106                         # other image files
1107                         idx_name=e.file_name[l:e.file_name_len]
1108                     r[i]={ 'name': idx_name,
1109                            'origin': 'images' }
1110             # then read the camera pix image index ('Gallery')
1111             # starting index should be max_image_entries+1
1112             idx=self.__phone.protocolclass.max_image_entries+1
1113             try:
1114                 dir_l=self.__phone.listfiles(\
1115                     self.__phone.protocolclass.cam_pix_file_path)
1116             except com_brew.BrewNoSuchDirectoryException:
1117                 dir_l={}
1118             l=len(self.__phone.protocolclass.cam_pix_file_path)+1
1119             for f in dir_l:
1120                 r[idx]={ 'name': f[l:]+'.jpg', 'origin': 'camera' }
1121                 idx += 1
1122         except:
1123             raise
1124         return r
1125 
1126     def get(self):
1127         return self.get_download_index()
1128 
1129     def get_download_info(self):
1130         r={}
1131         try:
1132             # first, 'My Image' entries
1133             img_idx=self.__phone.protocolclass.images()
1134             buf=prototypes.buffer(self.__phone.getfilecontents( \
1135                 self.__phone.protocolclass.image_index_file_name))
1136             img_idx.readfrombuffer(buf, logtitle="Read image download index")
1137             l=len(self.__phone.protocolclass.image_file_path)+1
1138             mms_img_path=self.__phone.protocolclass.mms_image_path
1139             mms_img_len=len(mms_img_path)
1140             for i in range(self.__phone.protocolclass.max_image_entries):
1141                 e=img_idx.entry[i]
1142                 if e.name_len and e.file_name_len:
1143                     if e.file_name[:mms_img_len]==mms_img_path:
1144                         idx_name=e.name[:e.name_len]
1145                     else:
1146                         idx_name=e.file_name[l:e.file_name_len]
1147                     r[idx_name]=e.file_name[:e.file_name_len]
1148             # then 'Gallery' entries
1149             try:
1150                 dir_l=self.__phone.listfiles(\
1151                     self.__phone.protocolclass.cam_pix_file_path)
1152             except com_brew.BrewNoSuchDirectoryException:
1153                 dir_l={}
1154             l=len(self.__phone.protocolclass.cam_pix_file_path)+1
1155             for f in dir_l:
1156                 r[f[l:]+'.jpg']=f
1157         except:
1158             pass
1159         return r
1160 

Generated by PyXR 0.9.4