def _ProcessGetItemEstimate(self): req_doc = self._ReadXMLRequest() self.server.logger.debug( "_handle_get_item_estimate: request document is \n%s", req_doc.serialize("utf-8", 1)) rsp_doc = self._CreateWBXMLDoc( "GetItemEstimate", "http://synce.org/formats/airsync_wm5/getitemestimate") xp = req_doc.xpathNewContext() xp.xpathRegisterNs( "e", "http://synce.org/formats/airsync_wm5/getitemestimate") for n in xp.xpathEval("/e:GetItemEstimate/e:Collections/e:Collection"): rsp_node = rsp_doc.getRootElement().newChild( None, "Response", None) rsp_node.newChild(None, "Status", "1") collclass = xml2util.GetNodeValue( xml2util.FindChildNode(n, "Class")) folderID = xml2util.GetNodeValue( xml2util.FindChildNode(n, "CollectionId")) filter = xml2util.GetNodeValue( xml2util.FindChildNode(n, "FilterType")) keystr = xml2util.GetNodeValue(xml2util.FindChildNode( n, "SyncKey")) keyID, synckey = util.GetSyncKeyData(keystr) firstrequest = (synckey == 0) coll_node = rsp_node.newChild(None, "Collection", None) coll_node.newChild(None, "Class", collclass) coll_node.newChild(None, "CollectionId", folderID) # get change count here. TODO: Support filter-type and sync-key chcount = self.backend.QueryChangeCount(folderID, firstrequest) coll_node.newChild(None, "Estimate", str(chcount)) self.server.logger.debug( "_handle_get_item_estimate: response document is \n%s", rsp_doc.serialize("utf-8", 1)) self._SendWBXMLResponse(rsp_doc.serialize("utf-8", 0))
def _ProcessKeepalive(self): req_doc = self._ReadXMLRequest() self.server.logger.info("ProcessKeepAlive: request is \n%s", req_doc.serialize("utf-8", 1)) req_node = req_doc.getRootElement() interval = xml2util.GetNodeValue( xml2util.FindChildNode(req_node, "HeartbeatInterval")) self.server.logger.info("ProcessKeepAlive: requested interval -%s-" % interval) # we now know how long we have! if interval != "": ivalue = int(interval) else: ivalue = 60 # get this from config later? self.server.logger.info("ProcessKeepAlive: actual interval -%d-" % ivalue) gobject.timeout_add(1000 * ivalue, self._ProcessEndKeepalive) return
def _ProcessGetItemEstimate(self): req_doc = self._ReadXMLRequest() self.server.logger.debug( "_ProcessGetItemEstimate: request document is \n%s", req_doc.serialize("utf-8", 1)) rsp_doc = self._CreateWBXMLDoc( "GetItemEstimate", "http://synce.org/formats/airsync_wm5/getitemestimate") AvailableItemDBs = self.server.device.PshipManager.GetCurrentPartnership( ).deviceitemdbs xp = req_doc.xpathNewContext() xp.xpathRegisterNs( "e", "http://synce.org/formats/airsync_wm5/getitemestimate") for n in xp.xpathEval("/e:GetItemEstimate/e:Collections/e:Collection"): rsp_node = rsp_doc.getRootElement().newChild( None, "Response", None) rsp_node.newChild(None, "Status", "1") coll_cls = xml2util.GetNodeValue(xml2util.FindChildNode( n, "Class")) coll_id = xml2util.GetNodeValue( xml2util.FindChildNode(n, "CollectionId")) coll_filter = xml2util.GetNodeValue( xml2util.FindChildNode(n, "FilterType")) coll_key = xml2util.GetNodeValue( xml2util.FindChildNode(n, "SyncKey")) coll_node = rsp_node.newChild(None, "Collection", None) coll_node.newChild(None, "Class", coll_cls) coll_node.newChild(None, "CollectionId", coll_id) itemDB = AvailableItemDBs[SYNC_ITEM_CLASS_TO_ID[coll_cls]] coll_node.newChild(None, "Estimate", str(itemDB.GetLocalChangeCount())) self.server.logger.debug( "_ProcessGetItemEstimate: response document is \n%s", rsp_doc.serialize("utf-8", 1)) self._SendWBXMLResponse(rsp_doc)
def _ProcessFolderSync(self): req_doc = self._ReadXMLRequest() self.server.logger.debug( "_ProcessFolderSync: request document is\n %s", req_doc.serialize("utf-8", 1)) req_folder_node = xml2util.FindChildNode(req_doc, "FolderSync") req_key = xml2util.GetNodeValue( xml2util.FindChildNode(req_folder_node, "SyncKey")) if req_key != "0": raise ValueError("SyncKey in FolderSync request is not 0") rsp_doc = self._CreateWBXMLDoc( "FolderSync", "http://synce.org/formats/airsync_wm5/folderhierarchy") rsp_folder_node = rsp_doc.getRootElement() rsp_folder_node.newChild(None, "Status", "1") rsp_folder_node.newChild(None, "SyncKey", "{00000000-0000-0000-0000-000000000000}1") rsp_changes_node = rsp_folder_node.newChild(None, "Changes", None) cpship = self.server.device.PshipManager.GetCurrentPartnership() rsp_changes_node.newChild(None, "Count", str(len(cpship.info.folders))) for server_id, data in cpship.info.folders.items(): parent_id, display_name, type = data add_node = rsp_changes_node.newChild(None, "Add", None) add_node.newChild(None, "ServerId", server_id) add_node.newChild(None, "ParentId", str(parent_id)) add_node.newChild(None, "DisplayName", display_name) add_node.newChild(None, "Type", str(type)) self.server.logger.debug( "_ProcessFolderSync: response document is \n%s", rsp_doc.serialize("utf-8", 1)) self._SendWBXMLResponse(rsp_doc)
def _ProcessCreateFolder(self): req_doc = self._ReadXMLRequest() self.server.logger.debug( "ProcessSyncCommand: request document is \n%s", req_doc.serialize("utf-8", 1)) root = req_doc.getRootElement() SyncKey = xml2util.GetNodeValue(xml2util.FindChildNode( root, "SyncKey")) ParentID = xml2util.GetNodeValue( xml2util.FindChildNode(root, "ParentId")) DisplayName = xml2util.GetNodeValue( xml2util.FindChildNode(root, "DisplayName")) Type = xml2util.GetNodeValue(xml2util.FindChildNode(root, "Type")) self.server.logger.debug("New folder %s" % DisplayName) self.server.logger.debug("ParentId %s" % ParentID) newID = self.backend.AddFolder(DisplayName, ParentID) if newID == "": status = 0 newID = None else: status = 1 rsp_doc = self._CreateWBXMLDoc( "FolderCreate", "http://synce.org/formats/airsync_wm5/folderhierarchy") node = rsp_doc.getRootElement() node.newChild(None, "Status", str(status)) node.newChild(None, "SyncKey", self.backend.sync_key + "1") node.newChild(None, "ServerId", newID) self.server.logger.debug("ProcessCreateFolder: reply document is \n%s", rsp_doc.serialize("utf-8", 1)) self._SendWBXMLResponse(rsp_doc.serialize("utf-8", 0))
def _ProcessSync(self): req_doc = self._ReadXMLRequest() self.server.logger.debug("_ProcessSync: request document is \n%s", req_doc.serialize("utf-8", 1)) rsp_doc = self._CreateWBXMLDoc( "Sync", "http://synce.org/formats/airsync_wm5/airsync") rsp_colls_node = rsp_doc.getRootElement().newChild( None, "Collections", None) AvailableItemDBs = self.server.device.PshipManager.GetCurrentPartnership( ).deviceitemdbs xp = req_doc.xpathNewContext() xp.xpathRegisterNs("s", "http://synce.org/formats/airsync_wm5/airsync") for n in xp.xpathEval("/s:Sync/s:Collections/s:Collection"): coll_cls = xml2util.GetNodeValue(xml2util.FindChildNode( n, "Class")) coll_key = xml2util.GetNodeValue( xml2util.FindChildNode(n, "SyncKey")) coll_id = xml2util.GetNodeValue( xml2util.FindChildNode(n, "CollectionId")) first_request = (coll_key == "0") coll_key = "%s%d" % (coll_id, int(coll_key.split("}")[-1]) + 1) rsp_coll_node = rsp_colls_node.newChild(None, "Collection", None) rsp_coll_node.newChild(None, "Class", coll_cls) rsp_coll_node.newChild(None, "SyncKey", coll_key) rsp_coll_node.newChild(None, "CollectionId", coll_id) rsp_coll_node.newChild(None, "Status", "1") itemDB = AvailableItemDBs[SYNC_ITEM_CLASS_TO_ID[coll_cls]] if not first_request and itemDB.GetLocalChangeCount() > 0: window_size = int( xml2util.GetNodeValue( xml2util.FindChildNode(n, "WindowSize"))) changes = itemDB.QueryLocalChanges(window_size) if itemDB.GetLocalChangeCount() > 0: rsp_coll_node.newChild(None, "MoreAvailable", None) rsp_cmd_node = rsp_coll_node.newChild(None, "Commands", None) for itemID, change in changes: remID, change_type, data = change if change_type == CHANGE_ADDED: if remID == None: remID = util.generate_guid() rsp_change_node = rsp_cmd_node.newChild( None, CHANGE_TYPE_TO_NODE_NAME[change_type], None) rsp_change_node.newChild(None, "ServerId", remID) if change_type != CHANGE_DELETED: os_doc = libxml2.parseDoc(data) self.server.logger.debug( "_ProcessSync: converting item to airsync, source is \n%s", os_doc.serialize("utf-8", 1)) as_doc = formatapi.ConvertFormat( DIR_TO_AIRSYNC, itemDB.type, os_doc, self.server.device.config.config_Global. cfg["OpensyncXMLFormat"]) self.server.logger.debug( "_ProcessSync: converting item to airsync, result is \n%s", as_doc.serialize("utf-8", 1)) rsp_change_node.addChild(as_doc.getRootElement()) itemDB.AcknowledgeLocalChanges([(itemID, remID)]) rsp_responses_node = rsp_doc.getRootElement().newChild( None, "Responses", None) cmd_count = 0 xp.setContextNode(n) for req_cmd_node in xp.xpathEval("s:Commands/*"): cmd_name = req_cmd_node.name chg_type = CHANGE_TYPE_FROM_NODE_NAME[cmd_name] if chg_type == CHANGE_ADDED: rsp_response_node = rsp_responses_node.newChild( None, cmd_name, None) cid = xml2util.GetNodeValue( xml2util.FindChildNode(req_cmd_node, "ClientId")) rsp_response_node.newChild(None, "ClientId", cid) remID = util.generate_guid() rsp_response_node.newChild(None, "ServerId", remID) rsp_response_node.newChild(None, "Status", "1") else: remID = xml2util.GetNodeValue( xml2util.FindChildNode(req_cmd_node, "ServerId")) xml = u"" if chg_type in (CHANGE_ADDED, CHANGE_MODIFIED): app_node = xml2util.FindChildNode(req_cmd_node, "ApplicationData") self.server.logger.debug( "_ProcessSync: converting item from airsync, source is \n%s", app_node.serialize("utf-8", 1)) os_doc = formatapi.ConvertFormat( DIR_FROM_AIRSYNC, itemDB.type, app_node, self.server. device.config.config_Global.cfg["OpensyncXMLFormat"]) self.server.logger.debug( "_ProcessSync: converting item from airsync, result is \n%s", os_doc.serialize("utf-8", 1)) xml = os_doc.serialize("utf-8", 0) itemDB.AddRemoteChanges([(remID, chg_type, xml)]) cmd_count += 1 if rsp_responses_node.children: rsp_coll_node.addChild(rsp_responses_node) self.server.logger.debug("_ProcessSync: response document is \n%s", rsp_doc.serialize("utf-8", 1)) self._SendWBXMLResponse(rsp_doc)
def _ProcessFolderSync(self): req_doc = self._ReadXMLRequest() self.server.logger.debug( "_handle_foldersync: request document is\n %s", req_doc.serialize("utf-8", 1)) req_folder_node = xml2util.FindChildNode(req_doc, "FolderSync") keydata = xml2util.GetNodeValue( xml2util.FindChildNode(req_folder_node, "SyncKey")) keyID, keyval = util.GetSyncKeyData(keydata) initialsync = (keyval == 0) if keyval == 0: self.server.logger.info("_ProcessFolderSync : initial sync.") if keyID == "": keyID = self.backend.sync_key newKey = keyID + str(keyval + 1) changes = self.backend.QueryFolderChanges(initialsync) rsp_doc = self._CreateWBXMLDoc( "FolderSync", "http://synce.org/formats/airsync_wm5/folderhierarchy") rsp_folder_node = rsp_doc.getRootElement() rsp_folder_node.newChild(None, "Status", "1") rsp_folder_node.newChild(None, "SyncKey", newKey) rsp_changes_node = rsp_folder_node.newChild(None, "Changes", None) rsp_changes_node.newChild(None, "Count", str(len(changes))) for chg in changes: folderID, parentID, change, displayname, foldertype = chg if change == OBJECT_NEW: add_node = rsp_changes_node.newChild(None, "Add", None) add_node.newChild(None, "ServerId", folderID) add_node.newChild(None, "ParentId", parentID) add_node.newChild(None, "DisplayName", displayname) add_node.newChild(None, "Type", str(foldertype)) elif change == OBJECT_TODEL: add_node = rsp_changes_node.newChild(None, "Delete", None) add_node.newChild(None, "ServerId", folderID) elif change == OBJECT_CHANGED: add_node = rsp_changes_node.newChild(None, "Update", None) add_node.newChild(None, "ServerId", folderID) add_node.newChild(None, "ParentId", parentID) add_node.newChild(None, "DisplayName", displayname) add_node.newChild(None, "Type", str(foldertype)) else: self.server.logger.debug( "ProcessFolderSync: unsupported change type %s" % str(change)) self.backend.AcknowledgeFolderChanges(changes) self.server.logger.debug( "handle_foldersync: response document is \n%s", rsp_doc.serialize("utf-8", 1)) self._SendWBXMLResponse(rsp_doc.serialize("utf-8", 0))
def _ProcessSyncCommand(self): req_doc = self._ReadXMLRequest() self.server.logger.debug( "ProcessSyncCommand: request document is \n%s", req_doc.serialize("utf-8", 1)) rsp_doc = self._CreateWBXMLDoc( "Sync", "http://synce.org/formats/airsync_wm5/airsync") rsp_colls_node = rsp_doc.getRootElement().newChild( None, "Collections", None) xp = req_doc.xpathNewContext() xp.xpathRegisterNs("s", "http://synce.org/formats/airsync_wm5/airsync") for n in xp.xpathEval("/s:Sync/s:Collections/s:Collection"): collclass = xml2util.GetNodeValue( xml2util.FindChildNode(n, "Class")) keystr = xml2util.GetNodeValue(xml2util.FindChildNode( n, "SyncKey")) folderID = xml2util.GetNodeValue( xml2util.FindChildNode(n, "CollectionId")) delasmove = xml2util.GetNodeValue( xml2util.FindChildNode(n, "DeletesAsMoves")) getchanges = xml2util.GetNodeValue( xml2util.FindChildNode(n, "GetChanges")) # get the sync key ID and value from the sync key node keyID, synckey = util.GetSyncKeyData(keystr) newKey = util.GetIncrementedSyncKey(folderID, keystr) # capture the options for later. opt_node = xml2util.FindChildNode(n, "Options") options = mailconv.Options(opt_node) # On the very first sync, the SyncKey is "0". On subsequent collections, it # needs to be updated from the folder ID. Basically, if we are in a first # request, it means 'send everything down the wire'. This is handled # slightly differently in an OpenSync/sync-engine situation. first_request = (synckey == 0) # build the response block to _our_ changes rsp_coll_node = rsp_colls_node.newChild(None, "Collection", None) rsp_coll_node.newChild(None, "Class", collclass) rsp_coll_node.newChild(None, "SyncKey", newKey) rsp_coll_node.newChild(None, "CollectionId", folderID) rsp_coll_node.newChild(None, "Status", "1") wsnode = xml2util.FindChildNode(n, "WindowSize") if wsnode != None: window_size = int( xml2util.GetNodeValue( xml2util.FindChildNode(n, "WindowSize"))) else: window_size = 1000 changes = self.backend.QueryChanges(folderID, window_size) changecount = len(changes) converter = mailconv.MailConvert(folderID) if changecount > 0: if changecount == window_size: rsp_coll_node.newChild(None, "MoreAvailable", None) rsp_cmd_node = rsp_coll_node.newChild(None, "Commands", None) for itemID, change, message in changes: rsp_change_node = rsp_cmd_node.newChild( None, CHANGE_TYPE_TO_NODE_NAME[change], None) combinedID = util.GenerateCombinedID(folderID, itemID) rsp_change_node.newChild(None, "ServerId", combinedID) if change != OBJECT_TODEL: xmlmail = converter.MailToApplicationNode( itemID, message) self.server.logger.debug("document data is \n%s", xmlmail.serialize("utf-8", 1)) rsp_change_node.addChild(xmlmail) self.backend.AcknowledgeChanges(folderID, changes) rsp_responses_node = rsp_doc.getRootElement().newChild( None, "Responses", None) cmd_count = 0 xp.setContextNode(n) for req_cmd_node in xp.xpathEval("s:Commands/*"): cmd_name = req_cmd_node.name objtype = CHANGE_TYPE_FROM_NODE_NAME[cmd_name] if cmd_name == "Add": # Do we need to support this for mail? We do for other class types rsp_response_node = rsp_responses_node.newChild( None, "Add", None) cid = xml2util.GetNodeValue( xml2util.FindChildNode(req_cmd_node, "ClientId")) rsp_response_node.newChild(None, "ClientId", cid) remID = util.generate_guid() rsp_response_node.newChild(None, "ServerId", remID) rsp_response_node.newChild(None, "Status", "1") elif cmd_name == "Fetch": # we need to fetch an item rsp_response_node = rsp_responses_node.newChild( None, "Fetch", None) remID = xml2util.GetNodeValue( xml2util.FindChildNode(req_cmd_node, "ServerId")) status = 0 item = self.backend.FetchItem( util.MessageIDFromCombinedID(remID)) if item: itemID, change, message = item status = 1 xmlmail = converter.MailToApplicationNode(message) self.server.logger.debug( "Fetch: document data is \n%s", xmlmail.serialize("utf-8", 1)) rsp_response_node.newChild(None, "Status", "1") rsp_response_node.addChild(xmlmail) elif cmd_name == "Delete": remID = xml2util.GetNodeValue( xml2util.FindChildNode(req_cmd_node, "ServerId")) if self.backend.DeleteItem( folderID, util.MessageIDFromCombinedID(remID)) == False: self.server.logger.debug( "Delete: deleting non-existent message ID %s" % remID) else: self.server.logger.info( "Delete: deleted message ID %s" % remID) else: # TODO - We need to support 'Change' as this sets the 'Read' flag self.server.logger.info("Unsupported command %s" % cmd_name) cmd_count += 1 if rsp_responses_node.children: rsp_coll_node.addChild(rsp_responses_node) self.server.logger.debug("_handle_sync: response document is \n%s", rsp_doc.serialize("utf-8", 1)) self._SendWBXMLResponse(rsp_doc.serialize("utf-8", 0))