[Erp5-report] r14954 - in /erp5/trunk/products/ERP5SyncML: ./ Conduit/ dtml/ tests/

nobody at svn.erp5.org nobody at svn.erp5.org
Mon Jun 25 17:22:33 CEST 2007


Author: seb
Date: Mon Jun 25 17:22:32 2007
New Revision: 14954

URL: http://svn.erp5.org?rev=14954&view=rev
Log:
- 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

Modified:
    erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py
    erp5/trunk/products/ERP5SyncML/Publication.py
    erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py
    erp5/trunk/products/ERP5SyncML/Subscription.py
    erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py
    erp5/trunk/products/ERP5SyncML/SyncCode.py
    erp5/trunk/products/ERP5SyncML/SynchronizationTool.py
    erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py
    erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml
    erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py

Modified: erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py (original)
+++ erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py Mon Jun 25 17:22:32 2007
@@ -139,7 +139,6 @@
     xml = self.convertToXml(xml)
     if xml is None:
       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
     if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and \
         self.getSubObjectDepth(xml)==0:
@@ -153,7 +152,6 @@
       if object_id is None:
         object_id = self.getAttribute(xml,'id')
       docid = self.getObjectDocid(xml)
-      #LOG('addNode',0,'object_id: %s' % object_id)
       if object_id is not None:
         if sub_object is None:
           try:
@@ -181,7 +179,6 @@
     elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \
          and self.getSubObjectDepth(xml)>=1:
       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:
         #LOG('addNode',0,'previous xml is not none and also sub_object_id')
         # Find the previous xml corresponding to this subobject
@@ -242,7 +239,7 @@
           sub_object = object._getOb(sub_object_id)
           sub_xml = self.getSubObjectXupdate(xml)
           conflict_list += self.deleteNode(xml=sub_xml,object=sub_object,
-                                           force=force, simulate=simulate, **kw)
+                                       force=force, simulate=simulate, **kw)
         except (KeyError, AttributeError, TypeError):
           #LOG('ERP5Conduit',0,'deleteNode, Unable to delete SubObject: %s' % str(sub_object_id))
           pass
@@ -385,7 +382,6 @@
                                            #subscriber_value=data)] # not needed any more
           # We will now apply the argument with the method edit
           if args != {} and (isConflict==0 or force) and (not simulate):
-            #LOG('updateNode',0,'object._edit, args: %s' % str(args))
             #object._edit(**args)
             self.editDocument(object=object,**args)
             # It is sometimes required to do something after an edit
@@ -394,12 +390,10 @@
 
         if keyword == 'object':
           # 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, 
               simulate=simulate, **kw)['conflict_list']
         elif keyword == self.history_tag and not simulate:
           # 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,
               simulate=simulate,**kw)['conflict_list']
         elif keyword in (self.local_role_tag,self.local_permission_tag) and not simulate:
@@ -433,8 +427,9 @@
               sub_xml = self.getSubObjectXupdate(xml)
               #LOG('updateNode',0,'sub_xml: %s' % str(sub_xml))
               # Then do the udpate
-              conflict_list += self.updateNode(xml=sub_xml, object=sub_object, force=force, 
-                              previous_xml=sub_previous_xml, simulate=simulate, **kw)
+              conflict_list += self.updateNode(xml=sub_xml, object=sub_object, 
+                  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:
           sub_object = None
           try:
@@ -470,7 +465,6 @@
     This lookd inside the args dictionnary and then
     convert any unicode string to string
     """
-    #LOG('ERP5Conduit.getFormatedArgs',0,'args: %s' % str(args))
     new_args = {}
     for keyword in args.keys():
       data = args[keyword]
@@ -646,7 +640,6 @@
     xml = self.convertToXml(xml)
     for subnode in self.getElementNodeList(xml):
       if subnode.nodeName==self.xml_object_tag:
-        LOG('getSub0bjectXml: object_id:',0,object_id)
         if object_id == self.getAttribute(subnode,'id'):
           return subnode
     return None
@@ -700,7 +693,6 @@
     if xml is None:
       return
     if type(xml) in (type('a'),type(u'a')):
-      #LOG('Conduit.convertToXml xml',0,repr(xml))
       if type(xml) is type(u'a'):
         xml = xml.encode('utf-8')
       xml = Parse(xml)
@@ -981,7 +973,7 @@
                                          force=force, simulate=simulate, **kw)['conflict_list']
       elif subnode.nodeName in self.XUPDATE_DEL:
         conflict_list += conduit.deleteNode(xml=sub_xupdate, object=object, \
-                                         force=force, simulate=simulate, **kw)
+                                      force=force, simulate=simulate, **kw)
       elif subnode.nodeName in self.XUPDATE_UPDATE:
         conflict_list += conduit.updateNode(xml=sub_xupdate, object=object, \
                                          force=force, simulate=simulate, **kw)
@@ -1065,27 +1057,19 @@
     This is really usefull if you want to write your own Conduit.
     """
     conflict_list = []
-    #LOG('addNode, workflow_history isHistoryAdd:',0,self.isHistoryAdd(xml))
     # We want to add a workflow action
     wf_tool = getToolByName(object,'portal_workflow')
     wf_id = self.getAttribute(xml,'id')
     if wf_id is None: # History added by xupdate
       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]
-    #LOG('addNode, workflow_history id:',0,wf_id)
-    #LOG('addNode, workflow_history xml:',0,xml)
     #for action in self.getWorkflowActionFromXml(xml):
     status = self.getStatusFromXml(xml)
     #LOG('addNode, status:',0,status)
     add_action = self.isWorkflowActionAddable(object=object,
                                            status=status,wf_tool=wf_tool,
                                            wf_id=wf_id,xml=xml)
-    #LOG('addNode, workflow_history add_action:',0,add_action)
     if add_action and not simulate:
-      #LOG('addNode, setting status:',0,'ok')
       wf_tool.setStatusOf(wf_id,object,status)
 
     # Specific CPS, try to remove duplicate lines in portal_repository._histories
@@ -1191,4 +1175,3 @@
     xml_string = buf.getvalue()
     buf.close() 
     return xml_string
-

Modified: erp5/trunk/products/ERP5SyncML/Publication.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Publication.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Publication.py (original)
+++ erp5/trunk/products/ERP5SyncML/Publication.py Mon Jun 25 17:22:32 2007
@@ -152,7 +152,7 @@
   constructors =   (addPublication,)
 
   # 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, 
       media_type, auth_required=False, authentication_format='', 
       authentication_type=''):
@@ -162,6 +162,7 @@
     self.id = id
     self.publication_url = publication_url
     self.destination_path = destination_path
+    self.setSourceURI(source_uri)
     self.setQuery(query)
     self.xml_mapping = xml_mapping
     #self.list_subscribers = PersistentMapping()

