Commit b14f6814 authored by Sebastien Robin's avatar Sebastien Robin

- add source_uri and target_uri fields in the dtml files

- change the subscriber property to use id, gid and rid
- encode the gid in base16
- change some parts to make VCard synchronization working
- remove some logs
- change the status implementation to use xpath expressions
- use utf-8 everywhere


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@14954 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 6b7e714c
...@@ -139,7 +139,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -139,7 +139,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xml = self.convertToXml(xml) xml = self.convertToXml(xml)
if xml is None: if xml is None:
return {'conflict_list':conflict_list, 'object':sub_object} return {'conflict_list':conflict_list, 'object':sub_object}
#LOG('addNode',0,'xml_reconstitued: %s' % str(xml))
# In the case where this new node is a object to add # In the case where this new node is a object to add
if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and \ if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and \
self.getSubObjectDepth(xml)==0: self.getSubObjectDepth(xml)==0:
...@@ -153,7 +152,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -153,7 +152,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if object_id is None: if object_id is None:
object_id = self.getAttribute(xml,'id') object_id = self.getAttribute(xml,'id')
docid = self.getObjectDocid(xml) docid = self.getObjectDocid(xml)
#LOG('addNode',0,'object_id: %s' % object_id)
if object_id is not None: if object_id is not None:
if sub_object is None: if sub_object is None:
try: try:
...@@ -181,7 +179,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -181,7 +179,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \ elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \
and self.getSubObjectDepth(xml)>=1: and self.getSubObjectDepth(xml)>=1:
sub_object_id = self.getSubObjectId(xml) sub_object_id = self.getSubObjectId(xml)
#LOG('addNode',0,'getSubObjectModification number: %s' % sub_object_id)
if previous_xml is not None and sub_object_id is not None: if previous_xml is not None and sub_object_id is not None:
#LOG('addNode',0,'previous xml is not none and also sub_object_id') #LOG('addNode',0,'previous xml is not none and also sub_object_id')
# Find the previous xml corresponding to this subobject # Find the previous xml corresponding to this subobject
...@@ -385,7 +382,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -385,7 +382,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
#subscriber_value=data)] # not needed any more #subscriber_value=data)] # not needed any more
# We will now apply the argument with the method edit # We will now apply the argument with the method edit
if args != {} and (isConflict==0 or force) and (not simulate): if args != {} and (isConflict==0 or force) and (not simulate):
#LOG('updateNode',0,'object._edit, args: %s' % str(args))
#object._edit(**args) #object._edit(**args)
self.editDocument(object=object,**args) self.editDocument(object=object,**args)
# It is sometimes required to do something after an edit # It is sometimes required to do something after an edit
...@@ -394,12 +390,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -394,12 +390,10 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if keyword == 'object': if keyword == 'object':
# This is the case where we have to call addNode # This is the case where we have to call addNode
#LOG('updateNode',0,'we will add sub-object')
conflict_list += self.addNode(xml=xml, object=object, force=force, conflict_list += self.addNode(xml=xml, object=object, force=force,
simulate=simulate, **kw)['conflict_list'] simulate=simulate, **kw)['conflict_list']
elif keyword == self.history_tag and not simulate: elif keyword == self.history_tag and not simulate:
# This is the case where we have to call addNode # This is the case where we have to call addNode
#LOG('updateNode',0,'we will add history')
conflict_list += self.addNode(xml=subnode,object=object,force=force, conflict_list += self.addNode(xml=subnode,object=object,force=force,
simulate=simulate,**kw)['conflict_list'] simulate=simulate,**kw)['conflict_list']
elif keyword in (self.local_role_tag,self.local_permission_tag) and not simulate: elif keyword in (self.local_role_tag,self.local_permission_tag) and not simulate:
...@@ -433,8 +427,9 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -433,8 +427,9 @@ class ERP5Conduit(XMLSyncUtilsMixin):
sub_xml = self.getSubObjectXupdate(xml) sub_xml = self.getSubObjectXupdate(xml)
#LOG('updateNode',0,'sub_xml: %s' % str(sub_xml)) #LOG('updateNode',0,'sub_xml: %s' % str(sub_xml))
# Then do the udpate # Then do the udpate
conflict_list += self.updateNode(xml=sub_xml, object=sub_object, force=force, conflict_list += self.updateNode(xml=sub_xml, object=sub_object,
previous_xml=sub_previous_xml, simulate=simulate, **kw) force=force, previous_xml=sub_previous_xml,
simulate=simulate, **kw)
elif previous_xml is None and xml is not None and sub_object_id is not None: elif previous_xml is None and xml is not None and sub_object_id is not None:
sub_object = None sub_object = None
try: try:
...@@ -470,7 +465,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -470,7 +465,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
This lookd inside the args dictionnary and then This lookd inside the args dictionnary and then
convert any unicode string to string convert any unicode string to string
""" """
#LOG('ERP5Conduit.getFormatedArgs',0,'args: %s' % str(args))
new_args = {} new_args = {}
for keyword in args.keys(): for keyword in args.keys():
data = args[keyword] data = args[keyword]
...@@ -646,7 +640,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -646,7 +640,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xml = self.convertToXml(xml) xml = self.convertToXml(xml)
for subnode in self.getElementNodeList(xml): for subnode in self.getElementNodeList(xml):
if subnode.nodeName==self.xml_object_tag: if subnode.nodeName==self.xml_object_tag:
LOG('getSub0bjectXml: object_id:',0,object_id)
if object_id == self.getAttribute(subnode,'id'): if object_id == self.getAttribute(subnode,'id'):
return subnode return subnode
return None return None
...@@ -700,7 +693,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -700,7 +693,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if xml is None: if xml is None:
return return
if type(xml) in (type('a'),type(u'a')): if type(xml) in (type('a'),type(u'a')):
#LOG('Conduit.convertToXml xml',0,repr(xml))
if type(xml) is type(u'a'): if type(xml) is type(u'a'):
xml = xml.encode('utf-8') xml = xml.encode('utf-8')
xml = Parse(xml) xml = Parse(xml)
...@@ -1065,27 +1057,19 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -1065,27 +1057,19 @@ class ERP5Conduit(XMLSyncUtilsMixin):
This is really usefull if you want to write your own Conduit. This is really usefull if you want to write your own Conduit.
""" """
conflict_list = [] conflict_list = []
#LOG('addNode, workflow_history isHistoryAdd:',0,self.isHistoryAdd(xml))
# We want to add a workflow action # We want to add a workflow action
wf_tool = getToolByName(object,'portal_workflow') wf_tool = getToolByName(object,'portal_workflow')
wf_id = self.getAttribute(xml,'id') wf_id = self.getAttribute(xml,'id')
if wf_id is None: # History added by xupdate if wf_id is None: # History added by xupdate
wf_id = self.getHistoryIdFromSelect(xml) wf_id = self.getHistoryIdFromSelect(xml)
#LOG('addNode, workflow_history id:',0,wf_id)
#LOG('addNode, workflow_history xml:',0,xml.toxml())#toxml isn't in 4Suite
#LOG('addNode, workflow_history xml.getElmentNodeList:',0,self.getElementNodeList(xml))
xml = self.getElementNodeList(xml)[0] xml = self.getElementNodeList(xml)[0]
#LOG('addNode, workflow_history id:',0,wf_id)
#LOG('addNode, workflow_history xml:',0,xml)
#for action in self.getWorkflowActionFromXml(xml): #for action in self.getWorkflowActionFromXml(xml):
status = self.getStatusFromXml(xml) status = self.getStatusFromXml(xml)
#LOG('addNode, status:',0,status) #LOG('addNode, status:',0,status)
add_action = self.isWorkflowActionAddable(object=object, add_action = self.isWorkflowActionAddable(object=object,
status=status,wf_tool=wf_tool, status=status,wf_tool=wf_tool,
wf_id=wf_id,xml=xml) wf_id=wf_id,xml=xml)
#LOG('addNode, workflow_history add_action:',0,add_action)
if add_action and not simulate: if add_action and not simulate:
#LOG('addNode, setting status:',0,'ok')
wf_tool.setStatusOf(wf_id,object,status) wf_tool.setStatusOf(wf_id,object,status)
# Specific CPS, try to remove duplicate lines in portal_repository._histories # Specific CPS, try to remove duplicate lines in portal_repository._histories
...@@ -1191,4 +1175,3 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -1191,4 +1175,3 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xml_string = buf.getvalue() xml_string = buf.getvalue()
buf.close() buf.close()
return xml_string return xml_string
...@@ -152,7 +152,7 @@ class Publication(Subscription): ...@@ -152,7 +152,7 @@ class Publication(Subscription):
constructors = (addPublication,) constructors = (addPublication,)
# Constructor # Constructor
def __init__(self, id, title, publication_url, destination_path, def __init__(self, id, title, publication_url, destination_path, source_uri,
query, xml_mapping, conduit, gpg_key, id_generator, gid_generator, query, xml_mapping, conduit, gpg_key, id_generator, gid_generator,
media_type, auth_required=False, authentication_format='', media_type, auth_required=False, authentication_format='',
authentication_type=''): authentication_type=''):
...@@ -162,6 +162,7 @@ class Publication(Subscription): ...@@ -162,6 +162,7 @@ class Publication(Subscription):
self.id = id self.id = id
self.publication_url = publication_url self.publication_url = publication_url
self.destination_path = destination_path self.destination_path = destination_path
self.setSourceURI(source_uri)
self.setQuery(query) self.setQuery(query)
self.xml_mapping = xml_mapping self.xml_mapping = xml_mapping
#self.list_subscribers = PersistentMapping() #self.list_subscribers = PersistentMapping()
......
...@@ -110,7 +110,7 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -110,7 +110,7 @@ class PublicationSynchronization(XMLSyncUtils):
if authentication_format == publication.getAuthenticationFormat(): if authentication_format == publication.getAuthenticationFormat():
if authentication_type == publication.getAuthenticationType(): if authentication_type == publication.getAuthenticationType():
decoded = subscriber.decode(authentication_format, data) decoded = subscriber.decode(authentication_format, data)
if decoded not in ('', None) and decoded.__contains__(':'): if decoded not in ('', None) and ':' in decoded:
(login, password) = decoded.split(':') (login, password) = decoded.split(':')
uf = self.getPortalObject().acl_users uf = self.getPortalObject().acl_users
for plugin_name, plugin in uf._getOb('plugins').listPlugins( for plugin_name, plugin in uf._getOb('plugins').listPlugins(
...@@ -124,6 +124,8 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -124,6 +124,8 @@ class PublicationSynchronization(XMLSyncUtils):
newSecurityManager(None, user) newSecurityManager(None, user)
subscriber.setUser(login) subscriber.setUser(login)
break break
else:
auth_code=self.UNAUTHORIZED
#in all others cases, the auth_code is set to UNAUTHORIZED #in all others cases, the auth_code is set to UNAUTHORIZED
...@@ -148,6 +150,13 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -148,6 +150,13 @@ class PublicationSynchronization(XMLSyncUtils):
subscriber.getSubscriptionUrl(), publication.getPublicationUrl(), subscriber.getSubscriptionUrl(), publication.getPublicationUrl(),
subscriber.getLastAnchor(), subscriber.getNextAnchor())) subscriber.getLastAnchor(), subscriber.getNextAnchor()))
cmd_id += 1 cmd_id += 1
else:
# chal message
xml(self.SyncMLChal(cmd_id, "SyncHdr",
publication.getPublicationUrl(), subscriber.getSubscriptionUrl(),
publication.getAuthenticationFormat(),
publication.getAuthenticationType(), self.AUTH_REQUIRED))
cmd_id += 1
xml(' </SyncBody>\n') xml(' </SyncBody>\n')
xml('</SyncML>\n') xml('</SyncML>\n')
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
......
...@@ -42,9 +42,10 @@ from zLOG import LOG ...@@ -42,9 +42,10 @@ from zLOG import LOG
import md5 import md5
try: try:
from base64 import b64encode, b64decode from base64 import b64encode, b64decode, b16encode, b16decode
except ImportError: except ImportError:
from base64 import encodestring as b64encode, decodestring as b64decode from base64 import encodestring as b64encode, decodestring as b64decode, \
encodestring as b16encode, decodestring as b16decode
#class Conflict(SyncCode, Implicit): #class Conflict(SyncCode, Implicit):
class Conflict(SyncCode, Base): class Conflict(SyncCode, Base):
...@@ -250,13 +251,16 @@ class Signature(Folder,SyncCode): ...@@ -250,13 +251,16 @@ class Signature(Folder,SyncCode):
isPortalContent = 0 # Make sure RAD generated accessors at the class level isPortalContent = 0 # Make sure RAD generated accessors at the class level
# Constructor # Constructor
def __init__(self,gid=None, id=None, status=None, xml_string=None,object=None): def __init__(self, id=None, rid=None, status=None, xml_string=None,
self.setGid(gid) object=None):
if object is not None: if object is not None:
self.setPath(object.getPhysicalPath()) self.setPath(object.getPhysicalPath())
self.setObjectId(object.getId())
else: else:
self.setPath(None) self.setPath(None)
self.setId(id) self.setId(id)
self.setGid(id)
self.setRid(rid)
self.status = status self.status = status
self.setXML(xml_string) self.setXML(xml_string)
self.partial_xml = None self.partial_xml = None
...@@ -280,7 +284,7 @@ class Signature(Folder,SyncCode): ...@@ -280,7 +284,7 @@ class Signature(Folder,SyncCode):
if temp_xml is not None: if temp_xml is not None:
# This happens when we have sent the xml # This happens when we have sent the xml
# and we just get the confirmation # and we just get the confirmation
self.setXML(self.getTempXML()) self.setXML(temp_xml)
self.setTempXML(None) self.setTempXML(None)
self.setPartialXML(None) self.setPartialXML(None)
self.setSubscriberXupdate(None) self.setSubscriberXupdate(None)
...@@ -367,7 +371,7 @@ class Signature(Folder,SyncCode): ...@@ -367,7 +371,7 @@ class Signature(Folder,SyncCode):
def getXML(self): def getXML(self):
""" """
set the XML corresponding to the object get the XML corresponding to the object
""" """
xml = getattr(self,'xml',None) xml = getattr(self,'xml',None)
if xml == '': if xml == '':
...@@ -437,18 +441,22 @@ class Signature(Folder,SyncCode): ...@@ -437,18 +441,22 @@ class Signature(Folder,SyncCode):
""" """
set the rid set the rid
""" """
if rid is type(u'a'):
rid = rid.encode('utf-8')
self.rid = rid self.rid = rid
def getRid(self): def getRid(self):
""" """
get the rid get the rid
""" """
return self.rid return getattr(self, 'rid', None)
def setId(self, id): def setId(self, id):
""" """
set the id set the id
""" """
if id is type(u'a'):
id = id.encode('utf-8')
self.id = id self.id = id
def getId(self): def getId(self):
...@@ -459,16 +467,32 @@ class Signature(Folder,SyncCode): ...@@ -459,16 +467,32 @@ class Signature(Folder,SyncCode):
def setGid(self, gid): def setGid(self, gid):
""" """
set the id set the gid
""" """
if gid is type(u'a'):
gid = gid.encode('utf-8')
self.gid = gid self.gid = gid
def getGid(self): def getGid(self):
""" """
get the id get the gid
""" """
return self.gid return self.gid
def setObjectId(self, id):
"""
set the id of the object associated to this signature
"""
if id is type(u'a'):
id = id.encode('utf-8')
self.object_id = id
def getObjectId(self):
"""
get the id of the object associated to this signature
"""
return getattr(self, 'object_id', None)
def setPartialXML(self, xml): def setPartialXML(self, xml):
""" """
Set the partial string we will have to Set the partial string we will have to
...@@ -545,7 +569,7 @@ class Signature(Folder,SyncCode): ...@@ -545,7 +569,7 @@ class Signature(Folder,SyncCode):
""" """
Returns the object corresponding to this signature Returns the object corresponding to this signature
""" """
return self.getParentValue().getObjectFromGid(self.getGid()) return self.getParentValue().getObjectIdGid(self.getObjectId())
def checkSynchronizationNeeded(self, object): def checkSynchronizationNeeded(self, object):
""" """
...@@ -643,8 +667,9 @@ class Subscription(Folder, SyncCode): ...@@ -643,8 +667,9 @@ class Subscription(Folder, SyncCode):
# Constructor # Constructor
def __init__(self, id, title, publication_url, subscription_url, def __init__(self, id, title, publication_url, subscription_url,
destination_path, query, xml_mapping, conduit, gpg_key, id_generator, destination_path, source_uri, target_uri, query, xml_mapping,
gid_generator, media_type, login, password): conduit, gpg_key, id_generator, gid_generator, media_type, login,
password):
""" """
We need to create a dictionnary of We need to create a dictionnary of
signatures of documents which belong to the synchronisation signatures of documents which belong to the synchronisation
...@@ -654,6 +679,8 @@ class Subscription(Folder, SyncCode): ...@@ -654,6 +679,8 @@ class Subscription(Folder, SyncCode):
self.publication_url = (publication_url) self.publication_url = (publication_url)
self.subscription_url = str(subscription_url) self.subscription_url = str(subscription_url)
self.destination_path = str(destination_path) self.destination_path = str(destination_path)
self.setSourceURI(source_uri)
self.setTargetURI(target_uri)
self.setQuery(query) self.setQuery(query)
self.setXMLMapping(xml_mapping) self.setXMLMapping(xml_mapping)
self.anchor = None self.anchor = None
...@@ -686,14 +713,30 @@ class Subscription(Folder, SyncCode): ...@@ -686,14 +713,30 @@ class Subscription(Folder, SyncCode):
""" """
self.title = value self.title = value
# Accessors def setSourceURI(self, value):
def getRemoteId(self, id, path=None):
""" """
Returns the remote id from a know local id setter for source_uri
Returns None if...
path allows to implement recursive sync
""" """
pass self.source_uri = value
def getSourceURI(self):
"""
getter for the source_uri (the local path of the subscription)
"""
return getattr(self, 'source_uri', None)
def setTargetURI(self, value):
"""
setter for target_uri
"""
self.target_uri = value
def getTargetURI(self):
"""
getter for the target_uri (the distant Publication we want to synchronize
with)
"""
return getattr(self, 'target_uri', None)
def getSynchronizationType(self, default=None): def getSynchronizationType(self, default=None):
""" """
...@@ -749,7 +792,7 @@ class Subscription(Folder, SyncCode): ...@@ -749,7 +792,7 @@ class Subscription(Folder, SyncCode):
self.last_message_id = message_id self.last_message_id = message_id
return True return True
def initLastMessageId(self, last_message_id=None): def initLastMessageId(self, last_message_id=0):
""" """
set the last message id to 0 set the last message id to 0
""" """
...@@ -767,12 +810,11 @@ class Subscription(Folder, SyncCode): ...@@ -767,12 +810,11 @@ class Subscription(Folder, SyncCode):
""" """
self.last_sent_message = xml self.last_sent_message = xml
def getLocalId(self, rid, path=None): def getDomainType(self):
""" """
Returns the local id from a know remote id return the ID
Returns None if...
""" """
pass return self.domain_type
def getId(self): def getId(self):
""" """
...@@ -780,12 +822,6 @@ class Subscription(Folder, SyncCode): ...@@ -780,12 +822,6 @@ class Subscription(Folder, SyncCode):
""" """
return self.id return self.id
def getDomainType(self):
"""
return the ID
"""
return self.domain_type
def setId(self, id): def setId(self, id):
""" """
set the ID set the ID
...@@ -934,13 +970,13 @@ class Subscription(Folder, SyncCode): ...@@ -934,13 +970,13 @@ class Subscription(Folder, SyncCode):
""" """
return the format of authentication return the format of authentication
""" """
return getattr(self, 'authentication_format', '') return getattr(self, 'authentication_format', 'b64')
def getAuthenticationType(self): def getAuthenticationType(self):
""" """
return the type of authentication return the type of authentication
""" """
return getattr(self, 'authentication_type', '') return getattr(self, 'authentication_type', 'syncml:auth-basic')
def setAuthenticationFormat(self, authentication_format): def setAuthenticationFormat(self, authentication_format):
""" """
...@@ -969,7 +1005,7 @@ class Subscription(Folder, SyncCode): ...@@ -969,7 +1005,7 @@ class Subscription(Folder, SyncCode):
# It might be a script python # It might be a script python
generator = getattr(object,gid_gen) generator = getattr(object,gid_gen)
o_gid = generator() # XXX - used to be o_gid = generator(object=object) which is redundant o_gid = generator() # XXX - used to be o_gid = generator(object=object) which is redundant
#LOG('getGidFromObject',0,'o_gid: %s' % repr(o_gid)) o_gid = b16encode(o_gid)
return o_gid return o_gid
def getObjectFromGid(self, gid): def getObjectFromGid(self, gid):
...@@ -977,16 +1013,13 @@ class Subscription(Folder, SyncCode): ...@@ -977,16 +1013,13 @@ class Subscription(Folder, SyncCode):
This tries to get the object with the given gid This tries to get the object with the given gid
This uses the query if it exist This uses the query if it exist
""" """
signature = self.getSignature(gid) signature = self.getSignatureFromGid(gid)
# First look if we do already have the mapping between # First look if we do already have the mapping between
# the id and the gid # the id and the gid
object_list = self.getObjectList() object_list = self.getObjectList()
destination = self.getDestination() destination = self.getDestination()
# LOG('getObjectFromGid', 0, 'self: %s' % self) if signature is not None and signature.getObjectId() is not None:
# LOG('getObjectFromGid',0,'gid: %s' % repr(gid)) o_id = signature.getObjectId()
# LOG('getObjectFromGid oject_list',0,object_list)
if signature is not None and signature.getId() is not None:
o_id = signature.getId()
o = None o = None
try: try:
o = destination._getOb(o_id) o = destination._getOb(o_id)
...@@ -998,7 +1031,18 @@ class Subscription(Folder, SyncCode): ...@@ -998,7 +1031,18 @@ class Subscription(Folder, SyncCode):
o_gid = self.getGidFromObject(o) o_gid = self.getGidFromObject(o)
if o_gid == gid: if o_gid == gid:
return o return o
# LOG('getObjectFromGid',0,'returning None') LOG('getObjectFromGid',0,'returning None')
return None
def getObjectFromId(self, id):
"""
return the object corresponding to the id
"""
object_list = self.getObjectList()
#XXX very slow with lot of objects
for object in object_list:
if object.getId() == id:
return object
return None return None
# def setOneWaySyncFromServer(self,value): # def setOneWaySyncFromServer(self,value):
...@@ -1195,28 +1239,83 @@ class Subscription(Folder, SyncCode): ...@@ -1195,28 +1239,83 @@ class Subscription(Folder, SyncCode):
""" """
add a Signature to the subscription add a Signature to the subscription
""" """
if signature.getGid() in self.objectIds(): if signature.getGid() in self.getGidList():
self._delObject(signature.getGid()) self.delSignature(signature.getGid())
self._setObject(signature.getGid(), aq_base(signature) ) self._setObject(signature.getGid(), aq_base(signature))
def delSignature(self, gid): def delSignature(self, gid):
""" """
add a Signature to the subscription del a Signature of the subscription
""" """
#del self.signatures[gid] #del self.signatures[gid]
self._delObject(gid) self._delObject(gid)
def getSignature(self, gid): def getSignatureFromObjectId(self, id):
""" """
add a Signature to the subscription return the signature corresponding to the gid
""" """
o = None o = None
if gid in self.objectIds(): # XXX very slow
o = self._getOb(gid) for signature in self.getSignatureList():
#if o is not None: if id == signature.getObjectId():
# return o.__of__(self) o = signature
break
return o
def getSignatureFromGid(self, gid):
"""
return the signature corresponding to the gid
"""
o = None
# XXX very slow
for signature in self.getSignatureList():
if gid == signature.getGid():
o = signature
break
return o
def getSignatureFromRid(self, rid):
"""
return the signature corresponding to the rid
"""
o = None
# XXX very slow
for signature in self.getSignatureList():
if rid == signature.getRid():
o = signature
break
return o return o
def getObjectIdList(self):
"""
Returns the list of gids from signature
"""
object_id_list = []
for signature in self.getSignatureList():
if signature.getObjectId() is not None:
object_id_list.append(signature.getObjectId())
return object_id_list
def getGidList(self):
"""
Returns the list of gids from signature
"""
gid_list = []
for signature in self.getSignatureList():
if signature.getGid() is not None:
gid_list.append(signature.getGid())
return gid_list
def getRidList(self):
"""
Returns the list of rids from signature
"""
rid_list = []
for signature in self.getSignatureList():
if signature.getRid() is not None:
rid_list.append(signature.getRid())
return rid_list
def getSignatureList(self): def getSignatureList(self):
""" """
add a Signature to the subscription add a Signature to the subscription
...@@ -1238,12 +1337,6 @@ class Subscription(Folder, SyncCode): ...@@ -1238,12 +1337,6 @@ class Subscription(Folder, SyncCode):
for id in self.objectIds(): for id in self.objectIds():
self._delObject(id) self._delObject(id)
def getGidList(self):
"""
Returns the list of ids from signature
"""
return self.objectIds()
def getConflictList(self): def getConflictList(self):
""" """
Return the list of all conflicts from all signatures Return the list of all conflicts from all signatures
...@@ -1280,20 +1373,6 @@ class Subscription(Folder, SyncCode): ...@@ -1280,20 +1373,6 @@ class Subscription(Folder, SyncCode):
new_list.append(o) new_list.append(o)
self.setRemainingObjectPathList(new_list) self.setRemainingObjectPathList(new_list)
# def getCurrentObject(self):
# """
# When we send some partial data, then we should
# always synchronize the same object until it is finished
# """
# getattr(self,'current_object',None)
#
# def setCurrentObject(self,object):
# """
# When we send some partial data, then we should
# always synchronize the same object until it is finished
# """
# setattr(self,'current_object',object)
def startSynchronization(self): def startSynchronization(self):
""" """
Set the status of every object as NOT_SYNCHRONIZED Set the status of every object as NOT_SYNCHRONIZED
......
...@@ -49,7 +49,7 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -49,7 +49,7 @@ class SubscriptionSynchronization(XMLSyncUtils):
# syncml header # syncml header
xml(self.SyncMLHeader(subscription.incrementSessionId(), xml(self.SyncMLHeader(subscription.incrementSessionId(),
subscription.incrementMessageId(), subscription.getPublicationUrl(), subscription.incrementMessageId(), subscription.getPublicationUrl(),
subscription.getSubscriptionUrl())) subscription.getSubscriptionUrl(), source_name=subscription.getLogin()))
# syncml body # syncml body
xml(' <SyncBody>\n') xml(' <SyncBody>\n')
...@@ -59,16 +59,15 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -59,16 +59,15 @@ class SubscriptionSynchronization(XMLSyncUtils):
# alert message # alert message
xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(), xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(),
subscription.getPublicationUrl(), subscription.getTargetURI(),
subscription.getDestinationPath(), subscription.getSourceURI(),
subscription.getLastAnchor(), subscription.getLastAnchor(),
subscription.getNextAnchor())) subscription.getNextAnchor()))
cmd_id += 1 cmd_id += 1
syncml_put = self.SyncMLPut(cmd_id, subscription)
xml(' <Put>\n') if syncml_put not in ('', None):
xml(' <CmdID>%s</CmdID>\n' % cmd_id) xml(syncml_put)
cmd_id += 1 cmd_id += 1
xml(' </Put>\n')
xml(' </SyncBody>\n') xml(' </SyncBody>\n')
xml('</SyncML>\n') xml('</SyncML>\n')
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
...@@ -79,20 +78,17 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -79,20 +78,17 @@ class SubscriptionSynchronization(XMLSyncUtils):
return {'has_response':1,'xml':xml_a} return {'has_response':1,'xml':xml_a}
def SubSync(self, id, msg=None, RESPONSE=None): def SubSync(self, subscription, msg=None, RESPONSE=None):
""" """
This is the synchronization method for the client This is the synchronization method for the client
""" """
#LOG('SubSync',0,'starting... id: %s' % str(id))
#LOG('SubSync',0,'starting... msg: %s' % str(msg))
response = None #check if subsync replies to this messages response = None #check if subsync replies to this messages
subscription = self.getSubscription(id)
if msg==None and (subscription.getSubscriptionUrl()).find('file')>=0: if msg==None and (subscription.getSubscriptionUrl()).find('file')>=0:
msg = self.readResponse(sync_id=id, msg = self.readResponse(sync_id=subscription.getSourceURI(),
from_url=subscription.getSubscriptionUrl()) from_url=subscription.getSubscriptionUrl())
if msg==None: if msg==None:
response = self.SubSyncInit(self.getSubscription(id)) response = self.SubSyncInit(subscription)
else: else:
xml_client = msg xml_client = msg
if isinstance(xml_client, str) or isinstance(xml_client, unicode): if isinstance(xml_client, str) or isinstance(xml_client, unicode):
...@@ -105,6 +101,9 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -105,6 +101,9 @@ class SubscriptionSynchronization(XMLSyncUtils):
if status_code == self.AUTH_REQUIRED: if status_code == self.AUTH_REQUIRED:
if self.checkChal(xml_client): if self.checkChal(xml_client):
authentication_format, authentication_type = self.getChal(xml_client) authentication_format, authentication_type = self.getChal(xml_client)
#LOG('auth_required :',0, 'format:%s, type:%s' % (authentication_format, authentication_type))
if authentication_format is not None and \
authentication_type is not None:
subscription.setAuthenticationFormat(authentication_format) subscription.setAuthenticationFormat(authentication_format)
subscription.setAuthenticationType(authentication_type) subscription.setAuthenticationType(authentication_type)
else: else:
...@@ -112,14 +111,14 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -112,14 +111,14 @@ class SubscriptionSynchronization(XMLSyncUtils):
authentication, but the authentication format is not find" authentication, but the authentication format is not find"
#LOG('readResponse', 0, 'Authentication required') #LOG('readResponse', 0, 'Authentication required')
response = self.SubSyncCred(id, xml_client) response = self.SubSyncCred(subscription, xml_client)
elif status_code == self.UNAUTHORIZED: elif status_code == self.UNAUTHORIZED:
#LOG('readResponse', 0, 'Bad authentication') LOG('readResponse', 0, 'Bad authentication')
return {'has_response':0,'xml':xml_client} return {'has_response':0,'xml':xml_client}
else: else:
response = self.SubSyncModif(self.getSubscription(id), xml_client) response = self.SubSyncModif(subscription, xml_client)
else: else:
response = self.SubSyncModif(self.getSubscription(id), xml_client) response = self.SubSyncModif(subscription, xml_client)
if RESPONSE is not None: if RESPONSE is not None:
...@@ -127,43 +126,43 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -127,43 +126,43 @@ class SubscriptionSynchronization(XMLSyncUtils):
else: else:
return response return response
def SubSyncCred (self, id, msg=None, RESPONSE=None): def SubSyncCred (self, subscription, msg=None, RESPONSE=None):
""" """
This method send crendentials This method send crendentials
""" """
#LOG('SubSyncCred',0,'starting... id: %s' % str(id))
#LOG('SubSyncCred',0,'starting... msg: %s' % str(msg))
cmd_id = 1 # specifies a SyncML message-unique command identifier cmd_id = 1 # specifies a SyncML message-unique command identifier
subscription = self.getSubscription(id)
xml_list = [] xml_list = []
xml = xml_list.append xml = xml_list.append
xml('<SyncML>\n') xml('<SyncML>\n')
# syncml header # syncml header
data = "%s:%s" % (subscription.getLogin(), subscription.getPassword()) data = "%s:%s" % (subscription.getLogin(), subscription.getPassword())
data=subscription.encode(subscription.getAuthenticationFormat(), data) data=subscription.encode(subscription.getAuthenticationFormat(), data)
xml(self.SyncMLHeader(subscription.getSessionId(), xml(self.SyncMLHeader(
subscription.incrementMessageId(), subscription.getPublicationUrl(), subscription.incrementSessionId(),
subscription.getSubscriptionUrl(), dataCred=data, subscription.incrementMessageId(),
subscription.getPublicationUrl(),
subscription.getSubscriptionUrl(),
source_name=subscription.getLogin(),
dataCred=data,
authentication_format=subscription.getAuthenticationFormat(), authentication_format=subscription.getAuthenticationFormat(),
authentication_type=subscription.getAuthenticationType())) authentication_type=subscription.getAuthenticationType()))
# syncml body # syncml body
xml(' <SyncBody>\n') xml(' <SyncBody>\n')
# We have to set every object as NOT_SYNCHRONIZED
subscription.startSynchronization()
# alert message # alert message
xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(), xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(),
subscription.getPublicationUrl(), subscription.getTargetURI(),
subscription.getDestinationPath(), subscription.getSourceURI(),
subscription.getLastAnchor(), subscription.getLastAnchor(),
subscription.getNextAnchor())) subscription.getNextAnchor()))
cmd_id += 1 cmd_id += 1
xml(self.SyncMLPut(cmd_id, subscription))
xml(' <Put>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1 cmd_id += 1
xml(' </Put>\n') xml(' <Final/>\n')
xml(' </SyncBody>\n') xml(' </SyncBody>\n')
xml('</SyncML>\n') xml('</SyncML>\n')
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
......
...@@ -41,6 +41,8 @@ class SyncCode(Persistent): ...@@ -41,6 +41,8 @@ class SyncCode(Persistent):
# SyncML Status Codes # SyncML Status Codes
SUCCESS = 200 SUCCESS = 200
ITEM_ADDED = 201
CHUNK_OK = 214 CHUNK_OK = 214
CONFLICT = 409 # A conflict is detected CONFLICT = 409 # A conflict is detected
CONFLICT_MERGE = 207 # We have merged the two versions, sending CONFLICT_MERGE = 207 # We have merged the two versions, sending
......
...@@ -161,7 +161,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -161,7 +161,7 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'manage_addPublication') 'manage_addPublication')
def manage_addPublication(self, title, publication_url, destination_path, def manage_addPublication(self, title, publication_url, destination_path,
query, xml_mapping, conduit, gpg_key, source_uri, query, xml_mapping, conduit, gpg_key,
synchronization_id_generator=None, gid_generator=None, synchronization_id_generator=None, gid_generator=None,
media_type=None, auth_required=0, authentication_format='', media_type=None, auth_required=0, authentication_format='',
authentication_type='', RESPONSE=None): authentication_type='', RESPONSE=None):
...@@ -174,7 +174,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -174,7 +174,7 @@ class SynchronizationTool( SubscriptionSynchronization,
folder = self.getObjectContainer() folder = self.getObjectContainer()
new_id = self.getPublicationIdFromTitle(title) new_id = self.getPublicationIdFromTitle(title)
pub = Publication(new_id, title, publication_url, destination_path, pub = Publication(new_id, title, publication_url, destination_path,
query, xml_mapping, conduit, gpg_key, source_uri, query, xml_mapping, conduit, gpg_key,
synchronization_id_generator, gid_generator, media_type, synchronization_id_generator, gid_generator, media_type,
auth_required, authentication_format, authentication_type) auth_required, authentication_format, authentication_type)
folder._setObject( new_id, pub ) folder._setObject( new_id, pub )
...@@ -187,7 +187,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -187,7 +187,8 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'manage_addSubscription') 'manage_addSubscription')
def manage_addSubscription(self, title, publication_url, subscription_url, def manage_addSubscription(self, title, publication_url, subscription_url,
destination_path, query, xml_mapping, conduit, gpg_key, destination_path, source_uri, target_uri, query,
xml_mapping, conduit, gpg_key,
synchronization_id_generator=None, gid_generator=None, synchronization_id_generator=None, gid_generator=None,
media_type=None, login=None, password=None, media_type=None, login=None, password=None,
RESPONSE=None): RESPONSE=None):
...@@ -201,7 +202,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -201,7 +202,8 @@ class SynchronizationTool( SubscriptionSynchronization,
folder = self.getObjectContainer() folder = self.getObjectContainer()
new_id = self.getSubscriptionIdFromTitle(title) new_id = self.getSubscriptionIdFromTitle(title)
sub = Subscription(new_id, title, publication_url, subscription_url, sub = Subscription(new_id, title, publication_url, subscription_url,
destination_path, query, xml_mapping, conduit, gpg_key, destination_path, source_uri, target_uri, query,
xml_mapping, conduit, gpg_key,
synchronization_id_generator, gid_generator, media_type, synchronization_id_generator, gid_generator, media_type,
login, password) login, password)
folder._setObject( new_id, sub ) folder._setObject( new_id, sub )
...@@ -214,7 +216,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -214,7 +216,7 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'manage_editPublication') 'manage_editPublication')
def manage_editPublication(self, title, publication_url, destination_path, def manage_editPublication(self, title, publication_url, destination_path,
query, xml_mapping, conduit, gpg_key, source_uri, query, xml_mapping, conduit, gpg_key,
synchronization_id_generator, gid_generator, synchronization_id_generator, gid_generator,
media_type=None, auth_required=0, media_type=None, auth_required=0,
authentication_format='', authentication_type='', authentication_format='', authentication_type='',
...@@ -226,6 +228,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -226,6 +228,7 @@ class SynchronizationTool( SubscriptionSynchronization,
pub.setTitle(title) pub.setTitle(title)
pub.setPublicationUrl(publication_url) pub.setPublicationUrl(publication_url)
pub.setDestinationPath(destination_path) pub.setDestinationPath(destination_path)
pub.setSourceURI(source_uri)
pub.setQuery(query) pub.setQuery(query)
pub.setConduit(conduit) pub.setConduit(conduit)
pub.setXMLMapping(xml_mapping) pub.setXMLMapping(xml_mapping)
...@@ -243,9 +246,9 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -243,9 +246,9 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'manage_editSubscription') 'manage_editSubscription')
def manage_editSubscription(self, title, publication_url, subscription_url, def manage_editSubscription(self, title, publication_url, subscription_url,
destination_path, query, xml_mapping, conduit, gpg_key, destination_path, source_uri, target_uri, query, xml_mapping, conduit,
synchronization_id_generator, gid_generator, media_type=None,login='', gpg_key, synchronization_id_generator, gid_generator, media_type=None,
password='', RESPONSE=None): login='', password='', RESPONSE=None):
""" """
modify a subscription modify a subscription
""" """
...@@ -253,6 +256,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -253,6 +256,8 @@ class SynchronizationTool( SubscriptionSynchronization,
sub.setTitle(title) sub.setTitle(title)
sub.setPublicationUrl(publication_url) sub.setPublicationUrl(publication_url)
sub.setDestinationPath(destination_path) sub.setDestinationPath(destination_path)
sub.setSourceURI(source_uri)
sub.setTargetURI(target_uri)
sub.setQuery(query) sub.setQuery(query)
sub.setConduit(conduit) sub.setConduit(conduit)
sub.setXMLMapping(xml_mapping) sub.setXMLMapping(xml_mapping)
...@@ -319,7 +324,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -319,7 +324,7 @@ class SynchronizationTool( SubscriptionSynchronization,
""" """
reset a subscription reset a subscription
""" """
self.SubSync(title) self.SubSync(self.getSubscription(title))
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions') RESPONSE.redirect('manageSubscriptions')
...@@ -371,7 +376,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -371,7 +376,7 @@ class SynchronizationTool( SubscriptionSynchronization,
def getSubscription(self, title): def getSubscription(self, title):
""" """
Returns the subscription with this id Returns the subscription with this title
""" """
for s in self.getSubscriptionList(): for s in self.getSubscriptionList():
if s.getTitle() == title: if s.getTitle() == title:
...@@ -489,7 +494,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -489,7 +494,7 @@ class SynchronizationTool( SubscriptionSynchronization,
subscriber_list = [domain] subscriber_list = [domain]
#LOG('getSynchronizationState, subscriber_list:',0,subscriber_list) #LOG('getSynchronizationState, subscriber_list:',0,subscriber_list)
for subscriber in subscriber_list: for subscriber in subscriber_list:
signature = subscriber.getSignature(o_id) signature = subscriber.getSignatureFromObjectId(o_id)
if signature is not None: if signature is not None:
state = signature.getStatus() state = signature.getStatus()
#LOG('getSynchronizationState:',0,'sub.dest :%s, state: %s' % \ #LOG('getSynchronizationState:',0,'sub.dest :%s, state: %s' % \
...@@ -514,7 +519,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -514,7 +519,7 @@ class SynchronizationTool( SubscriptionSynchronization,
subscriber = conflict.getSubscriber() subscriber = conflict.getSubscriber()
# get the signature: # get the signature:
#LOG('p_sync.applyPublisherValue, subscriber: ',0,subscriber) #LOG('p_sync.applyPublisherValue, subscriber: ',0,subscriber)
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid signature = subscriber.getSignatureFromObjectId(object.getId()) # XXX may be change for rid
copy_path = conflict.getCopyPath() copy_path = conflict.getCopyPath()
#LOG('p_sync.applyPublisherValue, copy_path: ',0,copy_path) #LOG('p_sync.applyPublisherValue, copy_path: ',0,copy_path)
signature.delConflict(conflict) signature.delConflict(conflict)
...@@ -690,7 +695,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -690,7 +695,7 @@ class SynchronizationTool( SubscriptionSynchronization,
subscriber = conflict.getSubscriber() subscriber = conflict.getSubscriber()
# get the signature: # get the signature:
#LOG('p_sync.setRemoteObject, subscriber: ',0,subscriber) #LOG('p_sync.setRemoteObject, subscriber: ',0,subscriber)
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid signature = subscriber.getSignatureFromObjectId(object.getId()) # XXX may be change for rid
# Import the conduit and get it # Import the conduit and get it
conduit_name = subscriber.getConduit() conduit_name = subscriber.getConduit()
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]), conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]),
...@@ -885,12 +890,22 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -885,12 +890,22 @@ class SynchronizationTool( SubscriptionSynchronization,
opener = urllib2.build_opener(proxy_handler, proxy_auth_handler, opener = urllib2.build_opener(proxy_handler, proxy_auth_handler,
auth_handler, urllib2.HTTPHandler) auth_handler, urllib2.HTTPHandler)
urllib2.install_opener(opener) urllib2.install_opener(opener)
to_encode = {'text':xml,'sync_id':sync_id} to_encode = {}
encoded = urllib.urlencode(to_encode) head = '<?xml version="1.0" encoding="UTF-8"?>'
to_encode['text'] = head + xml
to_encode['sync_id'] = sync_id
headers = {'Content-type': 'application/vnd.syncml+xml'}
#XXX bad hack for synchronization with erp5
if to_url.find('readResponse')<0: if to_url.find('readResponse')<0:
to_url = to_url + '/portal_synchronizations/readResponse' to_url = to_url + '/portal_synchronizations/readResponse'
request = urllib2.Request(url=to_url,data=encoded) encoded = urllib.urlencode(to_encode)
#result = urllib2.urlopen(request).read() data=encoded
request = urllib2.Request(url=to_url, data=data)
#XXX only to synchronize with other server than erp5 (must be improved):
# data=head+xml
# request = urllib2.Request(to_url, data, headers)
try: try:
result = urllib2.urlopen(request).read() result = urllib2.urlopen(request).read()
except socket.error, msg: except socket.error, msg:
...@@ -898,10 +913,12 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -898,10 +913,12 @@ class SynchronizationTool( SubscriptionSynchronization,
sync_id=sync_id, xml=xml, domain=domain) sync_id=sync_id, xml=xml, domain=domain)
LOG('sendHttpResponse, socket ERROR:',0,msg) LOG('sendHttpResponse, socket ERROR:',0,msg)
return return
except urllib2.URLError, msg:
LOG("sendHttpResponse, can't open url %s :" % to_url, 0, msg)
return
#LOG('sendHttpResponse, before result, domain:',0,domain) #LOG('sendHttpResponse, before result, domain:',0,domain)
#LOG('sendHttpResponse, result:',0,result)
if domain is not None: if domain is not None:
if domain.domain_type == self.SUB: if domain.domain_type == self.SUB:
gpg_key = domain.getGPGKey() gpg_key = domain.getGPGKey()
...@@ -929,7 +946,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -929,7 +946,7 @@ class SynchronizationTool( SubscriptionSynchronization,
if len(message_list) == 0: if len(message_list) == 0:
for subscription in self.getSubscriptionList(): for subscription in self.getSubscriptionList():
#LOG('sync, subcription:',0,subscription) #LOG('sync, subcription:',0,subscription)
self.activate(activity='RAMQueue').SubSync(subscription.getTitle()) self.activate(activity='RAMQueue').SubSync(subscription)
security.declarePublic('readResponse') security.declarePublic('readResponse')
def readResponse(self, text=None, sync_id=None, to_url=None, from_url=None): def readResponse(self, text=None, sync_id=None, to_url=None, from_url=None):
...@@ -937,9 +954,6 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -937,9 +954,6 @@ class SynchronizationTool( SubscriptionSynchronization,
We will look at the url and we will see if we need to send mail, http We will look at the url and we will see if we need to send mail, http
response, or just copy to a file. response, or just copy to a file.
""" """
#LOG('readResponse, ',0,'starting')
#LOG('readResponse, self.getPhysicalPath: ',0,self.getPhysicalPath())
#LOG('readResponse, sync_id: ',0,sync_id)
# Login as a manager to make sure we can create objects # Login as a manager to make sure we can create objects
uf = self.acl_users uf = self.acl_users
user = uf.getUserById('syncml').__of__(uf) user = uf.getUserById('syncml').__of__(uf)
...@@ -994,9 +1008,9 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -994,9 +1008,9 @@ class SynchronizationTool( SubscriptionSynchronization,
for subscription in self.getSubscriptionList(): for subscription in self.getSubscriptionList():
if subscription.getSubscriptionUrl()==url and \ if subscription.getSubscriptionUrl()==url and \
subscription.getTitle()==sync_id: subscription.getTitle()==sync_id:
result = self.activate(activity='RAMQueue').SubSync(sync_id, result = self.activate(activity='RAMQueue').SubSync(\
text) self.getSubscription(sync_id), text)
#result = self.SubSync(sync_id,xml) #result = self.SubSync(self.getSubscription(sync_id),xml)
# we use from only if we have a file # we use from only if we have a file
elif isinstance(from_url, str): elif isinstance(from_url, str):
...@@ -1041,24 +1055,4 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -1041,24 +1055,4 @@ class SynchronizationTool( SubscriptionSynchronization,
conduit_object = getattr(conduit_module, conduit)() conduit_object = getattr(conduit_module, conduit)()
return conduit_object.addNode(**kw) return conduit_object.addNode(**kw)
# security.declarePrivate('notify_sync')
# def notify_sync(self, event_type, object, infos):
# """Notification from the event service.
#
# # XXX very specific to cps
#
# Called when an object is added/deleted/modified.
# Update the date of sync
# """
# from Products.CPSCore.utils import _isinstance
# from Products.CPSCore.ProxyBase import ProxyBase
#
# if event_type in ('sys_modify_object',
# 'modify_object'):
# if not(_isinstance(object, ProxyBase)):
# repotool = getToolByName(self, 'portal_repository')
# if repotool.isObjectInRepository(object):
# object_id = object.getId()
InitializeClass( SynchronizationTool ) InitializeClass( SynchronizationTool )
...@@ -30,7 +30,7 @@ import smtplib ...@@ -30,7 +30,7 @@ import smtplib
from Products.ERP5SyncML.SyncCode import SyncCode from Products.ERP5SyncML.SyncCode import SyncCode
from Products.ERP5SyncML.Subscription import Signature from Products.ERP5SyncML.Subscription import Signature
from DateTime import DateTime from DateTime import DateTime
from cStringIO import StringIO from StringIO import StringIO
from xml.dom.ext import PrettyPrint from xml.dom.ext import PrettyPrint
import random import random
from zLOG import LOG from zLOG import LOG
...@@ -50,6 +50,11 @@ except ImportError: ...@@ -50,6 +50,11 @@ except ImportError:
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
raise ImportError, "Sorry, it was not possible to import Ft library" raise ImportError, "Sorry, it was not possible to import Ft library"
try:
from base64 import b16encode, b16decode
except ImportError:
from base64 import encodestring as b16encode, decodestring as b16decode
class XMLSyncUtilsMixin(SyncCode): class XMLSyncUtilsMixin(SyncCode):
def SyncMLHeader(self, session_id, msg_id, target, source, target_name=None, def SyncMLHeader(self, session_id, msg_id, target, source, target_name=None,
...@@ -79,8 +84,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -79,8 +84,8 @@ class XMLSyncUtilsMixin(SyncCode):
if dataCred not in (None, ''): if dataCred not in (None, ''):
xml(' <Cred>\n') xml(' <Cred>\n')
xml(' <Meta>\n') xml(' <Meta>\n')
xml(' <Format>%s</Format>\n' % authentication_format) xml(" <Format xmlns='syncml:metinf'>%s</Format>\n" % authentication_format)
xml(' <Type>%s</Type>\n' % authentication_type) xml(" <Type xmlns='syncml:metinf'>%s</Type>\n" % authentication_type)
xml(' </Meta>\n') xml(' </Meta>\n')
xml(' <Data>%s</Data>\n' % dataCred) xml(' <Data>%s</Data>\n' % dataCred)
xml(' </Cred>\n') xml(' </Cred>\n')
...@@ -142,17 +147,36 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -142,17 +147,36 @@ class XMLSyncUtilsMixin(SyncCode):
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
return xml_a return xml_a
def SyncMLConfirmation(self, cmd_id, target_ref, sync_code, cmd): def SyncMLConfirmation(self, cmd_id=None, target_ref=None, cmd=None,
sync_code=None, msg_ref=None, cmd_ref=None, source_ref=None,
remote_xml=None):
""" """
This is used in order to confirm that an object was correctly This is used in order to confirm that an object was correctly
synchronized synchronized
""" """
if remote_xml is not None :
msg_ref=remote_xml.xpath("string(//MsgID)").encode('utf-8')
cmd_ref=remote_xml.xpath("string(.//CmdID)").encode('utf-8')
target_ref=remote_xml.xpath("string(.//Target/LocURI)").encode('utf-8')
source_ref=remote_xml.xpath("string(.//Source/LocURI)").encode('utf-8')
xml_list = [] xml_list = []
xml = xml_list.append xml = xml_list.append
xml(' <Status>\n') xml(' <Status>\n')
#here there is a lot of test to keep compatibility with older call
if cmd_id not in (None,'') :
xml(' <CmdID>%s</CmdID>\n' % cmd_id) xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <TargetRef>%s</TargetRef>\n' % target_ref) if msg_ref not in (None,''):
xml(' <MsgRef>%s</MsgRef>\n' % msg_ref)
if cmd_ref not in (None,''):
xml(' <CmdRef>%s</CmdRef>\n' %cmd_ref)
if cmd not in (None,''):
xml(' <Cmd>%s</Cmd>\n' % cmd) xml(' <Cmd>%s</Cmd>\n' % cmd)
if target_ref not in (None,''):
xml(' <TargetRef>%s</TargetRef>\n' % target_ref)
if source_ref not in (None,''):
xml(' <SourceRef>%s</SourceRef>\n' % source_ref)
if sync_code not in (None,''):
xml(' <Data>%s</Data>\n' % sync_code) xml(' <Data>%s</Data>\n' % sync_code)
xml(' </Status>\n') xml(' </Status>\n')
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
...@@ -172,8 +196,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -172,8 +196,8 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <SourceRef>%s</SourceRef>\n' % source_ref) xml(' <SourceRef>%s</SourceRef>\n' % source_ref)
xml(' <Chal>\n') xml(' <Chal>\n')
xml(' <Meta>\n') xml(' <Meta>\n')
xml(' <Format>%s</Format>\n' % auth_format) xml(" <Format xmlns='syncml:metinf'>%s</Format>\n" % auth_format)
xml(' <Type>%s</Type>\n' % auth_type) xml(" <Type xmlns='syncml:metinf'>%s</Type>\n" % auth_type)
xml(' </Meta>\n') xml(' </Meta>\n')
xml(' </Chal>\n') xml(' </Chal>\n')
xml(' <Data>%s</Data>\n' % str(data_code)) xml(' <Data>%s</Data>\n' % str(data_code))
...@@ -181,6 +205,87 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -181,6 +205,87 @@ class XMLSyncUtilsMixin(SyncCode):
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
return xml_a return xml_a
def SyncMLPut(self, cmd_id, subscription):
"""
this is used to inform the server of the CTType version supported
"""
from Products.ERP5SyncML import Conduit
# Import the conduit and get it
conduit_name = subscription.getConduit()
if conduit_name.startswith('Products'):
path = conduit_name
conduit_name = conduit_name.split('.')[-1]
conduit_module = __import__(path, globals(), locals(), [''])
conduit = getattr(conduit_module, conduit_name)()
else:
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]),
globals(), locals(), [''])
conduit = getattr(conduit_module, conduit_name)()
#if the conduit support the SyncMLPut :
if hasattr(conduit, 'getCapabilitiesCTType') and \
hasattr(conduit, 'getCapabilitiesVerCTList') and \
hasattr(conduit, 'getPreferedCapabilitieVerCT'):
xml_list = []
xml = xml_list.append
if conduit.getCapabilitiesVerCTList() not in ([], None):
xml(' <Put>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <Meta>\n')
xml(' <Type>application/vnd.syncml-devinf+xml</Type>\n');
xml(' </Meta>\n')
xml(' <Item>\n')
xml(' <Source>\n')
xml(' <LocURI>./devinf11</LocURI>\n')
xml(' </Source>\n')
xml(' <Data>\n')
xml(' <DevInf>\n')
xml(' <VerDTD>1.1</VerDTD>\n')
xml(' <Man>Fabien MORIN</Man>\n')
xml(' <Mod>ERP5SyncML</Mod>\n')
xml(' <OEM>Open Source</OEM>\n')
xml(' <SwV>0.1</SwV>\n')
xml(' <DevID>%s</DevID>\n' % subscription.getSubscriptionUrl())
xml(' <DevTyp>workstation</DevTyp>\n')
xml(' <UTC/>\n')
xml(' <DataStore>\n')
xml(' <SourceRef>%s</SourceRef>\n' % subscription.getSourceURI())
xml(' <Rx-Pref>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
xml(' <VerCT>%s</VerCT>\n' % conduit.getPreferedCapabilitieVerCT())
xml(' </Rx-Pref>\n')
for rx_version in conduit.getCapabilitiesVerCTList():
xml(' <Rx>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
xml(' <VerCT>%s</VerCT>\n' % rx_version)
xml(' </Rx>\n')
xml(' <Tx-Pref>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
xml(' <VerCT>%s</VerCT>\n' % conduit.getPreferedCapabilitieVerCT())
xml(' </Tx-Pref>\n')
for tx_version in conduit.getCapabilitiesVerCTList():
xml(' <Tx>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
xml(' <VerCT>%s</VerCT>\n' % rx_version)
xml(' </Tx>\n')
xml(' <SyncCap>\n')
xml(' <SyncType>2</SyncType>\n')
xml(' <SyncType>1</SyncType>\n')
xml(' <SyncType>4</SyncType>\n')
xml(' <SyncType>6</SyncType>\n')
xml(' </SyncCap>\n')
xml(' </DataStore>\n')
xml(' </DevInf>\n')
xml(' </Data>\n')
xml(' </Item>\n')
xml(' </Put>\n')
xml_a = ''.join(xml_list)
return xml_a
return ''
def sendMail(self, fromaddr, toaddr, id_sync, msg): def sendMail(self, fromaddr, toaddr, id_sync, msg):
""" """
Send a message via email Send a message via email
...@@ -213,9 +318,14 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -213,9 +318,14 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <Source>\n') xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % gid) xml(' <LocURI>%s</LocURI>\n' % gid)
xml(' </Source>\n') xml(' </Source>\n')
xml(' <Data>\n') if media_type == self.MEDIA_TYPE['TEXT_XML']:
xml(' <Data>')
xml(xml_string) xml(xml_string)
xml(' </Data>\n') xml('</Data>\n')
else:
xml(' <Data><![CDATA[')
xml(xml_string)
xml('\n]]></Data>\n')
if more_data == 1: if more_data == 1:
xml(' <MoreData/>\n') xml(' <MoreData/>\n')
xml(' </Item>\n') xml(' </Item>\n')
...@@ -258,7 +368,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -258,7 +368,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <Source>\n') xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % str(gid)) xml(' <LocURI>%s</LocURI>\n' % str(gid))
xml(' </Source>\n') xml(' </Source>\n')
xml(' <Data>\n') xml(' <Data>')
xml(xml_string) xml(xml_string)
xml(' </Data>\n') xml(' </Data>\n')
if more_data == 1: if more_data == 1:
...@@ -308,7 +418,6 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -308,7 +418,6 @@ class XMLSyncUtilsMixin(SyncCode):
xml = xml_method() xml = xml_method()
else: else:
raise ValueError, "Sorry the script or method was not found" raise ValueError, "Sorry the script or method was not found"
#LOG('getXMLObject', 0, 'xml_mapping:%s, xml:%s, object:%s xml_method:%s' % (xml_mapping, xml, object, xml_method))
return xml return xml
def getSessionId(self, xml): def getSessionId(self, xml):
...@@ -370,7 +479,10 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -370,7 +479,10 @@ class XMLSyncUtilsMixin(SyncCode):
""" """
Return the value of the alert code inside the xml_stream Return the value of the alert code inside the xml_stream
""" """
return xml.xpath('string(TargetRef)') status = xml.xpath('string(TargetRef)')
if isinstance(status, unicode):
status = status.encode('utf-8')
return status
def getStatusCode(self, xml): def getStatusCode(self, xml):
""" """
...@@ -386,7 +498,10 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -386,7 +498,10 @@ class XMLSyncUtilsMixin(SyncCode):
Return the value of the command inside the xml_stream Return the value of the command inside the xml_stream
""" """
if xml.nodeName=='Status': if xml.nodeName=='Status':
return xml.xpath('string(//Status/Cmd)') cmd = xml.xpath('string(//Status/Cmd)')
if isinstance(cmd, unicode):
cmd = cmd.encode('utf-8')
return cmd
else: else:
return None return None
...@@ -399,8 +514,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -399,8 +514,8 @@ class XMLSyncUtilsMixin(SyncCode):
data='' data=''
first_node = xml.childNodes[0] first_node = xml.childNodes[0]
format = first_node.xpath('string(/SyncML/SyncHdr/Cred/Meta/Format)') format = first_node.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Format'])")
type = first_node.xpath('string(/SyncML/SyncHdr/Cred/Meta/Type)') type = first_node.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Type'])")
data = first_node.xpath('string(/SyncML/SyncHdr/Cred/Data)') data = first_node.xpath('string(/SyncML/SyncHdr/Cred/Data)')
format = format.encode('utf-8') format = format.encode('utf-8')
...@@ -420,12 +535,12 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -420,12 +535,12 @@ class XMLSyncUtilsMixin(SyncCode):
""" """
return the chalenge information : format and type return the chalenge information : format and type
""" """
format='' format=None
type='' type=None
first_node = xml.childNodes[0] first_node = xml.childNodes[0]
format = first_node.xpath('string(/SyncML/SyncBody/Status/Chal/Meta/Format)') format = first_node.xpath("string(//*[local-name() = 'Format'])")
type = first_node.xpath('string(/SyncML/SyncBody/Status/Chal/Meta/Type)') type = first_node.xpath("string(//*[local-name() = 'Type'])")
format = format.encode('utf-8') format = format.encode('utf-8')
type = type.encode('utf-8') type = type.encode('utf-8')
...@@ -468,7 +583,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -468,7 +583,7 @@ class XMLSyncUtilsMixin(SyncCode):
sync = True sync = True
return sync return sync
def CheckStatus(self, xml_stream): def checkStatus(self, xml_stream):
""" """
Check if there's a Status section in the xml_xtream Check if there's a Status section in the xml_xtream
""" """
...@@ -477,37 +592,28 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -477,37 +592,28 @@ class XMLSyncUtilsMixin(SyncCode):
status = True status = True
return status return status
def getNextSyncAction(self, xml_stream, last_action): def getSyncActionList(self, xml_stream):
""" """
It goes throw actions in the Sync section of the SyncML file, return the list of the action (could be "add", "replace", "delete").
then it returns the next action (could be "add", "replace",
"delete").
""" """
first_node = xml_stream.childNodes[0] return xml_stream.xpath('//Add|//Delete|//Replace')
client_body = first_node.childNodes[3]
if client_body.nodeName != "SyncBody": def getSyncBodyStatusList(self, xml_stream):
print "This is not a SyncML Body" """
next_action = None return the list of dictionary corredponding to the data of each status bloc
for subnode in client_body.childNodes: the data are : cmd, code and source
if subnode.nodeType == subnode.ELEMENT_NODE and \ """
subnode.nodeName == "Sync": status_list = []
# if we didn't use this method before xml = xml_stream.xpath('//Status')
if last_action == None and len(subnode.childNodes) > 1: for status in xml:
next_action = subnode.childNodes[1] tmp_dict = {}
else: tmp_dict['cmd'] = status.xpath('string(./Cmd)').encode('utf-8')
found = None tmp_dict['code'] = status.xpath('string(./Data)').encode('utf-8')
for subnode2 in subnode.childNodes: tmp_dict['source'] = status.xpath('string(./SourceRef)').encode('utf-8')
if subnode2.nodeType == subnode.ELEMENT_NODE and \ tmp_dict['target'] = status.xpath('string(./TargetRef)').encode('utf-8')
subnode2 != last_action and found is None: status_list.append(tmp_dict)
pass
elif subnode2.nodeType == subnode.ELEMENT_NODE and \ return status_list
subnode2 == last_action and found is None:
found = 1
elif subnode2.nodeType == subnode.ELEMENT_NODE and \
found is not None:
next_action = subnode2
break
return next_action
def getNextSyncBodyStatus(self, xml_stream, last_status): def getNextSyncBodyStatus(self, xml_stream, last_status):
""" """
...@@ -539,16 +645,19 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -539,16 +645,19 @@ class XMLSyncUtilsMixin(SyncCode):
""" """
return the section data in text form, it's usefull for the VCardConduit return the section data in text form, it's usefull for the VCardConduit
""" """
return action.xpath('string(Item/Data)') data = action.xpath('string(Item/Data)')
if isinstance(data, unicode):
data = data.encode('utf-8')
return data
def getDataSubNode(self, action): def getDataSubNode(self, action):
""" """
Return the node starting with <object....> of the action Return the node starting with <object....> of the action
""" """
if action.xpath('Item/Data') not in ([], None): if action.xpath('.//Item/Data') not in ([], None):
data_node = action.xpath('Item/Data')[0] data_node = action.xpath('.//Item/Data')[0]
if data_node.hasChildNodes() and data_node.childNodes.__len__()>1: if data_node.hasChildNodes():
return data_node.childNodes[1] return data_node.childNodes[0]
return None return None
def getPartialData(self, action): def getPartialData(self, action):
...@@ -575,20 +684,16 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -575,20 +684,16 @@ class XMLSyncUtilsMixin(SyncCode):
return None return None
def getActionId(self, action): def getActionId(self, action, action_name):
""" """
Return the rid of the object described by the action Return the rid of the object described by the action
""" """
for subnode in action.childNodes: id = action.xpath('string(.//Item/Source/LocURI)')
if subnode.nodeType == subnode.ELEMENT_NODE and \ if id in (None, ''):
subnode.nodeName == 'Item': id = action.xpath('string(.//Item/Target/LocURI)')
for subnode2 in subnode.childNodes: if isinstance(id, unicode):
if subnode2.nodeType == subnode2.ELEMENT_NODE and \ id = id.encode('utf-8')
subnode2.nodeName == 'Source': return id
for subnode3 in subnode2.childNodes:
if subnode3.nodeType == subnode3.ELEMENT_NODE and \
subnode3.nodeName == 'LocURI':
return str(subnode3.childNodes[0].data)
def checkActionMoreData(self, action): def checkActionMoreData(self, action):
""" """
...@@ -602,7 +707,10 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -602,7 +707,10 @@ class XMLSyncUtilsMixin(SyncCode):
""" """
Return the type of the object described by the action Return the type of the object described by the action
""" """
return action.xpath('string(Meta/Type)') action_type = action.xpath('string(Meta/Type)')
if isinstance(action_type, unicode):
action_type = action_type.encode('utf-8')
return action_type
def getElementNodeList(self, node): def getElementNodeList(self, node):
""" """
...@@ -647,18 +755,21 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -647,18 +755,21 @@ class XMLSyncUtilsMixin(SyncCode):
#object_gid = domain.getGidFromObject(object) #object_gid = domain.getGidFromObject(object)
local_gid_list = map(lambda x: domain.getGidFromObject(x),object_list) local_gid_list = map(lambda x: domain.getGidFromObject(x),object_list)
# Objects to remove # Objects to remove
#for object_id in id_list:
for object_gid in subscriber.getGidList(): for object_gid in subscriber.getGidList():
if not (object_gid in local_gid_list): if not (object_gid in local_gid_list):
# This is an object to remove # This is an object to remove
signature = subscriber.getSignature(object_gid) signature = subscriber.getSignatureFromGid(object_gid)
if signature.getStatus()!=self.PARTIAL: # If partial, then we have a signature if signature.getStatus()!=self.PARTIAL: # If partial, then we have a signature
# but no local object # but no local object
xml_object = signature.getXML() xml_object = signature.getXML()
if xml_object is not None: # This prevent to delete an object that if xml_object is not None: # This prevent to delete an object that
# we were not able to create # we were not able to create
rid = signature.getRid()
if rid != None:
object_gid=rid #to use the remote id if it exist
syncml_data += self.deleteXMLObject(xml_object=signature.getXML()\ syncml_data += self.deleteXMLObject(xml_object=signature.getXML()\
or '', object_gid=object_gid,cmd_id=cmd_id) or '', object_gid=object_gid,cmd_id=cmd_id)
cmd_id += 1
local_gid_list = [] local_gid_list = []
#for object in domain.getObjectList(): #for object in domain.getObjectList():
...@@ -683,9 +794,10 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -683,9 +794,10 @@ class XMLSyncUtilsMixin(SyncCode):
#LOG('getSyncMLData',0,'xml_mapping: %s' % str(domain.xml_mapping)) #LOG('getSyncMLData',0,'xml_mapping: %s' % str(domain.xml_mapping))
#LOG('getSyncMLData',0,'code: %s' % str(self.getAlertCode(remote_xml))) #LOG('getSyncMLData',0,'code: %s' % str(self.getAlertCode(remote_xml)))
#LOG('getSyncMLData',0,'gid_list: %s' % str(local_gid_list)) #LOG('getSyncMLData',0,'gid_list: %s' % str(local_gid_list))
#LOG('getSyncMLData',0,'subscriber.getGidList: %s' % subscriber.getGidList())
#LOG('getSyncMLData',0,'hasSignature: %s' % str(subscriber.hasSignature(object_gid))) #LOG('getSyncMLData',0,'hasSignature: %s' % str(subscriber.hasSignature(object_gid)))
#LOG('getSyncMLData',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC)) #LOG('getSyncMLData',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC))
signature = subscriber.getSignature(object_gid) signature = subscriber.getSignatureFromGid(object_gid)
# Here we first check if the object was modified or not by looking at dates # Here we first check if the object was modified or not by looking at dates
if signature is not None: if signature is not None:
...@@ -702,7 +814,10 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -702,7 +814,10 @@ class XMLSyncUtilsMixin(SyncCode):
xml_object = self.getXMLObject(object=object, xml_object = self.getXMLObject(object=object,
xml_mapping=domain.xml_mapping) xml_mapping=domain.xml_mapping)
xml_string = xml_object xml_string = xml_object
signature = Signature(gid=object_gid,id=object.getId(),object=object) if isinstance(xml_string, unicode):
xml_string = xml_object.encode('utf-8')
gid = subscriber.getGidFromObject(object)
signature = Signature(id=gid,object=object)
signature.setTempXML(xml_object) signature.setTempXML(xml_object)
if xml_string.count('\n') > self.MAX_LINES: if xml_string.count('\n') > self.MAX_LINES:
if xml_string.find('--') >= 0: # This make comment fails, so we need to replace if xml_string.find('--') >= 0: # This make comment fails, so we need to replace
...@@ -714,16 +829,18 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -714,16 +829,18 @@ class XMLSyncUtilsMixin(SyncCode):
while i < self.MAX_LINES: while i < self.MAX_LINES:
short_string += rest_string[:rest_string.find('\n')+1] short_string += rest_string[:rest_string.find('\n')+1]
rest_string = xml_string[len(short_string):] rest_string = xml_string[len(short_string):]
#LOG('XMLSyncUtils',0,'rest_string: %s' % str(rest_string))
i += 1 i += 1
#LOG('getSyncMLData',0,'setPartialXML with: %s' % str(rest_string)) #LOG('getSyncMLData',0,'setPartialXML with: %s' % str(rest_string))
signature.setPartialXML(rest_string) signature.setPartialXML(rest_string)
status =self.PARTIAL status = self.PARTIAL
signature.setAction('Add') signature.setAction('Add')
xml_string = '<!--' + short_string + '-->' xml_string = '<!--' + short_string + '-->'
gid = signature.getRid()#in fisrt, we try with rid if there is one
if gid == None:
gid = signature.getGid()
syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object, syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,
gid=object_gid, xml_string=xml_string, more_data=more_data, gid=gid, xml_string=xml_string,
media_type=subscriber.getMediaType()) more_data=more_data, media_type=subscriber.getMediaType())
cmd_id += 1 cmd_id += 1
signature.setStatus(status) signature.setStatus(status)
subscriber.addSignature(signature) subscriber.addSignature(signature)
...@@ -734,8 +851,9 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -734,8 +851,9 @@ class XMLSyncUtilsMixin(SyncCode):
#LOG('getSyncMLData',0,'checkMD5: %s' % str(signature.checkMD5(xml_object))) #LOG('getSyncMLData',0,'checkMD5: %s' % str(signature.checkMD5(xml_object)))
#LOG('getSyncMLData',0,'getStatus: %s' % str(signature.getStatus())) #LOG('getSyncMLData',0,'getStatus: %s' % str(signature.getStatus()))
if signature.getStatus()==self.PUB_CONFLICT_MERGE: if signature.getStatus()==self.PUB_CONFLICT_MERGE:
xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id, xml_confirmation += self.SyncMLConfirmation(cmd_id=cmd_id,
self.CONFLICT_MERGE,'Replace') source_ref=signature.getGid(), sync_code=self.CONFLICT_MERGE,
cmd='Replace')
set_synchronized = 1 set_synchronized = 1
if not signature.checkMD5(xml_object): if not signature.checkMD5(xml_object):
set_synchronized = 0 set_synchronized = 0
...@@ -762,9 +880,12 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -762,9 +880,12 @@ class XMLSyncUtilsMixin(SyncCode):
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
xml_string = self.getXMLObject(object=object, xml_string = self.getXMLObject(object=object,
xml_mapping=domain.xml_mapping) xml_mapping=domain.xml_mapping)
gid = signature.getRid()#in fisrt, we try with rid if there is one
if gid == None:
gid = signature.getGid()
syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object, syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object,
gid=object_gid, xml_string=xml_string, more_data=more_data, gid=gid, xml_string=xml_string,
media_type=subscriber.getMediaType()) more_data=more_data, media_type=subscriber.getMediaType())
cmd_id += 1 cmd_id += 1
signature.setTempXML(xml_object) signature.setTempXML(xml_object)
# Now we can apply the xupdate from the subscriber # Now we can apply the xupdate from the subscriber
...@@ -780,6 +901,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -780,6 +901,7 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setTempXML(xml_object) signature.setTempXML(xml_object)
if set_synchronized: # We have to do that after this previous update if set_synchronized: # We have to do that after this previous update
# We should not have this case when we are in CONFLICT_MERGE # We should not have this case when we are in CONFLICT_MERGE
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN: elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN:
# We have decided to apply the update # We have decided to apply the update
...@@ -789,8 +911,9 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -789,8 +911,9 @@ class XMLSyncUtilsMixin(SyncCode):
xml_update = signature.getPartialXML() xml_update = signature.getPartialXML()
conduit.updateNode(xml=signature.getPartialXML(), object=object, conduit.updateNode(xml=signature.getPartialXML(), object=object,
previous_xml=signature.getXML(),force=1) previous_xml=signature.getXML(),force=1)
xml_confirmation += self.SyncMLConfirmation(cmd_id,object_gid, xml_confirmation += self.SyncMLConfirmation(cmd_id=cmd_id,
self.CONFLICT_CLIENT_WIN,'Replace') target_ref=object_gid, sync_code=self.CONFLICT_CLIENT_WIN,
cmd='Replace')
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
elif signature.getStatus()==self.PARTIAL: elif signature.getStatus()==self.PARTIAL:
xml_string = signature.getPartialXML() xml_string = signature.getPartialXML()
...@@ -815,13 +938,19 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -815,13 +938,19 @@ class XMLSyncUtilsMixin(SyncCode):
xml_mapping=domain.xml_mapping) xml_mapping=domain.xml_mapping)
#LOG('xml_string =', 0, xml_string) #LOG('xml_string =', 0, xml_string)
if signature.getAction()=='Replace': if signature.getAction()=='Replace':
gid = signature.getRid()#in fisrt, we try with rid if there is one
if gid == None:
gid = signature.getGid()
syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object, syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object,
gid=object_gid, xml_string=xml_string, more_data=more_data, gid=gid, xml_string=xml_string, more_data=more_data,
media_type=subscriber.getMediaType()) media_type=subscriber.getMediaType())
elif signature.getAction()=='Add': elif signature.getAction()=='Add':
gid = signature.getRid()#in fisrt, we try with rid if there is one
if gid == None:
gid = signature.getGid()
syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object, syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,
gid=object_gid, xml_string=xml_string, more_data=more_data, gid=gid, xml_string=xml_string,
media_type=subscriber.getMediaType()) more_data=more_data, media_type=subscriber.getMediaType())
return (syncml_data,xml_confirmation,cmd_id) return (syncml_data,xml_confirmation,cmd_id)
def applyActionList(self, domain=None, subscriber=None,destination_path=None, def applyActionList(self, domain=None, subscriber=None,destination_path=None,
...@@ -830,27 +959,42 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -830,27 +959,42 @@ class XMLSyncUtilsMixin(SyncCode):
This just look to a list of action to do, then id applies This just look to a list of action to do, then id applies
each action one by one, thanks to a conduit each action one by one, thanks to a conduit
""" """
next_action = self.getNextSyncAction(remote_xml, None)
xml_confirmation = '' xml_confirmation = ''
has_next_action = 0 has_next_action = 0
if next_action is not None: for action in self.getSyncActionList(remote_xml):
has_next_action = 1
while next_action != None:
conflict_list = [] conflict_list = []
status_code = self.SUCCESS status_code = self.SUCCESS
# Thirst we have to check the kind of action it is # Thirst we have to check the kind of action it is
partial_data = self.getPartialData(next_action) partial_data = self.getPartialData(action)
object_gid = self.getActionId(next_action) rid = self.getActionId(action, action.nodeName)
signature = subscriber.getSignature(object_gid) if action.nodeName != 'Delete':
object = domain.getObjectFromGid(object_gid) if hasattr(conduit, 'getGidFromXML'):
gid = b16encode(conduit.getGidFromXML(self.getDataText(action)))
else:
gid=rid
else:
gid=rid
object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=gid)
signature = subscriber.getSignatureFromGid(gid)
if signature != None and rid != gid:
#in this case, the object was created on another subscriber than erp5
# and we should save it's remote id
signature.setRid(rid)
LOG('gid == rid ?', 0, 'gid=%s, rid=%s' % (gid, rid))
object = subscriber.getObjectFromGid(gid)
if signature == None: if signature == None:
#LOG('applyActionList, signature is None',0,signature) #LOG('applyActionList, signature is None',0,signature)
signature = Signature(gid=object_gid, status=self.NOT_SYNCHRONIZED, if gid == rid:
signature = Signature(id=gid, status=self.NOT_SYNCHRONIZED,
object=object).__of__(subscriber)
else:
signature = Signature(rid=rid, id=gid, status=self.NOT_SYNCHRONIZED,
object=object).__of__(subscriber) object=object).__of__(subscriber)
signature.setObjectId(object_id)
subscriber.addSignature(signature) subscriber.addSignature(signature)
force = signature.getForce() force = signature.getForce()
#LOG('applyActionList',0,'object: %s' % repr(object)) #LOG('applyActionList',0,'object: %s' % repr(object))
if self.checkActionMoreData(next_action) == 0: if self.checkActionMoreData(action) == 0:
data_subnode = None data_subnode = None
if partial_data != None: if partial_data != None:
signature_partial_xml = signature.getPartialXML() signature_partial_xml = signature.getPartialXML()
...@@ -859,20 +1003,19 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -859,20 +1003,19 @@ class XMLSyncUtilsMixin(SyncCode):
else: else:
data_subnode = partial_data data_subnode = partial_data
#LOG('SyncModif',0,'data_subnode: %s' % data_subnode) #LOG('SyncModif',0,'data_subnode: %s' % data_subnode)
#data_subnode = FromXml(data_subnode)
if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_XML']: if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_XML']:
data_subnode = Parse(data_subnode) data_subnode = Parse(data_subnode)
data_subnode = data_subnode.childNodes[0] # Because we just created a new xml data_subnode = data_subnode.childNodes[0] # Because we just created a new xml
# document, with childNodes[0] a DocumentType and childNodes[1] the Element Node # document, with childNodes[0] a DocumentType and childNodes[1] the Element Node
else: else:
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
data_subnode = self.getDataText(next_action) data_subnode = self.getDataText(action)
else: else:
data_subnode = self.getDataSubNode(next_action) data_subnode = self.getDataSubNode(action)
if next_action.nodeName == 'Add': if action.nodeName == 'Add':
# Then store the xml of this new subobject # Then store the xml of this new subobject
if object is None: if object is None:
object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=object_gid) object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=gid)
#if object_id is not None: #if object_id is not None:
add_data = conduit.addNode(xml=data_subnode, add_data = conduit.addNode(xml=data_subnode,
object=destination_path, object_id=object_id) object=destination_path, object_id=object_id)
...@@ -880,14 +1023,14 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -880,14 +1023,14 @@ class XMLSyncUtilsMixin(SyncCode):
conflict_list += add_data['conflict_list'] conflict_list += add_data['conflict_list']
# Retrieve directly the object from addNode # Retrieve directly the object from addNode
object = add_data['object'] object = add_data['object']
#LOG('XMLSyncUtils, in ADD add_data',0,add_data) LOG('XMLSyncUtils, in ADD add_data',0,add_data)
if object is not None: if object is not None:
signature.setPath(object.getPhysicalPath()) signature.setPath(object.getPhysicalPath())
#LOG('applyActionList',0,'object after add: %s' % repr(object)) signature.setObjectId(object.getId())
else: else:
#Object was retrieve but need to be updated without recreated #Object was retrieve but need to be updated without recreated
#usefull when an object is only deleted by workflow. #usefull when an object is only deleted by workflow.
object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=object_gid) object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=gid)
add_data = conduit.addNode(xml=data_subnode, add_data = conduit.addNode(xml=data_subnode,
object=destination_path, object=destination_path,
object_id=object_id, object_id=object_id,
...@@ -901,17 +1044,20 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -901,17 +1044,20 @@ class XMLSyncUtilsMixin(SyncCode):
#if mapping is not None: #if mapping is not None:
# xml_object = mapping() # xml_object = mapping()
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
signature.setId(object.getId()) #signature.setId(object.getId())
signature.setPath(object.getPhysicalPath()) signature.setPath(object.getPhysicalPath())
signature.setXML(xml_object) signature.setXML(xml_object)
xml_confirmation +=\ xml_confirmation += self.SyncMLConfirmation(
self.SyncMLConfirmation(cmd_id,object_gid,self.SUCCESS,'Add') cmd_id=cmd_id,
cmd='Add',
sync_code=self.ITEM_ADDED,
remote_xml=action)
cmd_id +=1 cmd_id +=1
elif next_action.nodeName == 'Replace': elif action.nodeName == 'Replace':
#LOG('SyncModif',0,'object: %s will be updated...' % str(object)) #LOG('SyncModif',0,'object: %s will be updated...' % str(object))
if object is not None: if object is not None:
#LOG('SyncModif',0,'object: %s will be updated...' % object.id) #LOG('SyncModif',0,'object: %s will be updated...' % object.id)
signature = subscriber.getSignature(object_gid) signature = subscriber.getSignatureFromGid(gid)
#LOG('SyncModif',0,'previous signature: %s' % str(signature)) #LOG('SyncModif',0,'previous signature: %s' % str(signature))
previous_xml = signature.getXML() previous_xml = signature.getXML()
#LOG('SyncModif',0,'previous signature: %i' % len(previous_xml)) #LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
...@@ -934,8 +1080,11 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -934,8 +1080,11 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setPartialXML(data_subnode_string) signature.setPartialXML(data_subnode_string)
elif not simulate: elif not simulate:
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
xml_confirmation += self.SyncMLConfirmation(cmd_id, xml_confirmation += self.SyncMLConfirmation(\
object_gid,status_code,'Replace') cmd_id=cmd_id,
cmd='Replace',
sync_code=status_code,
remote_xml=action)
cmd_id +=1 cmd_id +=1
if simulate: if simulate:
# This means we are on the publiher side and we want to store # This means we are on the publiher side and we want to store
...@@ -947,17 +1096,22 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -947,17 +1096,22 @@ class XMLSyncUtilsMixin(SyncCode):
#LOG('applyActionList, subscriber_xupdate:',0,data_subnode_string) #LOG('applyActionList, subscriber_xupdate:',0,data_subnode_string)
signature.setSubscriberXupdate(data_subnode_string) signature.setSubscriberXupdate(data_subnode_string)
elif next_action.nodeName == 'Delete': elif action.nodeName == 'Delete':
object_id = signature.getId() object_id = signature.getId()
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
data_subnode = self.getDataText(next_action) data_subnode = self.getDataText(action)
else: else:
data_subnode = self.getDataSubNode(next_action) data_subnode = self.getDataSubNode(action)
if subscriber.getObjectFromGid(object_id) not in (None, ''):
#if the object exist:
conduit.deleteNode(xml=data_subnode, object=destination_path, conduit.deleteNode(xml=data_subnode, object=destination_path,
object_id=object_id) object_id=subscriber.getObjectFromGid(object_id).getId())
subscriber.delSignature(object_gid) subscriber.delSignature(gid)
xml_confirmation += self.SyncMLConfirmation(cmd_id, xml_confirmation += self.SyncMLConfirmation(
object_gid,status_code,'Delete') cmd_id=cmd_id,
cmd='Delete',
sync_code=status_code,
remote_xml=action)
else: # We want to retrieve more data else: # We want to retrieve more data
signature.setStatus(self.PARTIAL) signature.setStatus(self.PARTIAL)
#LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial)) #LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial))
...@@ -967,13 +1121,17 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -967,13 +1121,17 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setPartialXML(previous_partial) signature.setPartialXML(previous_partial)
#LOG('SyncModif',0,'previous_partial: %s' % str(previous_partial)) #LOG('SyncModif',0,'previous_partial: %s' % str(previous_partial))
#LOG('SyncModif',0,'waiting more data for :%s' % signature.getId()) #LOG('SyncModif',0,'waiting more data for :%s' % signature.getId())
xml_confirmation += self.SyncMLConfirmation(cmd_id, object_gid, #xml_confirmation += self.SyncMLConfirmation(cmd_id, object_gid,
self.WAITING_DATA, next_action.nodeName) # self.WAITING_DATA, action.nodeName)
xml_confirmation += self.SyncMLConfirmation(\
cmd_id=cmd_id,
cmd=action.nodeName,
sync_code=self.WAITING_DATA,
remote_xml=action)
if conflict_list != [] and signature is not None: if conflict_list != [] and signature is not None:
# We had a conflict # We had a conflict
signature.setStatus(self.CONFLICT) signature.setStatus(self.CONFLICT)
next_action = self.getNextSyncAction(remote_xml, next_action)
return (xml_confirmation,has_next_action,cmd_id) return (xml_confirmation,has_next_action,cmd_id)
def applyStatusList(self, subscriber=None,remote_xml=None): def applyStatusList(self, subscriber=None,remote_xml=None):
...@@ -981,44 +1139,46 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -981,44 +1139,46 @@ class XMLSyncUtilsMixin(SyncCode):
This read a list of status list (ie syncml confirmations). This read a list of status list (ie syncml confirmations).
This method have to change status codes on signatures This method have to change status codes on signatures
""" """
next_status = self.getNextSyncBodyStatus(remote_xml, None) status_list = self.getSyncBodyStatusList(remote_xml)
#LOG('applyStatusList, next_status',0,next_status)
# We do not want the first one
next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
has_status_list = 0 has_status_list = 0
if next_status is not None:
has_status_list = 1
destination_waiting_more_data = 0 destination_waiting_more_data = 0
while next_status != None: if status_list != []:
object_gid = self.getStatusTarget(next_status) for status in status_list:
status_code = self.getStatusCode(next_status) status_cmd = status['cmd']
status_cmd = self.getStatusCommand(next_status) #if status_cmd in ('Delete'):
signature = subscriber.getSignature(object_gid) # object_gid = status['target']
#LOG('SyncModif',0,'next_status: %s' % str(status_code)) #else:
object_gid = status['source']
status_code = int(status['code'])
if status_cmd in ('Add','Replace'): if status_cmd in ('Add','Replace'):
has_status_list = 1
signature = subscriber.getSignatureFromGid(object_gid)
if signature == None:
signature = subscriber.getSignatureFromRid(object_gid)
if status_code == self.CHUNK_OK: if status_code == self.CHUNK_OK:
destination_waiting_more_data = 1 destination_waiting_more_data = 1
signature.setStatus(self.PARTIAL) signature.setStatus(self.PARTIAL)
elif status_code == self.CONFLICT: elif status_code == self.CONFLICT:
signature.setStatus(self.CONFLICT) signature.setStatus(self.CONFLICT)
elif status_code == self.CONFLICT_MERGE: elif status_code == self.CONFLICT_MERGE:
# We will have to apply the update, and we should not care about conflicts # We will have to apply the update, and we should not care
# so we have to force the update # about conflicts, so we have to force the update
signature.setStatus(self.NOT_SYNCHRONIZED) signature.setStatus(self.NOT_SYNCHRONIZED)
signature.setForce(1) signature.setForce(1)
elif status_code == self.CONFLICT_CLIENT_WIN: elif status_code == self.CONFLICT_CLIENT_WIN:
# The server was agree to apply our updates, nothing to do # The server was agree to apply our updates, nothing to do
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
elif status_code == self.SUCCESS: elif status_code in (self.SUCCESS, self.ITEM_ADDED):
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
elif status_cmd == 'Delete': elif status_cmd == 'Delete':
if status_code == self.SUCCESS: if status_code == self.SUCCESS:
subscriber.delSignature(object_gid) signature = subscriber.getSignatureFromGid(object_gid)
next_status = self.getNextSyncBodyStatus(remote_xml, next_status) if signature == None:
signature = subscriber.getSignatureFromRid(object_gid)
subscriber.delSignature(signature.getGid())
return (destination_waiting_more_data, has_status_list) return (destination_waiting_more_data, has_status_list)
class XMLSyncUtils(XMLSyncUtilsMixin): class XMLSyncUtils(XMLSyncUtilsMixin):
def Sync(self, id, msg=None, RESPONSE=None): def Sync(self, id, msg=None, RESPONSE=None):
...@@ -1131,53 +1291,113 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -1131,53 +1291,113 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
xml(self.SyncMLHeader(domain.getSessionId(), domain.incrementMessageId(), xml(self.SyncMLHeader(domain.getSessionId(), domain.incrementMessageId(),
domain.getPublicationUrl(), domain.getSubscriptionUrl())) domain.getPublicationUrl(), domain.getSubscriptionUrl()))
cmd_id += 1
# Add or replace objects # Add or replace objects
syncml_data = '' syncml_data = ''
# Now we have to send our own modifications
if has_next_action == 0 and not \
(domain.domain_type==self.SUB and alert_code==self.SLOW_SYNC):
(syncml_data,xml_confirmation,cmd_id) = self.getSyncMLData(domain=domain,
remote_xml=remote_xml,
subscriber=subscriber,
destination_path=destination_path,
cmd_id=cmd_id,xml_confirmation=xml_confirmation,
conduit=conduit)
# syncml body # syncml body
xml(' <SyncBody>\n') xml(' <SyncBody>\n')
# status for SyncHdr
message_id = self.getMessageId(remote_xml)
xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1
xml(' <MsgRef>%s</MsgRef>\n' % message_id)
xml(' <CmdRef>0</CmdRef>\n') #to make reference to the SyncHdr, it's 0
xml(' <Cmd>SyncHdr</Cmd>\n')
xml(' <TargetRef>%s</TargetRef>\n' \
% remote_xml.xpath('string(//SyncHdr/Target/LocURI)').encode('utf-8'))
xml(' <SourceRef>%s</SourceRef>\n' \
% remote_xml.xpath('string(//SyncHdr/Source/LocURI)').encode('utf-8'))
xml(' <Data>200</Data>\n')
xml(' </Status>\n')
#list of element in the SyncBody bloc
syncbody_element_list = remote_xml.xpath('//SyncBody/*')
#add the status bloc corresponding to the receive command
for syncbody_element in syncbody_element_list:
if str(syncbody_element.nodeName) not in ('Status', 'Final', 'Replace'):
xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1
xml(' <MsgRef>%s</MsgRef>\n' % message_id)
xml(' <CmdRef>%s</CmdRef>\n' \
% syncbody_element.xpath('string(.//CmdID)').encode('utf-8'))
xml(' <Cmd>%s</Cmd>\n' % syncbody_element.nodeName.encode('utf-8'))
target_ref = syncbody_element.xpath('string(.//Target/LocURI)').encode('utf-8')
if target_ref not in (None, ''):
xml(' <TargetRef>%s</TargetRef>\n' % target_ref )
source_ref = syncbody_element.xpath('string(.//Source/LocURI)').encode('utf-8')
if source_ref not in (None, ''):
xml(' <SourceRef>%s</SourceRef>\n' % source_ref )
#xml(' <Data>%s</Data>\n' % subscriber.getSynchronizationType())
if syncbody_element.nodeName.encode('utf-8') == 'Add':
xml(' <Data>%s</Data>\n' % '201')
else:
xml(' <Data>%s</Data>\n' % '200')
# if syncbody_element.xpath('.//Item') not in ([], None, '') and\
# syncbody_element.xpath('.//Item.....'): #contient une ancre Next...
xml(' <Item>\n')
xml(' <Data>\n')
xml(" <Anchor xmlns='syncml:metinf'>\n")
xml(' <Next>%s</Next>\n' % subscriber.getNextAnchor())
xml(' </Anchor>\n')
xml(' </Data>\n')
xml(' </Item>\n')
xml(' </Status>\n')
destination_url = '' destination_url = ''
if domain.domain_type == self.PUB:
subscriber.NewAnchor()
destination_url = domain.getPublicationUrl()
xml(self.SyncMLStatus(cmd_id, subscriber.getSubscriptionUrl(),
domain.getDestinationPath(),
subscriber.getSynchronizationType(),
subscriber.getNextAnchor()))
elif domain.domain_type == self.SUB:
destination_url = domain.getPublicationUrl()
xml(self.SyncMLStatus(cmd_id, domain.getPublicationUrl(),
subscriber.getDestinationPath(),
subscriber.getSynchronizationType(),
subscriber.getNextAnchor()))
# alert message if we want more data # alert message if we want more data
if destination_waiting_more_data == 1: if destination_waiting_more_data == 1:
xml(self.SyncMLAlert(cmd_id, self.WAITING_DATA, xml(self.SyncMLAlert(cmd_id, self.WAITING_DATA,
destination_url, subscriber.getTargetURI(),
domain.getDestinationPath(), subscriber.getSourceURI(),
subscriber.getLastAnchor(), subscriber.getLastAnchor(),
subscriber.getNextAnchor())) subscriber.getNextAnchor()))
# Now we should send confirmations # Now we should send confirmations
xml(xml_confirmation) cmd_id_before_getsyncmldata = cmd_id
(syncml_data,xml_confirmation,cmd_id) = self.getSyncMLData(domain=domain,
remote_xml=remote_xml,
subscriber=subscriber,
destination_path=destination_path,
cmd_id=cmd_id+1,xml_confirmation=xml_confirmation,
conduit=conduit)
if syncml_data != '': if syncml_data != '':
xml(' <Sync>\n') xml(' <Sync>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id_before_getsyncmldata)
if domain.domain_type == self.SUB:
if subscriber.getTargetURI() not in ('', None):
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % subscriber.getTargetURI())
xml(' </Target>\n')
if subscriber.getSourceURI() not in ('', None):
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % subscriber.getSourceURI())
xml(' </Source>\n')
elif domain.domain_type == self.PUB:
if domain.getTargetURI() not in ('', None):
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % domain.getTargetURI())
xml(' </Target>\n')
if domain.getSourceURI() not in ('', None):
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % domain.getSourceURI())
xml(' </Source>\n')
xml(syncml_data) xml(syncml_data)
xml(' </Sync>\n') xml(' </Sync>\n')
xml(xml_confirmation)
xml(' <Final/>\n') xml(' <Final/>\n')
xml(' </SyncBody>\n') xml(' </SyncBody>\n')
xml('</SyncML>\n') xml('</SyncML>\n')
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
if domain.domain_type == self.PUB: # We always reply if domain.domain_type == self.PUB: # We always reply
subscriber.setLastSentMessage(xml_a) subscriber.setLastSentMessage(xml_a)
self.sendResponse(from_url=domain.publication_url, self.sendResponse(from_url=domain.publication_url,
......
...@@ -67,6 +67,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -67,6 +67,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="destination_path" value="<dtml-var getDestinationPath>" size="40" /> <input type="text" name="destination_path" value="<dtml-var getDestinationPath>" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Source URI
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="source_uri" size="40" value="<dtml-var getSourceURI>" />
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
...@@ -77,6 +77,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -77,6 +77,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="destination_path" value="<dtml-var getDestinationPath>" size="40" /> <input type="text" name="destination_path" value="<dtml-var getDestinationPath>" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Source URI
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="source_uri" size="40" value="<dtml-var getSourceURI>" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Target URI
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="target_uri" size="40" value="<dtml-var getTargetURI>" />
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
...@@ -63,6 +63,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -63,6 +63,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="destination_path" size="40" /> <input type="text" name="destination_path" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Source URI
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="source_uri" size="40" />
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
...@@ -73,6 +73,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -73,6 +73,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="destination_path" size="40" /> <input type="text" name="destination_path" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Source URI
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="source_uri" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Target URI
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="target_uri" size="40" />
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
...@@ -43,9 +43,10 @@ from Products.ERP5SyncML.SyncCode import SyncCode ...@@ -43,9 +43,10 @@ from Products.ERP5SyncML.SyncCode import SyncCode
from zLOG import LOG from zLOG import LOG
try: try:
from base64 import b64encode, b64decode from base64 import b64encode, b64decode, b16encode, b16decode
except ImportError: except ImportError:
from base64 import encodestring as b64encode, decodestring as b64decode from base64 import encodestring as b64encode, decodestring as b64decode, \
encodestring as b16encode, decodestring as b16decode
class TestERP5SyncMLMixin: class TestERP5SyncMLMixin:
...@@ -195,10 +196,10 @@ class TestERP5SyncMLMixin: ...@@ -195,10 +196,10 @@ class TestERP5SyncMLMixin:
file.write('') file.write('')
file.close() file.close()
nb_message = 1 nb_message = 1
result = portal_sync.SubSync(subscription.getTitle()) result = portal_sync.SubSync(subscription)
while result['has_response']==1: while result['has_response']==1:
portal_sync.PubSync(publication.getTitle()) portal_sync.PubSync(publication.getTitle())
result = portal_sync.SubSync(subscription.getTitle()) result = portal_sync.SubSync(subscription)
nb_message += 1 + result['has_response'] nb_message += 1 + result['has_response']
return nb_message return nb_message
...@@ -226,16 +227,16 @@ class TestERP5SyncMLMixin: ...@@ -226,16 +227,16 @@ class TestERP5SyncMLMixin:
file.write('') file.write('')
file.close() file.close()
nb_message = 1 nb_message = 1
result = portal_sync.SubSync(subscription.getTitle()) result = portal_sync.SubSync(subscription)
while result['has_response']==1: while result['has_response']==1:
# We do thing three times, so that we will test # We do thing three times, so that we will test
# if we manage well duplicate messages # if we manage well duplicate messages
portal_sync.PubSync(publication.getTitle()) portal_sync.PubSync(publication.getTitle())
portal_sync.PubSync(publication.getTitle()) portal_sync.PubSync(publication.getTitle())
portal_sync.PubSync(publication.getTitle()) portal_sync.PubSync(publication.getTitle())
result = portal_sync.SubSync(subscription.getTitle()) result = portal_sync.SubSync(subscription)
result = portal_sync.SubSync(subscription.getTitle()) result = portal_sync.SubSync(subscription)
result = portal_sync.SubSync(subscription.getTitle()) result = portal_sync.SubSync(subscription)
nb_message += 1 + result['has_response'] nb_message += 1 + result['has_response']
return nb_message return nb_message
...@@ -337,9 +338,15 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): ...@@ -337,9 +338,15 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
LOG('Testing... ',0,'test_02_AddPublication') LOG('Testing... ',0,'test_02_AddPublication')
portal_id = self.getPortalName() portal_id = self.getPortalName()
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
portal_sync.manage_addPublication(self.pub_id,self.publication_url, portal_sync.manage_addPublication(title=self.pub_id,
'/%s/person_server' % portal_id,'objectValues', self.xml_mapping, publication_url=self.publication_url,
'ERP5Conduit','') destination_path='/%s/person_server' % portal_id,
source_uri='Person',
query='objectValues',
xml_mapping=self.xml_mapping,
conduit='ERP5Conduit',
gpg_key='',
gid_generator='getId')
pub = portal_sync.getPublication(self.pub_id) pub = portal_sync.getPublication(self.pub_id)
self.failUnless(pub is not None) self.failUnless(pub is not None)
...@@ -350,9 +357,17 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): ...@@ -350,9 +357,17 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
LOG('Testing... ',0,'test_03_AddSubscription1') LOG('Testing... ',0,'test_03_AddSubscription1')
portal_id = self.getPortalId() portal_id = self.getPortalId()
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
portal_sync.manage_addSubscription(self.sub_id1, self.publication_url, portal_sync.manage_addSubscription(title=self.sub_id1,
self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues', publication_url=self.publication_url,
self.xml_mapping,'ERP5Conduit','') subscription_url=self.subscription_url1,
destination_path='/%s/person_client1' % portal_id,
source_uri='Person',
target_uri='Person',
query='objectValues',
xml_mapping=self.xml_mapping,
conduit='ERP5Conduit',
gpg_key='',
gid_generator='getId')
sub = portal_sync.getSubscription(self.sub_id1) sub = portal_sync.getSubscription(self.sub_id1)
self.failUnless(sub is not None) self.failUnless(sub is not None)
...@@ -363,9 +378,17 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): ...@@ -363,9 +378,17 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
LOG('Testing... ',0,'test_04_AddSubscription2') LOG('Testing... ',0,'test_04_AddSubscription2')
portal_id = self.getPortalId() portal_id = self.getPortalId()
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
portal_sync.manage_addSubscription(self.sub_id2,self.publication_url, portal_sync.manage_addSubscription(title=self.sub_id2,
self.subscription_url2,'/%s/person_client2' % portal_id,'objectValues', publication_url=self.publication_url,
self.xml_mapping,'ERP5Conduit','') subscription_url=self.subscription_url2,
destination_path='/%s/person_client2' % portal_id,
source_uri='Person',
target_uri='Person',
query='objectValues',
xml_mapping=self.xml_mapping,
conduit='ERP5Conduit',
gpg_key='',
gid_generator='getId')
sub = portal_sync.getSubscription(self.sub_id2) sub = portal_sync.getSubscription(self.sub_id2)
self.failUnless(sub is not None) self.failUnless(sub is not None)
...@@ -530,7 +553,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): ...@@ -530,7 +553,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
# By default we can just give the id # By default we can just give the id
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
publication = portal_sync.getPublication(self.pub_id) publication = portal_sync.getPublication(self.pub_id)
object = publication.getObjectFromGid(self.id1) object = publication.getObjectFromId(self.id1)
self.failUnless(object is not None) self.failUnless(object is not None)
self.failUnless(object.getId()==self.id1) self.failUnless(object.getId()==self.id1)
...@@ -994,10 +1017,11 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): ...@@ -994,10 +1017,11 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
publication = portal_sync.getPublication(self.pub_id) publication = portal_sync.getPublication(self.pub_id)
self.failUnless(len(publication.getObjectList())==nb_person) self.failUnless(len(publication.getObjectList())==nb_person)
gid = self.first_name1 + ' ' + self.last_name1 # ie the title 'Sebastien Robin' gid = self.first_name1 + ' ' + self.last_name1 # ie the title 'Sebastien Robin'
gid = b16encode(gid)
person_c1 = subscription1.getObjectFromGid(gid) person_c1 = subscription1.getObjectFromGid(gid)
id_c1 = person_c1.getId() id_c1 = person_c1.getId()
self.failUnless(id_c1 in ('1','2')) # id given by the default generateNewId self.failUnless(id_c1 in ('1','2')) # id given by the default generateNewId
person_s = publication.getObjectFromGid(gid) person_s = publication.getSubscriber(self.subscription_url1).getObjectFromGid(gid)
id_s = person_s.getId() id_s = person_s.getId()
self.failUnless(id_s==self.id1) self.failUnless(id_s==self.id1)
# This will test updating object # This will test updating object
...@@ -1014,7 +1038,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): ...@@ -1014,7 +1038,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.checkSynchronizationStateIsSynchronized() self.checkSynchronizationStateIsSynchronized()
self.failUnless(len(subscription1.getObjectList())==(nb_person-1)) self.failUnless(len(subscription1.getObjectList())==(nb_person-1))
self.failUnless(len(publication.getObjectList())==(nb_person-1)) self.failUnless(len(publication.getObjectList())==(nb_person-1))
person_s = publication.getObjectFromGid(gid) person_s = publication.getSubscriber(self.subscription_url1).getObjectFromGid(gid)
person_c1 = subscription1.getObjectFromGid(gid) person_c1 = subscription1.getObjectFromGid(gid)
self.failUnless(person_s.getDescription()==self.description3) self.failUnless(person_s.getDescription()==self.description3)
self.failUnless(person_c1.getDescription()==self.description3) self.failUnless(person_c1.getDescription()==self.description3)
...@@ -1299,9 +1323,17 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): ...@@ -1299,9 +1323,17 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
LOG('Testing... ',0,'test_32_AddOneWaySubscription') LOG('Testing... ',0,'test_32_AddOneWaySubscription')
portal_id = self.getPortalId() portal_id = self.getPortalId()
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
portal_sync.manage_addSubscription(self.sub_id1, self.publication_url, portal_sync.manage_addSubscription(title=self.sub_id1,
self.subscription_url1, '/%s/person_client1' % portal_id, publication_url=self.publication_url,
'objectValues', '', 'ERP5Conduit', '') subscription_url=self.subscription_url1,
destination_path='/%s/person_client1' % portal_id,
source_uri='Person',
target_uri='Person',
query='objectValues',
xml_mapping='',
conduit='ERP5Conduit',
gpg_key='',
gid_generator='getId')
sub = portal_sync.getSubscription(self.sub_id1) sub = portal_sync.getSubscription(self.sub_id1)
self.failUnless(sub is not None) self.failUnless(sub is not None)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment