1 """
2 Synchronize with SIM card via DBUS (currently used as default contacts storage in SHR)
3
4 This file is part of Pisi.
5
6 Check these links for background information:
7 - U{http://git.freesmartphone.org/?p=specs.git;a=blob_plain;f=html/org.freesmartphone.GSM.SIM.html;hb=HEAD}
8 - U{http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html#data-types}
9 - U{http://wiki.openmoko.org/wiki/Dbus}
10
11 Pisi is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
15
16 Pisi is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with Pisi. If not, see <http://www.gnu.org/licenses/>.
23 """
24
25 from contacts import contacts
26 from pisiconstants import *
27 import pisiprogress
28 import pisitools
29
30 import dbus
31
32 DBUS_GSM_DEVICE = ["org.freesmartphone.ogsmd", "/org/freesmartphone/GSM/Device"]
33 """Addressing information in DBUS"""
34 DBUS_SIM = 'org.freesmartphone.GSM.SIM'
35 """Addressing information in DBUS"""
36 DBUS_CONTACTS = 'contacts'
37 """Addressing information in DBUS"""
38 DBUS_NAME_MOBILEPHONE_SUFFIX = '*'
39 """String to be appended to an entry when phone number is of type 'mobile'"""
40 DBUS_NAME_WORKPHONE_SUFFIX = '-'
41 """String to be appended to an entry when phone number is of type 'work'"""
42 DBUS_NAME_HOMEPHONE_SUFFIX = '+'
43 """String to be appended to an entry when phone number is of type 'home'"""
44
45 PHONE_TYPE_MOBILE = 0
46 PHONE_TYPE_HOME = 1
47 PHONE_TYPE_WORK = 2
48
50 """
51 The implementation of the interface L{contacts.AbstractContactSynchronizationModule} for SIM Card backend accesed via DBUS
52 """
53 - def __init__( self, modulesString, config, configsection, folder, verbose=False, soft=False):
54 """
55 Constructor
56
57 Super class constructor (L{contacts.AbstractContactSynchronizationModule.__init__}) is called.
58 Local variables are initialized.
59 The settings from the configuration file are loaded.
60
61 @param modulesString: is a small string to help you make a unique id. It is the two modules configuration-names concatinated.
62 @param config: is the configuration from ~/.pisi/conf. Use like config.get(configsection,'user')
63 @param configsection: is the configuration from ~/.pisi/conf. Use like config.get(configsection,'user')
64 @param folder: is a string with the location for where your module can save its own files if necessary
65 @param verbose: should make your module "talk" more
66 @param soft: should tell you if you should make changes (ie. save)
67 """
68 contacts.AbstractContactSynchronizationModule.__init__(self, verbose, soft, modulesString, config, configsection, "Contacts DBUS SIM")
69 self.verbose = verbose
70 self._determineSimLimitations()
71 self._availableIds = {}
72 for i in range (1, self._max_simentries + 1):
73 self._availableIds[i] = 1
74 self._idMappings = {}
75 pisiprogress.getCallback().verbose("DBUS_SIM module loaded")
76
78 """
79 Supporting function in order to auto-determine limitation of SIM card (max number of entries and max length of name)
80 """
81 bus = dbus.SystemBus()
82 gsm_device_obj = bus.get_object(DBUS_GSM_DEVICE[0], DBUS_GSM_DEVICE[1])
83 sim = dbus.Interface(gsm_device_obj,DBUS_SIM)
84 infos = sim.GetPhonebookInfo(DBUS_CONTACTS)
85 self._max_simentries = infos["max_index"]
86 self._name_maxlength = infos["name_length"]
87
89 """
90 Load all data from backend
91 """
92 pisiprogress.getCallback().verbose ("DBUS_SIM: Loading")
93 pisiprogress.getCallback().progress.push(0, 100)
94 pisiprogress.getCallback().verbose (" >SIM Card Limitations: %d entries maximum; no more than %d characters per name" %(self._max_simentries, self._name_maxlength))
95 bus = dbus.SystemBus()
96 gsm_device_obj = bus.get_object(DBUS_GSM_DEVICE[0], DBUS_GSM_DEVICE[1])
97 sim = dbus.Interface(gsm_device_obj,DBUS_SIM)
98 dbusContacts = sim.RetrievePhonebook(DBUS_CONTACTS, 1, self._max_simentries)
99 pisiprogress.getCallback().progress.setProgress(20)
100 pisiprogress.getCallback().update("Loading")
101 i = 0
102 for c in dbusContacts:
103 dbus_id = c[0]
104 name = c[1]
105 number = c[2]
106
107 del self._availableIds[dbus_id]
108
109 type = PHONE_TYPE_MOBILE
110 if name.endswith(DBUS_NAME_MOBILEPHONE_SUFFIX):
111 name = name[:len(name) - len(DBUS_NAME_MOBILEPHONE_SUFFIX)]
112 if name.endswith(DBUS_NAME_WORKPHONE_SUFFIX):
113 type = PHONE_TYPE_WORK
114 name = name[:len(name) - len(DBUS_NAME_WORKPHONE_SUFFIX)]
115 elif name.endswith(DBUS_NAME_HOMEPHONE_SUFFIX):
116 type = PHONE_TYPE_HOME
117 name = name[:len(name) - len(DBUS_NAME_HOMEPHONE_SUFFIX)]
118
119 atts = {}
120 title, first, last, middle = pisitools.parseFullName(name)
121 atts['title'] = title
122 atts['firstname'] = first
123 atts['lastname'] = last
124 atts['middlename'] = middle
125
126 id = contacts.assembleID(atts)
127 if self._allContacts.has_key(id):
128 c = self._allContacts[id]
129 if type == PHONE_TYPE_MOBILE:
130 c.attributes['mobile'] = number.strip()
131 elif type == PHONE_TYPE_WORK:
132 c.attributes['officePhone'] = number.strip()
133 elif type == PHONE_TYPE_HOME:
134 c.attributes['phone'] = number.strip()
135
136 else:
137 if type == PHONE_TYPE_MOBILE:
138 atts['mobile'] = number.strip()
139 elif type == PHONE_TYPE_WORK:
140 atts['officePhone'] = number.strip()
141 elif type == PHONE_TYPE_HOME:
142 atts['phone'] = number.strip()
143 c = contacts.Contact(id, atts)
144 self._allContacts[id] = c
145 self._idMappings[id] = []
146 self._idMappings[id].append(dbus_id)
147 i+=1
148 pisiprogress.getCallback().progress.setProgress(20 + ((i*80) / len(dbusContacts)))
149 pisiprogress.getCallback().update('Loading')
150 pisiprogress.getCallback().progress.drop()
151
153 """
154 Writing through: Adds a single value to the SIM Card
155 """
156 c = self.getContact(id)
157 fullName = pisitools.assembleFullName(c)
158 if len(fullName) > self._name_maxlength - len(DBUS_NAME_MOBILEPHONE_SUFFIX):
159 fullName = fullName[:self._name_maxlength - len(DBUS_NAME_MOBILEPHONE_SUFFIX)]
160
161 if len(self._availableIds) == 0:
162 return
163 try:
164 number = c.attributes['mobile']
165 if number and number != '':
166 myid = self._availableIds.keys()[0]
167 sim.StoreEntry(DBUS_CONTACTS, myid, fullName + DBUS_NAME_MOBILEPHONE_SUFFIX, number)
168 del self._availableIds[myid]
169 except KeyError:
170 pass
171
172
173 if len(self._availableIds) == 0:
174 return
175 try:
176 number = c.attributes['phone']
177 if number and number != '':
178 myid = self._availableIds.keys()[0]
179 sim.StoreEntry(DBUS_CONTACTS, myid, fullName + DBUS_NAME_HOMEPHONE_SUFFIX, number)
180 del self._availableIds[myid]
181 except KeyError:
182 pass
183
184
185 if len(self._availableIds) == 0:
186 return
187 try:
188 number = c.attributes['officePhone']
189 if number and number != '':
190 myid = self._availableIds.keys()[0]
191 sim.StoreEntry(DBUS_CONTACTS, myid, fullName + DBUS_NAME_WORKPHONE_SUFFIX, number)
192 del self._availableIds[myid]
193 except KeyError:
194 pass
195
197 """
198 Writing through: Removes a single value from the SIM card
199 """
200 for dbus_id in self._idMappings[id]:
201 sim.DeleteEntry(DBUS_CONTACTS, dbus_id)
202 self._availableIds[dbus_id] = 1
203
235