Modified: erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py (original)
+++ erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py Mon Jun 25 17:22:32 2007
@@ -110,7 +110,7 @@
           if authentication_format == publication.getAuthenticationFormat():
             if authentication_type == publication.getAuthenticationType():
               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(':')
                 uf = self.getPortalObject().acl_users
                 for plugin_name, plugin in uf._getOb('plugins').listPlugins(
@@ -124,6 +124,8 @@
                     newSecurityManager(None, user)
                     subscriber.setUser(login)
                     break
+                  else:
+                    auth_code=self.UNAUTHORIZED
           #in all others cases, the auth_code is set to UNAUTHORIZED  
 
 
@@ -148,6 +150,13 @@
               subscriber.getSubscriptionUrl(), publication.getPublicationUrl(),
               subscriber.getLastAnchor(), subscriber.getNextAnchor()))
             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('</SyncML>\n')
           xml_a = ''.join(xml_list)

Modified: erp5/trunk/products/ERP5SyncML/Subscription.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Subscription.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Subscription.py (original)
+++ erp5/trunk/products/ERP5SyncML/Subscription.py Mon Jun 25 17:22:32 2007
@@ -42,9 +42,10 @@
 import md5
 
 try:
-    from base64 import b64encode, b64decode
+    from base64 import b64encode, b64decode, b16encode, b16decode
 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, Base):
@@ -250,13 +251,16 @@
   isPortalContent = 0 # Make sure RAD generated accessors at the class level
   
   # Constructor
-  def __init__(self,gid=None, id=None, status=None, xml_string=None,object=None):
-    self.setGid(gid)
+  def __init__(self, id=None, rid=None, status=None, xml_string=None, 
+      object=None):
     if object is not None:
       self.setPath(object.getPhysicalPath())
+      self.setObjectId(object.getId())
     else:
       self.setPath(None)
     self.setId(id)
+    self.setGid(id)
+    self.setRid(rid)
     self.status = status
     self.setXML(xml_string)
     self.partial_xml = None
@@ -280,7 +284,7 @@
       if temp_xml is not None:
         # This happens when we have sent the xml
         # and we just get the confirmation
-        self.setXML(self.getTempXML())
+        self.setXML(temp_xml)
       self.setTempXML(None)
       self.setPartialXML(None)
       self.setSubscriberXupdate(None)
@@ -367,7 +371,7 @@
 
   def getXML(self):
     """
-      set the XML corresponding to the object
+      get the XML corresponding to the object
     """
     xml =  getattr(self,'xml',None)
     if xml == '':
@@ -437,18 +441,22 @@
     """
       set the rid
     """
+    if rid is type(u'a'): 
+      rid = rid.encode('utf-8')
     self.rid = rid
 
   def getRid(self):
     """
       get the rid
     """
-    return self.rid
+    return getattr(self, 'rid', None)
 
   def setId(self, id):
     """
       set the id
     """
+    if id is type(u'a'): 
+      id = id.encode('utf-8')
     self.id = id
 
   def getId(self):
@@ -459,15 +467,31 @@
 
   def setGid(self, gid):
     """
-      set the id
-    """
+      set the gid
+    """
+    if gid is type(u'a'): 
+      gid = gid.encode('utf-8')
     self.gid = gid
 
   def getGid(self):
     """
-      get the id
+      get the 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):
     """
@@ -545,7 +569,7 @@
     """
     Returns the object corresponding to this signature
     """
-    return self.getParentValue().getObjectFromGid(self.getGid())
+    return self.getParentValue().getObjectIdGid(self.getObjectId())
 
   def checkSynchronizationNeeded(self, object):
     """
@@ -643,8 +667,9 @@
 
   # Constructor
   def __init__(self, id, title, publication_url, subscription_url, 
-      destination_path, query, xml_mapping, conduit, gpg_key, id_generator, 
-      gid_generator, media_type, login, password):
+      destination_path, source_uri, target_uri, query, xml_mapping, 
+      conduit, gpg_key, id_generator, gid_generator, media_type, login, 
+      password):
     """
       We need to create a dictionnary of
       signatures of documents which belong to the synchronisation
@@ -654,6 +679,8 @@
     self.publication_url = (publication_url)
     self.subscription_url = str(subscription_url)
     self.destination_path = str(destination_path)
+    self.setSourceURI(source_uri)
+    self.setTargetURI(target_uri)
     self.setQuery(query)
     self.setXMLMapping(xml_mapping)
     self.anchor = None
@@ -685,15 +712,31 @@
     setter for title
     """
     self.title = value
-
-  # Accessors
-  def getRemoteId(self, id, path=None):
-    """
-      Returns the remote id from a know local id
-      Returns None if...
-      path allows to implement recursive sync
-    """
-    pass
+  
+  def setSourceURI(self, value):
+    """
+    setter for source_uri
+    """
+    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):
     """
@@ -749,7 +792,7 @@
     self.last_message_id = message_id
     return True
 
-  def initLastMessageId(self, last_message_id=None):
+  def initLastMessageId(self, last_message_id=0):
     """
     set the last message id to 0
     """
@@ -767,24 +810,17 @@
     """
     self.last_sent_message = xml
 
-  def getLocalId(self, rid, path=None):
-    """
-      Returns the local id from a know remote id
-      Returns None if...
-    """
-    pass
+  def getDomainType(self):
+    """
+      return the ID
+    """
+    return self.domain_type
 
   def getId(self):
     """
       return the ID
     """
     return self.id
-
-  def getDomainType(self):
-    """
-      return the ID
-    """
-    return self.domain_type
 
   def setId(self, id):
     """
@@ -934,13 +970,13 @@
     """
       return the format of authentication
     """
-    return getattr(self, 'authentication_format', '')
+    return getattr(self, 'authentication_format', 'b64')
 
   def getAuthenticationType(self):
     """
       return the type of authentication
     """
-    return getattr(self, 'authentication_type', '')
+    return getattr(self, 'authentication_type', 'syncml:auth-basic')
 
   def setAuthenticationFormat(self, authentication_format):
     """
@@ -969,7 +1005,7 @@
       # It might be a script python
       generator = getattr(object,gid_gen)
       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
 
   def getObjectFromGid(self, gid):
@@ -977,16 +1013,13 @@
     This tries to get the object with the given gid
     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
     # the id and the gid
     object_list = self.getObjectList()
     destination = self.getDestination()
-    # LOG('getObjectFromGid', 0, 'self: %s' % self)    
-    # LOG('getObjectFromGid',0,'gid: %s' % repr(gid))
-    # LOG('getObjectFromGid oject_list',0,object_list)
-    if signature is not None and signature.getId() is not None:
-      o_id = signature.getId()
+    if signature is not None and signature.getObjectId() is not None:
+      o_id = signature.getObjectId()
       o = None
       try:
         o = destination._getOb(o_id)
@@ -998,7 +1031,18 @@
       o_gid = self.getGidFromObject(o)
       if o_gid == gid:
         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
 
 #  def setOneWaySyncFromServer(self,value):
@@ -1195,27 +1239,82 @@
     """
       add a Signature to the subscription
     """
-    if signature.getGid() in self.objectIds():
-      self._delObject(signature.getGid())
-    self._setObject(signature.getGid(), aq_base(signature) )
+    if signature.getGid() in self.getGidList():
+      self.delSignature(signature.getGid())
+    self._setObject(signature.getGid(), aq_base(signature))
 
   def delSignature(self, gid):
     """
-      add a Signature to the subscription
+      del a Signature of the subscription
     """
     #del self.signatures[gid]
     self._delObject(gid)
 
