1 """
2 The original of this file was taken from a Conduit branch of John Carr (http://git.gnome.org/cgit/conduit/log/?h=syncml),
3 It was modified significantly for PISI use by Michael Pilgermann.
4
5 Major keys are:
6 - there is a bit of a problem as Syncml is actually the server (making all the comparing) - but PISI has its engine itself
7 - consequently, we are always using slow sync (all items have to be transmitted) and tell the Syncml server our changes afterwards
8
9 The entire implementation (of libsyncml) is asynchronous. We just tell the server, we want to sync - then the server comes back at some
10 point to ask for further information. So it's all based on handlers, which have to be registered in the beginning - all the work has
11 to be done in there.
12 """
13
14 import pysyncml
15 import enums
16 import threading
17 import uuid
18
19 SYNCML_LOGFILE = '/tmp/pisi-syncml.log'
20 """Log file for Syncml module debug output"""
21
23 """
24 There was this nice logging facility in the original
25
26 I only redirected to a file - there might always be some problems with this complex protocol.
27 """
30
32 self._file.write("Error: " + arg + "\n")
33
34 - def info(self, arg):
35 self._file.write("Info: " + arg + "\n")
36
38 self._file.write("Debug: " + arg + "\n")
39
40 log = Log()
41
43 """
44 This class is doing all the work
45
46 Don't instantiate it - there is a datastore and a connection missing; go for SyncmlContactsInterface instead.
47 """
48
49 _syncml_version_ = "1.1"
50
88
89
90 - def handle_change(self, sync_object, source, type, uid, data, size, userdata, err):
91 """ handle_change is called by libsyncml to tells us about changes on the server or device
92 we are synchronising to.
93
94 This WILL happen in a different thread to where sync is happening.
95 """
96 log.debug("got change (%s)" %(['UNKNOWN','ADD', 'REPLACE','DELETE'][type]))
97 self._rawContacts[uid] = data
98 err = pysyncml.Error()
99 self.syncobj.add_mapping(source, uid, uid, pysyncml.byref(err))
100 return 1
101
103 """ handle_devinf is called by libsyncml to tells us information such as device mfr and firmware
104 version of whatever we are syncing against.
105
106 This WILL happen in a different thread to where sync is happening.
107 There is a known bug with SE C902 where this is called twice - ignore the 2nd one or crashes
108 occur
109 """
110 return 1
111
113 """
114 200 is good ...
115 201 seems to indicate OK (sometimes not :()
116 211 - I think not
117 """
118 log.info("CB: Handle Change Status (ret: %s)" %code)
119 if code < 200 or 299 < code:
120 return 0
121 return 1
122
124 anchor = self.anchor[name] if name in self.anchor else None
125 log.debug("get_anchor('%s') returns %s" % (name, anchor or "None"))
126 return pysyncml.strdup(anchor) if anchor else None
127
129 log.info("CB: Set Anchor")
130 self.anchor[name] = value
131 return 1
132
136
138 err = pysyncml.Error()
139 for key in self._actions[0].keys():
140 LUID = str(uuid.uuid4())
141 self.syncobj.add_change(self._store_, enums.SML_CHANGE_ADD, LUID, self._actions[0][key], len(self._actions[0][key]), "", pysyncml.byref(err))
142 for key in self._actions[1].keys():
143 self.syncobj.add_change(self._store_, enums.SML_CHANGE_DELETE, key, "", 0, key, pysyncml.byref(err))
144 for key in self._actions[2].keys():
145 self.syncobj.add_change(self._store_, enums.SML_CHANGE_REPLACE, key, self._actions[2][key], len(self._actions[2][key]), key, pysyncml.byref(err))
146 self.syncobj.send_changes(pysyncml.byref(err))
147
149 err = pysyncml.Error()
150
151 self._setup_connection(err)
152 self._setup_datastore(err)
153
154 self.syncobj.set_option(enums.SML_DATA_SYNC_CONFIG_VERSION, self._syncml_version_, pysyncml.byref(err))
155 self.syncobj.set_option(enums.SML_DATA_SYNC_CONFIG_IDENTIFIER, self._syncml_identifier_, pysyncml.byref(err))
156 self.syncobj.set_option(enums.SML_DATA_SYNC_CONFIG_USE_WBXML, "1", pysyncml.byref(err))
157
158 self.syncobj.register_event_callback(self._handle_event, None)
159 self.syncobj.register_change_callback(self._handle_change, None)
160 self.syncobj.register_handle_remote_devinf_callback(self._handle_devinf, None)
161 self.syncobj.register_change_status_callback(self._handle_change_status)
162 self.syncobj.register_set_anchor_callback(self._handle_set_anchor, None)
163 self.syncobj.register_get_anchor_callback(self._handle_get_anchor, None)
164 self.syncobj.register_get_alert_type_callback(self._handle_get_alert_type, None)
165
166 if not self.syncobj.init(pysyncml.byref(err)):
167 log.error("Unable to prepare synchronisation")
168 return
169
170 if not self.syncobj.run(pysyncml.byref(err)):
171 log.error("Unable to synchronise")
172 log.error (err.message)
173 return
174
175 log.info("running sync..")
176 return err
177
196
197
199 """
200 Public Interface: download all contacts from server
201 """
202 self._syncml_run()
203 self._refresh_lock.wait(60)
204 self._refresh_lock.clear()
205 return self._rawContacts
206
208 """
209 Public Interface: apply changes on server
210 """
211 self._syncml_run()
212 self._refresh_lock.wait(60)
213 self._refresh_lock.clear()
214
215 self._actions = [adds, dels, mods]
216 self._syncml_run()
217 self._refresh_lock.wait(60)
218 self._refresh_lock.clear()
219
222
223
224
226 """
227 Encapsulates the connection establishment via HTTP
228 """
229
230 - def __init__(self, username, password):
234
244
245
263
264
282
283
296
298 """
299 Public Interface that should be used from outside
300 """
301 _mime_ = "text/x-calendar"
302 _syncml_version_ = "1.2"
303
304 - def __init__(self, url, username, password, store, syncml_identifier):
305 self._address_ = url
306 self._store_ = store
307 self._syncml_identifier_ = syncml_identifier
308 HttpClient.__init__(self, username, password)
309