| Trees | Indices | Help |
|
|---|
|
|
1 import conduit
2 import conduit.utils as Utils
3 import conduit.dataproviders.DataProvider as DataProvider
4 import conduit.dataproviders.DataProviderCategory as DataProviderCategory
5 import conduit.dataproviders.HalFactory as HalFactory
6 import conduit.datatypes.Note as Note
7 import conduit.datatypes.Contact as Contact
8 import conduit.Exceptions as Exceptions
9
10 import xml.dom.minidom
11 import vobject
12
13 import logging
14 log = logging.getLogger("modules.SynCE")
15
16 import os.path
17 import traceback
18 import dbus
19 import threading
20 import gobject
21 import array
22
23 from gettext import gettext as _
24
25 SYNC_ITEM_CALENDAR = 0
26 SYNC_ITEM_CONTACTS = 1
27 SYNC_ITEM_EMAIL = 2
28 SYNC_ITEM_FAVORITES = 3
29 SYNC_ITEM_FILES = 4
30 SYNC_ITEM_MEDIA = 5
31 SYNC_ITEM_NOTES = 6
32 SYNC_ITEM_TASKS = 7
33
34 TYPETONAMES = {
35 SYNC_ITEM_CONTACTS : "Contacts",
36 SYNC_ITEM_CALENDAR : "Calendar",
37 SYNC_ITEM_TASKS : "Tasks"
38 }
39
40 CHANGE_ADDED = 1
41 CHANGE_MODIFIED = 4
42 CHANGE_DELETED = 3
43
44 MODULES = {
45 "SynceFactory" : { "type": "dataprovider-factory" },
46 }
47
49
50 SUPPORTED_PARTNERSHIPS = ("Calendar", "Contacts", "Tasks")
51
53 HalFactory.HalFactory.__init__(self, **kwargs)
54 self._found = False
55 self._item_types = {}
56 self._partnerships = []
57
60
62 self._partnerships = partnerships
63
66
69
71 #create partnerships for Contact, Calendar, Tasks
72 ids = [id for id, name in self._item_types.items() if str(name) in self.SUPPORTED_PARTNERSHIPS]
73 self.engine.CreatePartnership(
74 "Conduit", #partnership name
75 ids, #ids of those items to sync
76 reply_handler=self._create_partnership_rx,
77 error_handler=self._create_partnership_error
78 )
79
81 self._found = True
82
83 #call async so we dont block at startup
84 #reply_handler will be called with the method's return values as arguments; or
85 #the error_handler
86 self.engine = dbus.Interface(
87 dbus.SessionBus().get_object('org.synce.SyncEngine', '/org/synce/SyncEngine'),
88 'org.synce.SyncEngine'
89 )
90 self.engine.GetItemTypes(
91 reply_handler=self._get_item_types_rx,
92 error_handler=lambda *args: None
93 )
94 self.engine.GetPartnerships(
95 reply_handler=self._get_partnerships_rx,
96 error_handler=lambda *args: None
97 )
98
100 if props.has_key("sync.plugin") and props["sync.plugin"]=="synce":
101 self._found_device()
102 return True
103 return False
104
110
113
115
116 if self._found:
117 import gtk
118 import socket
119
120 vbox = gtk.VBox(False,5)
121 mod = gtk.ListStore(
122 gobject.TYPE_PYOBJECT, #parnership id
123 gobject.TYPE_PYOBJECT, #parnership guid
124 str,str,str) #device name, pc name, items
125 treeview = gtk.TreeView(mod)
126
127 #Three colums: device name, pc name, items
128 index = 2
129 for name in ("Device", "Computer", "Items to Synchronize"):
130 col = gtk.TreeViewColumn(
131 name,
132 gtk.CellRendererText(),
133 text=index)
134 treeview.append_column(col)
135 index = index + 1
136 vbox.pack_start(treeview,True,True)
137
138 btn = gtk.Button(None,gtk.STOCK_ADD)
139 btn.set_label("Create Partnership")
140 btn.connect("clicked", self._on_create_partnership_clicked, mod)
141 vbox.pack_start(btn, False, False)
142
143 #add the existing partnerships
144 for id,guid,name,hostname,devicename,storetype,items in self._partnerships:
145 mod.append((
146 id,
147 guid,
148 str(devicename),
149 str(hostname),
150 ", ".join([str(self._item_types[item]) for item in items]))
151 )
152 #disable partnership if one exists
153 if str(hostname) == socket.gethostname():
154 btn.set_sensitive(False)
155
156 return vbox
157 return None
158
161
163 """
164 Wrap the SyncEngine dbus foo (thinly)
165 Make it synchronous and (eventually) borg it so multiple dp's share one connection
166 """
167
169 self.engine = None
170 self.SyncEvent = threading.Event()
171 self.PrefillEvent = threading.Event()
172
176
180
182 if not self.engine:
183 self.bus = dbus.SessionBus()
184 proxy = self.bus.get_object("org.synce.SyncEngine", "/org/synce/SyncEngine")
185 self.engine = dbus.Interface(proxy, "org.synce.SyncEngine")
186 self.engine.connect_to_signal("Synchronized", lambda: gobject.idle_add(self._OnSynchronized))
187 self.engine.connect_to_signal("PrefillComplete", lambda: gobject.idle_add(self._OnPrefillComplete))
188
190 self.PrefillEvent.clear()
191 rc = self.engine.PrefillRemote(items)
192 if rc == 1:
193 self.PrefillEvent.wait(10)
194 log.info("Prefill: completed (rc=%d)" % rc)
195 return rc
196
198 self.SyncEvent.clear()
199 self.engine.Synchronize()
200 self.SyncEvent.wait(10)
201 log.info("Synchronize: completed")
202
204 return self.engine.GetRemoteChanges(type_ids)
205
207 self.engine.AcknowledgeRemoteChanges(acks)
208
210 self.engine.AddLocalChanges(chgset)
211
213 self.engine.FlushItemDB()
214
217
222
224 DataProvider.TwoWay.refresh(self)
225 self.engine = SyncEngineWrapper()
226 self.engine.Connect()
227 self.engine.Synchronize()
228
230 DataProvider.TwoWay.get_all(self)
231 self.objects = {}
232 self.engine.Prefill([TYPETONAMES[self._type_id_]])
233 chgs = self.engine.GetRemoteChanges([self._type_id_])
234 for guid, chgtype, data in chgs[self._type_id_]:
235 uid = array.array('B', guid).tostring()
236 blob = array.array('B', data).tostring()
237 self.objects[uid] = self._blob_to_data(uid, blob)
238
239 log.info("Got %s objects" % len(self.objects))
240 return self.objects.keys()
241
245
247 DataProvider.TwoWay.put(self, obj, overwrite, LUID)
248 existing = None
249 if LUID != None:
250 existing = self.get(LUID)
251 if existing != None:
252 comp = obj.compare(existing)
253 if comp == conduit.datatypes.COMPARISON_EQUAL:
254 log.info("objects are equal")
255 elif overwrite == True or comp == conduit.datatypes.COMPARISON_NEWER:
256 self.update(LUID, obj)
257 else:
258 raise Exceptions.SynchronizeConflictError(comp, obj, existing)
259 else:
260 LUID = self.add(obj)
261 return self.get(LUID).get_rid()
262
264 _uid = array.array('B')
265 _uid.fromstring(uid)
266 _blob = array.array('B')
267 _blob.fromstring(blob)
268 self.engine.AddLocalChanges({
269 self._type_id_: (
270 (_uid, chgtype, _blob),
271 )
272 })
273 # FIXME: This is a HACK to make it easy (ish) to return a RID in put()
274 if chgtype != CHANGE_DELETED:
275 self.objects[uid] = self._blob_to_data(uid,blob)
276 else:
277 del self.objects[uid]
278
280 LUID = Utils.uuid_string()
281 self._commit(LUID, CHANGE_ADDED, self._data_to_blob(obj))
282 return LUID
283
286
290
292 DataProvider.TwoWay.finish(self)
293 #self.engine.AcknowledgeRemoteChanges
294 self.engine.Synchronize()
295 self.engine.FlushItemDB()
296
302
306
308 return "synce-%d" % self._type_id_
309
311 _name_ = "Contacts"
312 _description_ = "Windows Mobile Contacts"
313 _module_type_ = "twoway"
314 _in_type_ = "contact"
315 _out_type_ = "contact"
316 _icon_ = "contact-new"
317 _type_id_ = SYNC_ITEM_CONTACTS
318 _configurable_ = False
319
321 parser = xml.dom.minidom.parseString(blob)
322 root = parser.getElementsByTagName("contact")[0]
323
324 c = Contact.Contact()
325 c.set_UID(uid)
326
327 def S(node):
328 if node and node[0].childNodes:
329 return node[0].firstChild.wholeText
330 return ""
331
332 for node in root.childNodes:
333 if node.nodeName == "FileAs":
334 pass
335 elif node.nodeName == "FormattedName":
336 try:
337 c.vcard.fn
338 except:
339 c.vcard.add('fn')
340 c.vcard.fn.value = S(node.getElementsByTagName('Content'))
341 elif node.nodeName == "Name":
342 family = S(node.getElementsByTagName('LastName'))
343 given = S(node.getElementsByTagName('FirstName'))
344 try:
345 c.vcard.n
346 except:
347 c.vcard.add('n')
348 c.vcard.n.value = vobject.vcard.Name(family=family, given=given)
349 elif node.nodeName == "Nickname":
350 pass
351 elif node.nodeName == "EMail":
352 email = c.vcard.add('email')
353 email.value = S(node.getElementsByTagName('Content'))
354 email.type_param = 'INTERNET'
355 elif node.nodeName == "Photo":
356 pass
357 elif node.nodeName == "Categories":
358 pass
359 elif node.nodeName == "Assistant":
360 pass
361 elif node.nodeName == "Manager":
362 pass
363 elif node.nodeName == "Organization":
364 pass
365 elif node.nodeName == "Spouse":
366 pass
367 elif node.nodeName == "Telephone":
368 tel = c.vcard.add('tel')
369 tel.value = S(node.getElementsByTagName('Content'))
370 for type_param in node.getElementsByTagName('Type'):
371 tel.params.setdefault('TYPE',[]).append(S([type_param]))
372 elif node.nodeName == "Title":
373 pass
374 elif node.nodeName == "Url":
375 pass
376 elif node.nodeName == "Uid":
377 pass
378 elif node.nodeName == "Revision":
379 pass
380 else:
381 log.warning("Unhandled node: %s" % node.nodeName)
382
383 return c
384
386 v = data.vcard
387 doc = xml.dom.minidom.Document()
388 node = doc.createElement("contact")
389 for chunk, value in v.contents.iteritems():
390 if chunk == "account":
391 pass
392 elif chunk == "tel":
393 for v in value:
394 t = doc.createElement("Telephone")
395 if 'TYPE' in v.params:
396 for type_param in v.params['TYPE']:
397 k = doc.createElement("Type")
398 k.appendChild(doc.createTextNode(type_param))
399 t.appendChild(k)
400 c = doc.createElement("Content")
401 c.appendChild(doc.createTextNode(v.value))
402 t.appendChild(c)
403 node.appendChild(t)
404 elif chunk == "bday":
405 pass
406 elif chunk == "n":
407 v = value[0]
408 n = doc.createElement("Name")
409 f = doc.createElement("FirstName")
410 f.appendChild(doc.createTextNode(v.value.given))
411 n.appendChild(f)
412 l = doc.createElement("LastName")
413 l.appendChild(doc.createTextNode(v.value.family))
414 n.appendChild(l)
415 a = doc.createElement("Additional")
416 n.appendChild(a)
417 p = doc.createElement("Prefix")
418 n.appendChild(p)
419 s = doc.createElement("Suffix")
420 n.appendChild(s)
421 node.appendChild(n)
422 elif chunk == "version":
423 pass
424 elif chunk == "org":
425 pass
426 elif chunk == "nickname":
427 pass
428 elif chunk == "email":
429 for v in value:
430 e = doc.createElement("EMail")
431 c = doc.createElement("Content")
432 c.appendChild(doc.createTextNode(v.value))
433 e.appendChild(c)
434 n.appendChild(e)
435 elif chunk == "fn":
436 v = value[0]
437 fn = doc.createElement("FormattedName")
438 c = doc.createElement("Content")
439 c.appendChild(doc.createTextNode(v.value))
440 fn.appendChild(c)
441 node.appendChild(fn)
442 else:
443 log.warning("Unhandled chunk (%s)" % chunk)
444
445 doc.appendChild(node)
446 return doc.toxml()
447
449 _name_ = "Calendar"
450 _description_ = "Windows Mobile Calendar"
451 _module_type_ = "twoway"
452 _in_type_ = "note"
453 _out_type_ = "note"
454 _icon_ = "contact-new"
455 _type_id_ = SYNC_ITEM_CALENDAR
456 _configurable_ = False
457
459 _name_ = "Tasks"
460 _description_ = "Windows Mobile Tasks"
461 _module_type_ = "twoway"
462 _in_type_ = "note"
463 _out_type_ = "note"
464 _icon_ = "contact-new"
465 _type_id_ = SYNC_ITEM_TASKS
466 _configurable_ = False
467
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0beta1 on Sat Aug 2 22:18:41 2008 | http://epydoc.sourceforge.net |