-  def getSignature(self, gid):
-    """
-      add a Signature to the subscription
+  def getSignatureFromObjectId(self, id):
+    """
+    return the signature corresponding to the gid
     """
     o = None
-    if gid in self.objectIds():
-      o = self._getOb(gid)
-    #if o is not None:
-    #  return o.__of__(self)
+    # XXX very slow
+    for signature in self.getSignatureList():
+      if id == signature.getObjectId():
+        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
+
+  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):
     """
@@ -1237,12 +1336,6 @@
     while len(self.objectIds())>0:
       for id in self.objectIds():
         self._delObject(id)
-
-  def getGidList(self):
-    """
-    Returns the list of ids from signature
-    """
-    return self.objectIds()
 
   def getConflictList(self):
     """
@@ -1279,20 +1372,6 @@
         if o != object_path:
           new_list.append(o)
       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):
     """

Modified: erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py (original)
+++ erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py Mon Jun 25 17:22:32 2007
@@ -49,7 +49,7 @@
     # syncml header
     xml(self.SyncMLHeader(subscription.incrementSessionId(), 
       subscription.incrementMessageId(), subscription.getPublicationUrl(), 
-      subscription.getSubscriptionUrl()))
+      subscription.getSubscriptionUrl(), source_name=subscription.getLogin()))
 
     # syncml body
     xml(' <SyncBody>\n')
@@ -59,16 +59,15 @@
 
     # alert message
     xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(),
-                            subscription.getPublicationUrl(),
-                            subscription.getDestinationPath(),
+                            subscription.getTargetURI(),
+                            subscription.getSourceURI(),
                             subscription.getLastAnchor(), 
                             subscription.getNextAnchor()))
     cmd_id += 1
-
-    xml('  <Put>\n')
-    xml('   <CmdID>%s</CmdID>\n' % cmd_id)
-    cmd_id += 1
-    xml('  </Put>\n')
+    syncml_put = self.SyncMLPut(cmd_id, subscription)
+    if syncml_put not in ('', None):
+      xml(syncml_put)
+      cmd_id += 1
     xml(' </SyncBody>\n')
     xml('</SyncML>\n')
     xml_a = ''.join(xml_list)
@@ -79,20 +78,17 @@
 
     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
     """
-    #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
-    subscription = self.getSubscription(id)
 
     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())
     if msg==None:
-      response = self.SubSyncInit(self.getSubscription(id))
+      response = self.SubSyncInit(subscription)
     else:
       xml_client = msg
       if isinstance(xml_client, str) or isinstance(xml_client, unicode):
@@ -105,21 +101,24 @@
           if status_code == self.AUTH_REQUIRED:
             if self.checkChal(xml_client):
               authentication_format, authentication_type = self.getChal(xml_client)
-              subscription.setAuthenticationFormat(authentication_format)
-              subscription.setAuthenticationType(authentication_type)
+              #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.setAuthenticationType(authentication_type)
             else:
               raise ValueError, "Sorry, the server chalenge for an \
                   authentication, but the authentication format is not find"
 
             #LOG('readResponse', 0, 'Authentication required')
-            response = self.SubSyncCred(id, xml_client)
+            response = self.SubSyncCred(subscription, xml_client)
           elif status_code == self.UNAUTHORIZED:
-            #LOG('readResponse', 0, 'Bad authentication')
+            LOG('readResponse', 0, 'Bad authentication')
             return {'has_response':0,'xml':xml_client}
           else:
-            response = self.SubSyncModif(self.getSubscription(id), xml_client)
+            response = self.SubSyncModif(subscription, xml_client)
         else: 
-            response = self.SubSyncModif(self.getSubscription(id), xml_client)
+            response = self.SubSyncModif(subscription, xml_client)
 
 
     if RESPONSE is not None:
@@ -127,43 +126,43 @@
     else:
       return response
 
-  def SubSyncCred (self, id, msg=None, RESPONSE=None):
+  def SubSyncCred (self, subscription, msg=None, RESPONSE=None):
     """
       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
-    subscription = self.getSubscription(id)
     xml_list = []
     xml = xml_list.append
     xml('<SyncML>\n')
     # syncml header
     data = "%s:%s" % (subscription.getLogin(), subscription.getPassword())
     data=subscription.encode(subscription.getAuthenticationFormat(), data)
-    xml(self.SyncMLHeader(subscription.getSessionId(),
-      subscription.incrementMessageId(), subscription.getPublicationUrl(),
-      subscription.getSubscriptionUrl(), dataCred=data, 
+    xml(self.SyncMLHeader(
+      subscription.incrementSessionId(),
+      subscription.incrementMessageId(), 
+      subscription.getPublicationUrl(),
+      subscription.getSubscriptionUrl(), 
+      source_name=subscription.getLogin(), 
+      dataCred=data, 
       authentication_format=subscription.getAuthenticationFormat(), 
       authentication_type=subscription.getAuthenticationType()))
 
     # syncml body
     xml(' <SyncBody>\n')
 
+    # We have to set every object as NOT_SYNCHRONIZED
+    subscription.startSynchronization()
+
     # alert message
     xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(),
-                            subscription.getPublicationUrl(),
-                            subscription.getDestinationPath(),
+                            subscription.getTargetURI(),
+                            subscription.getSourceURI(),
                             subscription.getLastAnchor(),
                             subscription.getNextAnchor()))
     cmd_id += 1
-
-    xml('  <Put>\n')
-    xml('   <CmdID>%s</CmdID>\n' % cmd_id)
+    xml(self.SyncMLPut(cmd_id, subscription))
     cmd_id += 1
-    xml('  </Put>\n')
+    xml('  <Final/>\n')
     xml(' </SyncBody>\n')
     xml('</SyncML>\n')
     xml_a = ''.join(xml_list)

Modified: erp5/trunk/products/ERP5SyncML/SyncCode.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SyncCode.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SyncCode.py (original)
+++ erp5/trunk/products/ERP5SyncML/SyncCode.py Mon Jun 25 17:22:32 2007
@@ -41,6 +41,8 @@
 
   # SyncML Status Codes
   SUCCESS = 200
+  ITEM_ADDED = 201
+
   CHUNK_OK = 214
   CONFLICT = 409 # A conflict is detected
   CONFLICT_MERGE = 207 # We have merged the two versions, sending

Modified: erp5/trunk/products/ERP5SyncML/SynchronizationTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SynchronizationTool.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SynchronizationTool.py (original)
+++ erp5/trunk/products/ERP5SyncML/SynchronizationTool.py Mon Jun 25 17:22:32 2007
@@ -161,7 +161,7 @@
   security.declareProtected(Permissions.ModifyPortalContent, 
       'manage_addPublication')
   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, 
             media_type=None, auth_required=0, authentication_format='', 
             authentication_type='', RESPONSE=None):
@@ -174,7 +174,7 @@
     folder = self.getObjectContainer()
     new_id = self.getPublicationIdFromTitle(title)
     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, 
                       auth_required, authentication_format, authentication_type)
     folder._setObject( new_id, pub )
@@ -187,7 +187,8 @@
   security.declareProtected(Permissions.ModifyPortalContent, 
       'manage_addSubscription')
   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, 
                        media_type=None, login=None, password=None, 
                        RESPONSE=None):
@@ -201,7 +202,8 @@
     folder = self.getObjectContainer()
     new_id = self.getSubscriptionIdFromTitle(title)
     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, 
                        login, password)
     folder._setObject( new_id, sub )
@@ -214,7 +216,7 @@
   security.declareProtected(Permissions.ModifyPortalContent, 
       'manage_editPublication')
   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, 
                        media_type=None, auth_required=0, 
                        authentication_format='', authentication_type='', 
@@ -226,6 +228,7 @@
     pub.setTitle(title)
     pub.setPublicationUrl(publication_url)
     pub.setDestinationPath(destination_path)
+    pub.setSourceURI(source_uri)
     pub.setQuery(query)
     pub.setConduit(conduit)
     pub.setXMLMapping(xml_mapping)
@@ -243,9 +246,9 @@
   security.declareProtected(Permissions.ModifyPortalContent, 
       'manage_editSubscription')
   def manage_editSubscription(self, title, publication_url, subscription_url,
-      destination_path, query, xml_mapping, conduit, gpg_key, 
-      synchronization_id_generator, gid_generator, media_type=None,login='', 
-      password='', RESPONSE=None):
+      destination_path, source_uri, target_uri, query, xml_mapping, conduit, 
+      gpg_key, synchronization_id_generator, gid_generator, media_type=None, 
+      login='', password='', RESPONSE=None):
     """
       modify a subscription
     """
@@ -253,6 +256,8 @@
     sub.setTitle(title)
     sub.setPublicationUrl(publication_url)
     sub.setDestinationPath(destination_path)
+    sub.setSourceURI(source_uri)
+    sub.setTargetURI(target_uri)
     sub.setQuery(query)
     sub.setConduit(conduit)
     sub.setXMLMapping(xml_mapping)
@@ -319,7 +324,7 @@
     """
       reset a subscription
     """
-    self.SubSync(title)
+    self.SubSync(self.getSubscription(title))
     if RESPONSE is not None:
       RESPONSE.redirect('manageSubscriptions')
 
@@ -371,7 +376,7 @@
 
   def getSubscription(self, title):
     """
-      Returns the subscription with this id
+      Returns the subscription with this title
     """
     for s in self.getSubscriptionList():
       if s.getTitle() == title:
@@ -489,7 +494,7 @@
           subscriber_list = [domain]
         #LOG('getSynchronizationState, subscriber_list:',0,subscriber_list)
         for subscriber in subscriber_list:
-          signature = subscriber.getSignature(o_id)
+          signature = subscriber.getSignatureFromObjectId(o_id)
           if signature is not None:
             state = signature.getStatus()
             #LOG('getSynchronizationState:',0,'sub.dest :%s, state: %s' % \
@@ -514,7 +519,7 @@
     subscriber = conflict.getSubscriber()
     # get the signature:
     #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()
     #LOG('p_sync.applyPublisherValue, copy_path: ',0,copy_path)
     signature.delConflict(conflict)
@@ -690,7 +695,7 @@
     subscriber = conflict.getSubscriber()
     # get the signature:
     #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
     conduit_name = subscriber.getConduit()
     conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]), 
@@ -885,12 +890,22 @@
     opener = urllib2.build_opener(proxy_handler, proxy_auth_handler, 
         auth_handler, urllib2.HTTPHandler)
     urllib2.install_opener(opener)
-    to_encode = {'text':xml,'sync_id':sync_id}
-    encoded = urllib.urlencode(to_encode)
+    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:
       to_url = to_url + '/portal_synchronizations/readResponse'
-    request = urllib2.Request(url=to_url,data=encoded)
-    #result = urllib2.urlopen(request).read()
+    encoded = urllib.urlencode(to_encode)
+    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:
       result = urllib2.urlopen(request).read()
     except socket.error, msg:
@@ -898,10 +913,12 @@
           sync_id=sync_id, xml=xml, domain=domain)
       LOG('sendHttpResponse, socket ERROR:',0,msg)
       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, result:',0,result)
     if domain is not None:
       if domain.domain_type == self.SUB:
         gpg_key = domain.getGPGKey()
@@ -929,7 +946,7 @@
     if len(message_list) == 0:
       for subscription in self.getSubscriptionList():
         #LOG('sync, subcription:',0,subscription)
-        self.activate(activity='RAMQueue').SubSync(subscription.getTitle())
+        self.activate(activity='RAMQueue').SubSync(subscription)
 
   security.declarePublic('readResponse')
   def readResponse(self, text=None, sync_id=None, to_url=None, from_url=None):
@@ -937,9 +954,6 @@
     We will look at the url and we will see if we need to send mail, http
     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
     uf = self.acl_users
     user = uf.getUserById('syncml').__of__(uf)
@@ -994,9 +1008,9 @@
       for subscription in self.getSubscriptionList():
         if subscription.getSubscriptionUrl()==url and \
             subscription.getTitle()==sync_id:
-              result = self.activate(activity='RAMQueue').SubSync(sync_id, 
-                  text)
-              #result = self.SubSync(sync_id,xml)
+              result = self.activate(activity='RAMQueue').SubSync(\
+                  self.getSubscription(sync_id), text)
+              #result = self.SubSync(self.getSubscription(sync_id),xml)
 
     # we use from only if we have a file 
     elif isinstance(from_url, str):
@@ -1041,24 +1055,4 @@
     conduit_object = getattr(conduit_module, conduit)()
     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 )

Modified: erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py (original)
+++ erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py Mon Jun 25 17:22:32 2007
@@ -30,7 +30,7 @@
 from Products.ERP5SyncML.SyncCode import SyncCode
 from Products.ERP5SyncML.Subscription import Signature
 from DateTime import DateTime
-from cStringIO import StringIO
+from StringIO import StringIO
 from xml.dom.ext import PrettyPrint
 import random
 from zLOG import LOG
@@ -49,6 +49,11 @@
   class Parse:
     def __init__(self, *args, **kw):
       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):
 
@@ -79,8 +84,8 @@
     if dataCred not in (None, ''):
       xml('  <Cred>\n')
       xml('   <Meta>\n')
-      xml('    <Format>%s</Format>\n' % authentication_format)
-      xml('    <Type>%s</Type>\n' % authentication_type)
+      xml("    <Format xmlns='syncml:metinf'>%s</Format>\n" % authentication_format)
+      xml("    <Type xmlns='syncml:metinf'>%s</Type>\n" % authentication_type)
       xml('   </Meta>\n')
       xml('   <Data>%s</Data>\n' % dataCred)
       xml('  </Cred>\n')
@@ -142,18 +147,37 @@
     xml_a = ''.join(xml_list)
     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
     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 = xml_list.append
     xml('  <Status>\n')
-    xml('   <CmdID>%s</CmdID>\n' % cmd_id)
-    xml('   <TargetRef>%s</TargetRef>\n' % target_ref)
-    xml('   <Cmd>%s</Cmd>\n' % cmd)
-    xml('   <Data>%s</Data>\n' % sync_code)
+    #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)
+    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)
+    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('  </Status>\n')
     xml_a = ''.join(xml_list)
     return xml_a
@@ -172,14 +196,95 @@
     xml('   <SourceRef>%s</SourceRef>\n' % source_ref)
     xml('   <Chal>\n')
     xml('    <Meta>\n')
-    xml('     <Format>%s</Format>\n' % auth_format)
-    xml('     <Type>%s</Type>\n' % auth_type)
+    xml("     <Format xmlns='syncml:metinf'>%s</Format>\n" % auth_format)
+    xml("     <Type xmlns='syncml:metinf'>%s</Type>\n" % auth_type)
     xml('    </Meta>\n')
     xml('   </Chal>\n')
     xml('   <Data>%s</Data>\n' % str(data_code))
     xml('  </Status>\n')
     xml_a = ''.join(xml_list)
     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):
     """
@@ -213,9 +318,14 @@
     xml('     <Source>\n')
     xml('      <LocURI>%s</LocURI>\n' % gid)
     xml('     </Source>\n')
-    xml('     <Data>\n')
-    xml(xml_string)
-    xml('     </Data>\n')
+    if media_type == self.MEDIA_TYPE['TEXT_XML']:
+      xml('     <Data>')
+      xml(xml_string)
+      xml('</Data>\n')
+    else:
+      xml('     <Data><![CDATA[')
+      xml(xml_string)
+      xml('\n]]></Data>\n')
     if more_data == 1:
       xml('     <MoreData/>\n')
     xml('    </Item>\n')
@@ -258,7 +368,7 @@
     xml('     <Source>\n')
     xml('      <LocURI>%s</LocURI>\n' % str(gid))
     xml('     </Source>\n')
-    xml('     <Data>\n')
+    xml('     <Data>')
     xml(xml_string)
     xml('     </Data>\n')
     if more_data == 1:
@@ -308,7 +418,6 @@
         xml = xml_method()
       else:
         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
 
   def getSessionId(self, xml):
@@ -370,7 +479,10 @@
     """
       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):
     """
@@ -386,7 +498,10 @@
       Return the value of the command inside the xml_stream
     """
     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:
       return None
 
@@ -399,8 +514,8 @@
     data=''
 
     first_node = xml.childNodes[0]
-    format = first_node.xpath('string(/SyncML/SyncHdr/Cred/Meta/Format)')
-    type = first_node.xpath('string(/SyncML/SyncHdr/Cred/Meta/Type)')
+    format = first_node.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Format'])")
+    type = first_node.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Type'])")
     data = first_node.xpath('string(/SyncML/SyncHdr/Cred/Data)')
 
     format = format.encode('utf-8')
@@ -420,12 +535,12 @@
     """
       return the chalenge information : format and type
     """    
-    format=''
-    type=''
+    format=None
+    type=None
 
     first_node = xml.childNodes[0]
-    format = first_node.xpath('string(/SyncML/SyncBody/Status/Chal/Meta/Format)')
-    type = first_node.xpath('string(/SyncML/SyncBody/Status/Chal/Meta/Type)')
+    format = first_node.xpath("string(//*[local-name() = 'Format'])")
+    type = first_node.xpath("string(//*[local-name() = 'Type'])")
 
     format = format.encode('utf-8')
     type = type.encode('utf-8')
@@ -468,7 +583,7 @@
       sync = True
     return sync
 
-  def CheckStatus(self, xml_stream):
+  def checkStatus(self, xml_stream):
     """
       Check if there's a Status section in the xml_xtream
     """
@@ -477,43 +592,34 @@
       status = True
     return status
 
-  def getNextSyncAction(self, xml_stream, last_action):
-    """
-      It goes throw actions in the Sync section of the SyncML file,
-      then it returns the next action (could be "add", "replace",
-      "delete").
-    """
-    first_node = xml_stream.childNodes[0]
-    client_body = first_node.childNodes[3]
-    if client_body.nodeName != "SyncBody":
-      print "This is not a SyncML Body"
-    next_action = None
-    for subnode in client_body.childNodes:
-      if subnode.nodeType == subnode.ELEMENT_NODE and \
-          subnode.nodeName == "Sync":
-        # if we didn't use this method before
-        if last_action == None and len(subnode.childNodes) > 1:
-          next_action = subnode.childNodes[1]
-        else:
-          found = None
-          for subnode2 in subnode.childNodes:
-            if subnode2.nodeType == subnode.ELEMENT_NODE and \
-                subnode2 != last_action and found is None:
-              pass
-            elif subnode2.nodeType == subnode.ELEMENT_NODE and \
-                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 getSyncActionList(self, xml_stream):
+    """
+    return the list of the action (could be "add", "replace", "delete").
+    """
+    return xml_stream.xpath('//Add|//Delete|//Replace')
+
+  def getSyncBodyStatusList(self, xml_stream):
+    """
+    return the list of dictionary corredponding to the data of each status bloc
+    the data are : cmd, code and source
+    """
+    status_list = []
+    xml = xml_stream.xpath('//Status')
+    for status in xml:
+      tmp_dict = {}
+      tmp_dict['cmd']     = status.xpath('string(./Cmd)').encode('utf-8')
+      tmp_dict['code']    = status.xpath('string(./Data)').encode('utf-8')
+      tmp_dict['source']  = status.xpath('string(./SourceRef)').encode('utf-8')
+      tmp_dict['target']  = status.xpath('string(./TargetRef)').encode('utf-8')
+      status_list.append(tmp_dict)
+
+    return status_list
 
   def getNextSyncBodyStatus(self, xml_stream, last_status):
     """
-      It goes throw actions in the Sync section of the SyncML file,
-      then it returns the next action (could be "add", "replace",
-      "delete").
+    It goes throw actions in the Sync section of the SyncML file,
+    then it returns the next action (could be "add", "replace",
+    "delete").
     """
     first_node = xml_stream.childNodes[0]
     client_body = first_node.childNodes[3]
@@ -539,16 +645,19 @@
     """
     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):
     """
       Return the node starting with <object....> of the action
     """
-    if action.xpath('Item/Data') not in ([], None):
-      data_node = action.xpath('Item/Data')[0]
-      if data_node.hasChildNodes() and data_node.childNodes.__len__()>1:
-        return data_node.childNodes[1]
+    if action.xpath('.//Item/Data') not in ([], None):
+      data_node = action.xpath('.//Item/Data')[0]
+      if data_node.hasChildNodes():
+        return data_node.childNodes[0]
     return None
 
   def getPartialData(self, action):
@@ -575,20 +684,16 @@
 
     return None
 
-  def getActionId(self, action):
+  def getActionId(self, action, action_name):
     """
       Return the rid of the object described by the action
     """
-    for subnode in action.childNodes:
-      if subnode.nodeType == subnode.ELEMENT_NODE and \
-          subnode.nodeName == 'Item':
-        for subnode2 in subnode.childNodes:
-          if subnode2.nodeType == subnode2.ELEMENT_NODE and \
-              subnode2.nodeName == 'Source':
-            for subnode3 in subnode2.childNodes:
-              if subnode3.nodeType == subnode3.ELEMENT_NODE and \
-                  subnode3.nodeName == 'LocURI':
-                return str(subnode3.childNodes[0].data)
+    id = action.xpath('string(.//Item/Source/LocURI)')
+    if id in (None, ''):
+      id = action.xpath('string(.//Item/Target/LocURI)')
+    if isinstance(id, unicode):
+      id = id.encode('utf-8')
+    return id
 
   def checkActionMoreData(self, action):
     """
@@ -602,7 +707,10 @@
     """
       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):
     """
@@ -647,18 +755,21 @@
       #object_gid = domain.getGidFromObject(object)
       local_gid_list = map(lambda x: domain.getGidFromObject(x),object_list)
       # Objects to remove
-      #for object_id in id_list:
       for object_gid in subscriber.getGidList():
         if not (object_gid in local_gid_list):
           # 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
                                                   # but no local object
             xml_object = signature.getXML()
             if xml_object is not None: # This prevent to delete an object that
                                       # 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()\
                   or '', object_gid=object_gid,cmd_id=cmd_id)
+              cmd_id += 1
 
     local_gid_list = []
     #for object in domain.getObjectList():
@@ -683,9 +794,10 @@
         #LOG('getSyncMLData',0,'xml_mapping: %s' % str(domain.xml_mapping))
         #LOG('getSyncMLData',0,'code: %s' % str(self.getAlertCode(remote_xml)))
         #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,'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
         if signature is not None:
@@ -702,7 +814,10 @@
           xml_object = self.getXMLObject(object=object, 
               xml_mapping=domain.xml_mapping)
           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)
           if xml_string.count('\n') > self.MAX_LINES:
             if xml_string.find('--') >= 0: # This make comment fails, so we need to replace
@@ -714,16 +829,18 @@
             while i < self.MAX_LINES:
               short_string += rest_string[:rest_string.find('\n')+1]
               rest_string = xml_string[len(short_string):]
-              #LOG('XMLSyncUtils',0,'rest_string: %s' % str(rest_string))
               i += 1
             #LOG('getSyncMLData',0,'setPartialXML with: %s' % str(rest_string))
             signature.setPartialXML(rest_string)
-            status =self.PARTIAL
+            status = self.PARTIAL
             signature.setAction('Add')
             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, 
-              gid=object_gid, xml_string=xml_string, more_data=more_data, 
-              media_type=subscriber.getMediaType())
+              gid=gid, xml_string=xml_string, 
+              more_data=more_data, media_type=subscriber.getMediaType())
           cmd_id += 1
           signature.setStatus(status)
           subscriber.addSignature(signature)
@@ -734,8 +851,9 @@
           #LOG('getSyncMLData',0,'checkMD5: %s' % str(signature.checkMD5(xml_object)))
           #LOG('getSyncMLData',0,'getStatus: %s' % str(signature.getStatus()))
           if signature.getStatus()==self.PUB_CONFLICT_MERGE:
-            xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id,
-                                  self.CONFLICT_MERGE,'Replace')
+            xml_confirmation += self.SyncMLConfirmation(cmd_id=cmd_id, 
+                source_ref=signature.getGid(), sync_code=self.CONFLICT_MERGE, 
+                cmd='Replace')
           set_synchronized = 1
           if not signature.checkMD5(xml_object):
             set_synchronized = 0
@@ -762,9 +880,12 @@
             if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
               xml_string = self.getXMLObject(object=object, 
                   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, 
-                gid=object_gid, xml_string=xml_string, more_data=more_data, 
-                media_type=subscriber.getMediaType())
+                gid=gid, xml_string=xml_string, 
+                more_data=more_data, media_type=subscriber.getMediaType())
             cmd_id += 1
             signature.setTempXML(xml_object)
           # Now we can apply the xupdate from the subscriber
@@ -780,6 +901,7 @@
             signature.setTempXML(xml_object)
           if set_synchronized: # We have to do that after this previous update
             # We should not have this case when we are in CONFLICT_MERGE
+
             signature.setStatus(self.SYNCHRONIZED)
         elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN:
           # We have decided to apply the update
@@ -789,8 +911,9 @@
           xml_update = signature.getPartialXML()
           conduit.updateNode(xml=signature.getPartialXML(), object=object,
                             previous_xml=signature.getXML(),force=1)
-          xml_confirmation += self.SyncMLConfirmation(cmd_id,object_gid,
-                                self.CONFLICT_CLIENT_WIN,'Replace')
+          xml_confirmation += self.SyncMLConfirmation(cmd_id=cmd_id, 
+              target_ref=object_gid, sync_code=self.CONFLICT_CLIENT_WIN,
+              cmd='Replace')
           signature.setStatus(self.SYNCHRONIZED)
         elif signature.getStatus()==self.PARTIAL:
           xml_string = signature.getPartialXML()
@@ -815,13 +938,19 @@
                 xml_mapping=domain.xml_mapping)
           #LOG('xml_string =', 0, xml_string)
           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,
-                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())
           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, 
-                gid=object_gid, xml_string=xml_string, more_data=more_data, 
-                media_type=subscriber.getMediaType())
+                gid=gid, xml_string=xml_string, 
+                more_data=more_data, media_type=subscriber.getMediaType())
     return (syncml_data,xml_confirmation,cmd_id)
 
   def applyActionList(self, domain=None, subscriber=None,destination_path=None,
@@ -830,27 +959,42 @@
     This just look to a list of action to do, then id applies
     each action one by one, thanks to a conduit
     """
-    next_action = self.getNextSyncAction(remote_xml, None)
     xml_confirmation = ''
     has_next_action = 0
-    if next_action is not None:
-      has_next_action = 1
-    while next_action != None:
+    for action in self.getSyncActionList(remote_xml):
       conflict_list = []
       status_code = self.SUCCESS
       # Thirst we have to check the kind of action it is
-      partial_data = self.getPartialData(next_action)
-      object_gid = self.getActionId(next_action)
-      signature = subscriber.getSignature(object_gid)
-      object = domain.getObjectFromGid(object_gid)
+      partial_data = self.getPartialData(action)
+      rid = self.getActionId(action, action.nodeName)
+      if action.nodeName != 'Delete':
+        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:
         #LOG('applyActionList, signature is None',0,signature)
-        signature = Signature(gid=object_gid, status=self.NOT_SYNCHRONIZED, 
-            object=object).__of__(subscriber)
+        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)
+        signature.setObjectId(object_id)
         subscriber.addSignature(signature)
       force = signature.getForce()
       #LOG('applyActionList',0,'object: %s' % repr(object))
-      if self.checkActionMoreData(next_action) == 0:
+      if self.checkActionMoreData(action) == 0:
         data_subnode = None
         if partial_data != None:
           signature_partial_xml = signature.getPartialXML()
@@ -859,20 +1003,19 @@
           else:
             data_subnode = partial_data
           #LOG('SyncModif',0,'data_subnode: %s' % data_subnode)
-          #data_subnode = FromXml(data_subnode)
           if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_XML']:
             data_subnode = Parse(data_subnode)
             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
         else:
           if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
-            data_subnode = self.getDataText(next_action)
+            data_subnode = self.getDataText(action)
           else:
-            data_subnode = self.getDataSubNode(next_action)
-        if next_action.nodeName == 'Add':
+            data_subnode = self.getDataSubNode(action)
+        if action.nodeName == 'Add':
           # Then store the xml of this new subobject
           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:
             add_data = conduit.addNode(xml=data_subnode, 
                 object=destination_path, object_id=object_id)
@@ -880,14 +1023,14 @@
               conflict_list += add_data['conflict_list']
             # Retrieve directly the object from addNode
             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:
               signature.setPath(object.getPhysicalPath())
-            #LOG('applyActionList',0,'object after add: %s' % repr(object))
+              signature.setObjectId(object.getId())
           else:
             #Object was retrieve but need to be updated without recreated
             #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,
                                        object=destination_path,
                                        object_id=object_id,
@@ -901,17 +1044,20 @@
             #if mapping is not None:
             #  xml_object = mapping()
             signature.setStatus(self.SYNCHRONIZED)
-            signature.setId(object.getId())
+            #signature.setId(object.getId())
             signature.setPath(object.getPhysicalPath())
             signature.setXML(xml_object)
-            xml_confirmation +=\
-                 self.SyncMLConfirmation(cmd_id,object_gid,self.SUCCESS,'Add')
+            xml_confirmation += self.SyncMLConfirmation(
+                cmd_id=cmd_id, 
+                cmd='Add', 
+                sync_code=self.ITEM_ADDED,
+                remote_xml=action)
             cmd_id +=1
-        elif next_action.nodeName == 'Replace':
+        elif action.nodeName == 'Replace':
           #LOG('SyncModif',0,'object: %s will be updated...' % str(object))
           if object is not None:
             #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))
             previous_xml = signature.getXML()
             #LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
@@ -934,8 +1080,11 @@
               signature.setPartialXML(data_subnode_string)
             elif not simulate:
               signature.setStatus(self.SYNCHRONIZED)
-            xml_confirmation += self.SyncMLConfirmation(cmd_id,
-                                        object_gid,status_code,'Replace')
+            xml_confirmation += self.SyncMLConfirmation(\
+                cmd_id=cmd_id, 
+                cmd='Replace', 
+                sync_code=status_code,
+                remote_xml=action)
             cmd_id +=1
             if simulate:
               # This means we are on the publiher side and we want to store
@@ -947,17 +1096,22 @@
               #LOG('applyActionList, subscriber_xupdate:',0,data_subnode_string)
               signature.setSubscriberXupdate(data_subnode_string)
 
-        elif next_action.nodeName == 'Delete':
+        elif action.nodeName == 'Delete':
           object_id = signature.getId()
           if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: 
-            data_subnode = self.getDataText(next_action)
+            data_subnode = self.getDataText(action)
           else:
-            data_subnode = self.getDataSubNode(next_action)
-          conduit.deleteNode(xml=data_subnode, object=destination_path, 
-              object_id=object_id)
-          subscriber.delSignature(object_gid)
-          xml_confirmation += self.SyncMLConfirmation(cmd_id, 
-              object_gid,status_code,'Delete')
+            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, 
+                object_id=subscriber.getObjectFromGid(object_id).getId())
+            subscriber.delSignature(gid)
+          xml_confirmation += self.SyncMLConfirmation(
+              cmd_id=cmd_id, 
+              cmd='Delete',
+              sync_code=status_code,
+              remote_xml=action)
       else: # We want to retrieve more data
         signature.setStatus(self.PARTIAL)
         #LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial))
@@ -967,13 +1121,17 @@
         signature.setPartialXML(previous_partial)
         #LOG('SyncModif',0,'previous_partial: %s' % str(previous_partial))
         #LOG('SyncModif',0,'waiting more data for :%s' % signature.getId())
-        xml_confirmation += self.SyncMLConfirmation(cmd_id, object_gid, 
-            self.WAITING_DATA, next_action.nodeName)
+        #xml_confirmation += self.SyncMLConfirmation(cmd_id, object_gid, 
+        #    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:
         # We had a conflict
         signature.setStatus(self.CONFLICT)
 
-      next_action = self.getNextSyncAction(remote_xml, next_action)
     return (xml_confirmation,has_next_action,cmd_id)
 
   def applyStatusList(self, subscriber=None,remote_xml=None):
@@ -981,42 +1139,44 @@
     This read a list of status list (ie syncml confirmations).
     This method have to change status codes on signatures
     """
-    next_status = self.getNextSyncBodyStatus(remote_xml, None)
-    #LOG('applyStatusList, next_status',0,next_status)
-    # We do not want the first one
-    next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
+    status_list = self.getSyncBodyStatusList(remote_xml)
     has_status_list = 0
-    if next_status is not None:
-      has_status_list = 1
     destination_waiting_more_data = 0
-    while next_status != None:
-      object_gid = self.getStatusTarget(next_status)
-      status_code = self.getStatusCode(next_status)
-      status_cmd = self.getStatusCommand(next_status)
-      signature = subscriber.getSignature(object_gid)
-      #LOG('SyncModif',0,'next_status: %s' % str(status_code))
-      if status_cmd in ('Add','Replace'):
-        if status_code == self.CHUNK_OK:
-          destination_waiting_more_data = 1
-          signature.setStatus(self.PARTIAL)
-        elif status_code == self.CONFLICT:
-          signature.setStatus(self.CONFLICT)
-        elif status_code == self.CONFLICT_MERGE:
-          # We will have to apply the update, and we should not care about conflicts
-          # so we have to force the update
-          signature.setStatus(self.NOT_SYNCHRONIZED)
-          signature.setForce(1)
-        elif status_code == self.CONFLICT_CLIENT_WIN:
-          # The server was agree to apply our updates, nothing to do
-          signature.setStatus(self.SYNCHRONIZED)
-        elif status_code == self.SUCCESS:
-          signature.setStatus(self.SYNCHRONIZED)
-      elif status_cmd == 'Delete':
-        if status_code == self.SUCCESS:
-          subscriber.delSignature(object_gid)
-      next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
+    if status_list != []:
+      for status in status_list:
+        status_cmd = status['cmd']
+        #if status_cmd in ('Delete'):
+        #  object_gid = status['target']
+        #else:
+        object_gid = status['source']
+        status_code = int(status['code'])
+        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:
+            destination_waiting_more_data = 1
+            signature.setStatus(self.PARTIAL)
+          elif status_code == self.CONFLICT:
+            signature.setStatus(self.CONFLICT)
+          elif status_code == self.CONFLICT_MERGE:
+            # We will have to apply the update, and we should not care 
+            # about conflicts, so we have to force the update
+            signature.setStatus(self.NOT_SYNCHRONIZED)
+            signature.setForce(1)
+          elif status_code == self.CONFLICT_CLIENT_WIN:
+            # The server was agree to apply our updates, nothing to do
+            signature.setStatus(self.SYNCHRONIZED)
+          elif status_code in (self.SUCCESS, self.ITEM_ADDED):
+            signature.setStatus(self.SYNCHRONIZED)
+        elif status_cmd == 'Delete':
+          if status_code == self.SUCCESS:
+            signature = subscriber.getSignatureFromGid(object_gid)
+            if signature == None:
+              signature = subscriber.getSignatureFromRid(object_gid)
+            subscriber.delSignature(signature.getGid())
     return (destination_waiting_more_data, has_status_list)
-
 
 
 class XMLSyncUtils(XMLSyncUtilsMixin):
@@ -1121,7 +1281,7 @@
     xml_list = []
     xml = xml_list.append
     xml('<SyncML>\n')
-    
+
     # syncml header
     if domain.domain_type == self.PUB:
       xml(self.SyncMLHeader(subscriber.getSessionId(), 
@@ -1130,54 +1290,114 @@
     elif domain.domain_type == self.SUB:
       xml(self.SyncMLHeader(domain.getSessionId(), domain.incrementMessageId(),
         domain.getPublicationUrl(), domain.getSubscriptionUrl()))
-
-
-    cmd_id += 1
+    
     # Add or replace objects
     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
     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 = ''
-    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
     if destination_waiting_more_data == 1:
       xml(self.SyncMLAlert(cmd_id, self.WAITING_DATA,
-                              destination_url,
-                              domain.getDestinationPath(),
+                              subscriber.getTargetURI(),
+                              subscriber.getSourceURI(),
                               subscriber.getLastAnchor(), 
                               subscriber.getNextAnchor()))
     # 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 != '':
       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('  </Sync>\n')
+    xml(xml_confirmation)
     xml('  <Final/>\n')
     xml(' </SyncBody>\n')
     xml('</SyncML>\n')
     xml_a = ''.join(xml_list)
+
     if domain.domain_type == self.PUB: # We always reply
       subscriber.setLastSentMessage(xml_a)
       self.sendResponse(from_url=domain.publication_url, 
@@ -1186,7 +1406,7 @@
       has_response = 1
     elif domain.domain_type == self.SUB:
       if self.checkAlert(remote_xml) or \
-         (xml_confirmation,syncml_data)!=('','') or \
+          (xml_confirmation,syncml_data)!=('','') or \
           has_status_list:
         subscriber.setLastSentMessage(xml_a)
         self.sendResponse(from_url=domain.subscription_url, 

Modified: erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml Mon Jun 25 17:22:32 2007
@@ -70,6 +70,16 @@
       <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">
         Query
         </label></div>
         </td>

Modified: erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml Mon Jun 25 17:22:32 2007
@@ -80,6 +80,26 @@
       <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>
+        <td align="left" valign="top">
+        <div class="form-label">
         Query
         </label></div>
         </td>

Modified: erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml Mon Jun 25 17:22:32 2007
@@ -61,6 +61,16 @@
     </td>
     <td align="left" valign="top">
     <input type="text" name="destination_path" size="40" />
+    </td>
+  </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>

Modified: erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml Mon Jun 25 17:22:32 2007
@@ -71,6 +71,26 @@
     </td>
     <td align="left" valign="top">
     <input type="text" name="destination_path" size="40" />
+    </td>
+  </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>

Modified: erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py (original)
+++ erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py Mon Jun 25 17:22:32 2007
@@ -43,9 +43,10 @@
 from zLOG import LOG
 
 try:
-    from base64 import b64encode, b64decode
+    from base64 import b64encode, b64decode, b16encode, b16decode
 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:
@@ -195,10 +196,10 @@
     file.write('')
     file.close()
     nb_message = 1
-    result = portal_sync.SubSync(subscription.getTitle())
+    result = portal_sync.SubSync(subscription)
     while result['has_response']==1:
       portal_sync.PubSync(publication.getTitle())
-      result = portal_sync.SubSync(subscription.getTitle())
+      result = portal_sync.SubSync(subscription)
       nb_message += 1 + result['has_response']
     return nb_message
 
@@ -226,16 +227,16 @@
     file.write('')
     file.close()
     nb_message = 1
-    result = portal_sync.SubSync(subscription.getTitle())
+    result = portal_sync.SubSync(subscription)
     while result['has_response']==1:
       # We do thing three times, so that we will test
       # if we manage well duplicate messages
       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.getTitle())
-      result = portal_sync.SubSync(subscription.getTitle())
+      result = portal_sync.SubSync(subscription)
+      result = portal_sync.SubSync(subscription)
+      result = portal_sync.SubSync(subscription)
       nb_message += 1 + result['has_response']
     return nb_message
 
@@ -337,9 +338,15 @@
       LOG('Testing... ',0,'test_02_AddPublication')
     portal_id = self.getPortalName()
     portal_sync = self.getSynchronizationTool()
-    portal_sync.manage_addPublication(self.pub_id,self.publication_url, 
-        '/%s/person_server' % portal_id,'objectValues', self.xml_mapping, 
-        'ERP5Conduit','')
+    portal_sync.manage_addPublication(title=self.pub_id,
+        publication_url=self.publication_url, 
+        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)
     self.failUnless(pub is not None)
 
@@ -350,9 +357,17 @@
       LOG('Testing... ',0,'test_03_AddSubscription1')
     portal_id = self.getPortalId()
     portal_sync = self.getSynchronizationTool()
-    portal_sync.manage_addSubscription(self.sub_id1, self.publication_url, 
-        self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues', 
-        self.xml_mapping,'ERP5Conduit','')
+    portal_sync.manage_addSubscription(title=self.sub_id1, 
+        publication_url=self.publication_url,
+        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)
     self.failUnless(sub is not None)
 
@@ -363,9 +378,17 @@
       LOG('Testing... ',0,'test_04_AddSubscription2')
     portal_id = self.getPortalId()
     portal_sync = self.getSynchronizationTool()
-    portal_sync.manage_addSubscription(self.sub_id2,self.publication_url, 
-        self.subscription_url2,'/%s/person_client2' % portal_id,'objectValues', 
-        self.xml_mapping,'ERP5Conduit','')
+    portal_sync.manage_addSubscription(title=self.sub_id2, 
+        publication_url=self.publication_url,
+        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)
     self.failUnless(sub is not None)
 
@@ -530,7 +553,7 @@
     # By default we can just give the id
     portal_sync = self.getSynchronizationTool()
     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.getId()==self.id1)
 
@@ -994,10 +1017,11 @@
     publication = portal_sync.getPublication(self.pub_id)
     self.failUnless(len(publication.getObjectList())==nb_person)
     gid = self.first_name1 +  ' ' + self.last_name1 # ie the title 'Sebastien Robin'
+    gid = b16encode(gid)
     person_c1 = subscription1.getObjectFromGid(gid)
     id_c1 = person_c1.getId()
     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()
     self.failUnless(id_s==self.id1)
     # This will test updating object
@@ -1014,7 +1038,7 @@
     self.checkSynchronizationStateIsSynchronized()
     self.failUnless(len(subscription1.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)
     self.failUnless(person_s.getDescription()==self.description3)
     self.failUnless(person_c1.getDescription()==self.description3)
@@ -1299,9 +1323,17 @@
       LOG('Testing... ',0,'test_32_AddOneWaySubscription')
     portal_id = self.getPortalId()
     portal_sync = self.getSynchronizationTool()
-    portal_sync.manage_addSubscription(self.sub_id1, self.publication_url, 
-        self.subscription_url1, '/%s/person_client1' % portal_id, 
-        'objectValues', '', 'ERP5Conduit', '')
+    portal_sync.manage_addSubscription(title=self.sub_id1, 
+        publication_url=self.publication_url,
+        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)
     self.failUnless(sub is not None)
 




More information about the Erp5-report mailing list