Commit c6b366b2 authored by Sebastien Robin's avatar Sebastien Robin

- add vcard and mobile phone support

- clean some parts


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@15529 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 3a40f0f5
...@@ -84,16 +84,6 @@ class Subscriber(Subscription): ...@@ -84,16 +84,6 @@ class Subscriber(Subscription):
Send ACK for a group of documents Send ACK for a group of documents
""" """
def getConduit(self):
"""
Return the conduit of the publication
"""
#LOG('Subscriber.getConduit, self.getPhysicalPath()',0,self.getPhysicalPath())
#LOG('Subscriber.getConduit, self.getParent().getPhysicalPath()',0,self.aq_parent.getPhysicalPath())
#LOG('Subscriber.getConduit, self.getParent()',0,self.getParent())
return self.aq_parent.getConduit()
#return self.conduit
def SendDocuments(self): def SendDocuments(self):
""" """
We send all the updated documents (ie. documents not marked We send all the updated documents (ie. documents not marked
...@@ -155,7 +145,8 @@ class Publication(Subscription): ...@@ -155,7 +145,8 @@ class Publication(Subscription):
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, source_uri, query, xml_mapping, conduit, gpg_key, id_generator,
gid_generator, media_type, auth_required, authentication_format, gid_generator, media_type, auth_required, authentication_format,
authentication_type, activity_enabled): authentication_type, activity_enabled, synchronize_with_erp5_sites,
sync_content_type):
""" """
constructor constructor
""" """
...@@ -178,6 +169,8 @@ class Publication(Subscription): ...@@ -178,6 +169,8 @@ class Publication(Subscription):
self.auth_required = auth_required self.auth_required = auth_required
self.authentication_format = authentication_format self.authentication_format = authentication_format
self.authentication_type = authentication_type self.authentication_type = authentication_type
self.setSyncContentType(sync_content_type)
self.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
def getPublicationUrl(self): def getPublicationUrl(self):
""" """
...@@ -238,6 +231,7 @@ class Publication(Subscription): ...@@ -238,6 +231,7 @@ class Publication(Subscription):
""" """
Add a new subscriber to the publication Add a new subscriber to the publication
""" """
LOG('addSubscriber starting ...',0,'')
# We have to remove the subscriber if it already exist (there were probably a reset on the client) # We have to remove the subscriber if it already exist (there were probably a reset on the client)
self.delSubscriber(subscriber.getSubscriptionUrl()) self.delSubscriber(subscriber.getSubscriptionUrl())
new_id = subscriber.getId() new_id = subscriber.getId()
......
...@@ -52,7 +52,7 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -52,7 +52,7 @@ class PublicationSynchronization(XMLSyncUtils):
Read the client xml message Read the client xml message
Send the first XML message from the server Send the first XML message from the server
""" """
#LOG('PubSyncInit',0,'Starting... publication: %s' % str(publication)) LOG('PubSyncInit',0,'Starting... publication: %s' % str(publication))
#the session id is set at the same value of those of the client #the session id is set at the same value of those of the client
subscriber.setSessionId(self.getSessionId(xml_client)) subscriber.setSessionId(self.getSessionId(xml_client))
...@@ -72,44 +72,71 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -72,44 +72,71 @@ class PublicationSynchronization(XMLSyncUtils):
alert = self.checkAlert(xml_client) alert = self.checkAlert(xml_client)
alert_code = self.getAlertCode(xml_client) alert_code = self.getAlertCode(xml_client)
cred = self.checkCred(xml_client) cred = self.checkCred(xml_client)
#XXX this is in developement, it's just for tests
if publication.isAuthenticationRequired(): #the source and the target of the subscriber are reversed compared
if not cred: # to those of the publication :
#LOG('PubSyncInit',0,'authentication required') subscriber.setSourceURI(self.getTargetURI(xml_client))
# Prepare the xml message for the Sync initialization package subscriber.setTargetURI(self.getSourceURI(xml_client))
cmd_id = 1 # specifies a SyncML message-unique command identifier
# If slow sync, then resend everything
if alert_code == self.SLOW_SYNC:
LOG('Warning !!!, reseting client synchronization for subscriber:',0,
subscriber)
subscriber.resetAllSignatures()
# Check if the last time synchronization is the same as the client one
mess='\nsubscriber.getNextAnchor:\t%s\nsubscriber.getLastAnchor:\t%s\
\nlast_anchor:\t\t\t%s\nnext_anchor:\t\t\t%s' % \
(subscriber.getNextAnchor(), subscriber.getLastAnchor(), last_anchor, \
next_anchor)
#LOG('PubSyncInit',0,mess)
if subscriber.getNextAnchor() != last_anchor:
if last_anchor in (None, ''):
LOG('PubSyncInit',0,'anchor null')
#raise ValueError, "Sorry, the anchor was null"
else:
message = "bad anchors in PubSyncInit! " + \
subscriber.getNextAnchor() + " and " + last_anchor
LOG('PubSyncInit',0,message)
else:
subscriber.setNextAnchor(next_anchor)
xml_list = [] xml_list = []
xml = xml_list.append xml = xml_list.append
cmd_id = 1 # specifies a SyncML message-unique command identifier
xml('<SyncML>\n') xml('<SyncML>\n')
# syncml header # syncml header
xml(self.SyncMLHeader(subscriber.getSessionId(), xml(self.SyncMLHeader(subscriber.getSessionId(),
subscriber.getMessageId(), subscriber.getSubscriptionUrl(), subscriber.getMessageId(),
subscriber.getSubscriptionUrl(),
publication.getPublicationUrl())) publication.getPublicationUrl()))
# syncml body # syncml body
xml(' <SyncBody>\n') xml(' <SyncBody>\n')
# 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)
self.sendResponse(from_url=publication.getPublicationUrl(),
to_url=subscriber.getSubscriptionUrl(),
sync_id=publication.getTitle(), xml=xml_a, domain=publication)
else:#(if the subscriber begin the session with a cred) -> to be tested if publication.isAuthenticationRequired():
(authentication_format, authentication_type, data) = self.getCred(xml_client)
#at the begining, the code is initialised at UNAUTHORIZED #at the begining, the code is initialised at UNAUTHORIZED
auth_code=self.UNAUTHORIZED auth_code=self.UNAUTHORIZED
LOG('PubSyncInit',0,'authentication required')
if authentication_format == publication.getAuthenticationFormat(): if not cred:
auth_code=self.AUTH_REQUIRED
LOG("there's no credential !!!",0,'')
# Prepare the xml message for the Sync initialization package
xml(self.SyncMLChal(cmd_id, "SyncHdr",
publication.getPublicationUrl(), subscriber.getSubscriptionUrl(),
publication.getAuthenticationFormat(),
publication.getAuthenticationType(), auth_code))
cmd_id += 1
# chal message
xml_status, cmd_id = self.SyncMLStatus(xml_client, auth_code,
cmd_id, next_anchor, subscription=subscriber).values()
xml(xml_status)
else:
(authentication_format, authentication_type, data) = \
self.getCred(xml_client)
if authentication_type == publication.getAuthenticationType(): if authentication_type == publication.getAuthenticationType():
authentication_format = publication.getAuthenticationFormat()
decoded = subscriber.decode(authentication_format, data) decoded = subscriber.decode(authentication_format, data)
if decoded not in ('', None) and ':' in decoded: if decoded not in ('', None) and ':' in decoded:
(login, password) = decoded.split(':') (login, password) = decoded.split(':')
...@@ -131,94 +158,59 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -131,94 +158,59 @@ class PublicationSynchronization(XMLSyncUtils):
# Prepare the xml message for the Sync initialization package # Prepare the xml message for the Sync initialization package
cmd_id = 1 # specifies a SyncML message-unique command identifier
xml_list = []
xml = xml_list.append
xml('<SyncML>\n')
# syncml header
xml(self.SyncMLHeader(subscriber.getSessionId(),
subscriber.getMessageId(),
subscriber.getSubscriptionUrl(),
publication.getPublicationUrl()))
# syncml body
xml(' <SyncBody>\n')
xml(self.SyncMLStatus(cmd_id, subscriber.getSubscriptionUrl(),
publication.getPublicationUrl(), auth_code))
cmd_id += 1
if auth_code == self.AUTH_ACCEPTED: if auth_code == self.AUTH_ACCEPTED:
xml_status, cmd_id = self.SyncMLStatus(xml_client, auth_code,
cmd_id, next_anchor, subscription=subscriber).values()
xml(xml_status)
# alert message # alert message
xml(self.SyncMLAlert(cmd_id, sync_type, xml(self.SyncMLAlert(cmd_id, sync_type, subscriber.getTargetURI(),
subscriber.getSubscriptionUrl(), publication.getPublicationUrl(), subscriber.getSourceURI(), subscriber.getLastAnchor(),
subscriber.getLastAnchor(), subscriber.getNextAnchor())) next_anchor))
cmd_id += 1 cmd_id += 1
else: else:
# chal message # chal message
xml(self.SyncMLChal(cmd_id, "SyncHdr", xml(self.SyncMLChal(cmd_id, "SyncHdr",
publication.getPublicationUrl(), subscriber.getSubscriptionUrl(), publication.getPublicationUrl(), subscriber.getSubscriptionUrl(),
publication.getAuthenticationFormat(), publication.getAuthenticationFormat(),
publication.getAuthenticationType(), self.AUTH_REQUIRED)) publication.getAuthenticationType(), auth_code))
cmd_id += 1 cmd_id += 1
xml(' </SyncBody>\n') xml_status, cmd_id = self.SyncMLStatus(xml_client,
xml('</SyncML>\n') self.AUTH_REQUIRED, cmd_id, next_anchor,
xml_a = ''.join(xml_list) subscription=subscriber).values()
xml(xml_status)
self.sendResponse(from_url=publication.getPublicationUrl(),
to_url=subscriber.getSubscriptionUrl(),
sync_id=publication.getTitle(), xml=xml_a, domain=publication)
else :
# If slow sync, then resend everything
if alert_code == self.SLOW_SYNC:
LOG('Warning !!!, reseting client synchronization for subscriber:',0,
subscriber)
subscriber.resetAllSignatures()
# Check if the last time synchronization is the same as the client one elif alert is not None: #if no identification is required :
mess='\nsubscriber.getNextAnchor:\t%s\nsubscriber.getLastAnchor:\t%s\ # syncml header
\nlast_anchor:\t\t\t%s\nnext_anchor:\t\t\t%s' % (subscriber.getNextAnchor(), subscriber.getLastAnchor(), last_anchor, next_anchor) xml_status, cmd_id = self.SyncMLStatus(xml_client, self.AUTH_ACCEPTED,
#LOG('PubSyncInit',0,mess) cmd_id, next_anchor, subscription=subscriber).values()
xml(xml_status)
# alert message
xml(self.SyncMLAlert(cmd_id, sync_type, subscriber.getTargetURI(),
subscriber.getSourceURI(), subscriber.getLastAnchor(), next_anchor))
cmd_id += 1
if subscriber.getNextAnchor() != last_anchor:
if last_anchor == None:
#LOG('PubSyncInit',0,'anchor null')
raise ValueError, "Sorry, the anchor was null"
else:
message = "bad anchors in PubSyncInit! " + \
subscriber.getNextAnchor() + " and " + last_anchor
#LOG('PubSyncInit',0,message)
else:
subscriber.setNextAnchor(next_anchor)
# We have to set every object as NOT_SYNCHRONIZED # We have to set every object as NOT_SYNCHRONIZED
subscriber.startSynchronization() subscriber.startSynchronization()
else: else:
# We have started the sync from the server (may be for a conflict resolution) # We have started the sync from the server (may be for a conflict
pass # resolution)
raise ValueError, "the syncml message is None. Maybe a synchronisation \
if alert is not None and not publication.isAuthenticationRequired(): has been started from the server (forbiden)"
# Prepare the xml message for the Sync initialization package # a synchronisation is always starded from a client and can't be from
cmd_id = 1 # specifies a SyncML message-unique command identifier # a server !
xml_list = []
xml = xml_list.append
xml('<SyncML>\n') xml(' <Final/>\n')
# syncml header
xml(self.SyncMLHeader(subscriber.getSessionId(),
subscriber.incrementMessageId(), subscriber.getSubscriptionUrl(),
publication.getPublicationUrl()))
# syncml body
xml(' <SyncBody>\n')
# alert message
xml(self.SyncMLAlert(cmd_id, sync_type, subscriber.getSubscriptionUrl(),
publication.getPublicationUrl(), subscriber.getLastAnchor(),
subscriber.getNextAnchor()))
cmd_id += 1
xml(' </SyncBody>\n') xml(' </SyncBody>\n')
xml('</SyncML>\n') xml('</SyncML>\n')
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
if publication.getSyncContentType() == self.CONTENT_TYPE['SYNCML_WBXML']:
xml_a = self.xml2wbxml(xml_a)
self.sendResponse(from_url=publication.getPublicationUrl(), self.sendResponse(from_url=publication.getPublicationUrl(),
to_url=subscriber.getSubscriptionUrl(), sync_id=publication.getTitle(), to_url=subscriber.getSubscriptionUrl(), sync_id=publication.getTitle(),
xml=xml_a, domain=publication) xml=xml_a, domain=publication,
content_type=publication.getSyncContentType())
return {'has_response':1,'xml':xml_a} return {'has_response':1,'xml':xml_a}
...@@ -226,7 +218,7 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -226,7 +218,7 @@ class PublicationSynchronization(XMLSyncUtils):
""" """
This is the synchronization method for the server This is the synchronization method for the server
""" """
#LOG('PubSync',0,'Starting... publication: %s' % str(publication_path)) LOG('PubSync',0,'Starting... publication: %s' % str(publication_path))
# Read the request from the client # Read the request from the client
publication = self.unrestrictedTraverse(publication_path) publication = self.unrestrictedTraverse(publication_path)
xml_client = msg xml_client = msg
...@@ -250,12 +242,13 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -250,12 +242,13 @@ class PublicationSynchronization(XMLSyncUtils):
if client_header.nodeName != "SyncHdr": if client_header.nodeName != "SyncHdr":
#LOG('PubSync',0,'This is not a SyncML Header') #LOG('PubSync',0,'This is not a SyncML Header')
raise ValueError, "Sorry, This is not a SyncML Header" raise ValueError, "Sorry, This is not a SyncML Header"
subscription_url = self.getSourceURI(client_header) subscription_url = self.getSubscriptionUrl(client_header)
# Get the subscriber or create it if not already in the list # Get the subscriber or create it if not already in the list
subscriber = publication.getSubscriber(subscription_url) subscriber = publication.getSubscriber(subscription_url)
if subscriber == None: if subscriber == None:
subscriber = Subscriber(publication.generateNewId(),subscription_url) subscriber = Subscriber(publication.generateNewId(),subscription_url)
subscriber.setXMLMapping(publication.getXMLMapping()) subscriber.setXMLMapping(publication.getXMLMapping())
subscriber.setConduit(publication.getConduit())
publication.addSubscriber(subscriber) publication.addSubscriber(subscriber)
# first synchronization # first synchronization
result = self.PubSyncInit(publication,xml_client,subscriber=subscriber, result = self.PubSyncInit(publication,xml_client,subscriber=subscriber,
...@@ -266,6 +259,8 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -266,6 +259,8 @@ class PublicationSynchronization(XMLSyncUtils):
xml_client=xml_client, subscriber=subscriber, sync_type=alert_code) xml_client=xml_client, subscriber=subscriber, sync_type=alert_code)
else: else:
#we log the user authenticated to do the synchronization with him #we log the user authenticated to do the synchronization with him
if self.checkMap(xml_client) :
self.setRidWithMap(xml_client, subscriber)
if publication.isAuthenticationRequired(): if publication.isAuthenticationRequired():
if subscriber.isAuthenticated(): if subscriber.isAuthenticated():
uf = self.getPortalObject().acl_users uf = self.getPortalObject().acl_users
......
...@@ -575,6 +575,7 @@ def addSubscription( self, id, title='', REQUEST=None ): ...@@ -575,6 +575,7 @@ def addSubscription( self, id, title='', REQUEST=None ):
""" """
Add a new Subscribption Add a new Subscribption
""" """
LOG('addSubscription starting...',0,'')
o = Subscription( id ,'','','','','','') o = Subscription( id ,'','','','','','')
self._setObject( id, o ) self._setObject( id, o )
if REQUEST is not None: if REQUEST is not None:
...@@ -648,7 +649,8 @@ class Subscription(Folder, SyncCode): ...@@ -648,7 +649,8 @@ class Subscription(Folder, SyncCode):
def __init__(self, id, title, publication_url, subscription_url, def __init__(self, id, title, publication_url, subscription_url,
destination_path, source_uri, target_uri, query, xml_mapping, destination_path, source_uri, target_uri, query, xml_mapping,
conduit, gpg_key, id_generator, gid_generator, media_type, login, conduit, gpg_key, id_generator, gid_generator, media_type, login,
password, activity_enabled, alert_code): password, activity_enabled, alert_code, synchronize_with_erp5_sites,
sync_content_type):
""" """
We need to create a dictionnary of We need to create a dictionnary of
signatures of documents which belong to the synchronisation signatures of documents which belong to the synchronisation
...@@ -679,7 +681,8 @@ class Subscription(Folder, SyncCode): ...@@ -679,7 +681,8 @@ class Subscription(Folder, SyncCode):
self.setConduit(conduit) self.setConduit(conduit)
Folder.__init__(self, id) Folder.__init__(self, id)
self.title = title self.title = title
self.setSyncContentType(sync_content_type)
self.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
#self.signatures = PersitentMapping() #self.signatures = PersitentMapping()
def getAlertCodeList(self): def getAlertCodeList(self):
...@@ -727,7 +730,7 @@ class Subscription(Folder, SyncCode): ...@@ -727,7 +730,7 @@ class Subscription(Folder, SyncCode):
def getSourceURI(self): def getSourceURI(self):
""" """
getter for the source_uri (the local path of the subscription) getter for the source_uri (the local path of the subscription data base)
""" """
return getattr(self, 'source_uri', None) return getattr(self, 'source_uri', None)
...@@ -739,11 +742,26 @@ class Subscription(Folder, SyncCode): ...@@ -739,11 +742,26 @@ class Subscription(Folder, SyncCode):
def getTargetURI(self): def getTargetURI(self):
""" """
getter for the target_uri (the distant Publication we want to synchronize getter for the target_uri (the distant Publication data base we want to
with) synchronize with)
""" """
return getattr(self, 'target_uri', None) return getattr(self, 'target_uri', None)
def setSyncContentType(self, sync_content_type):
"""
content type used by the subscriber
"""
self.sync_content_type=sync_content_type
# the varible name is sync_content_type instead of content_type because
# content_type seems to be a function name already used
def getSyncContentType(self):
"""
getter of the subscriber sync_content_type
"""
return getattr(self, 'sync_content_type', 'application/vnd.syncml+xml')
def getSynchronizationType(self, default=None): def getSynchronizationType(self, default=None):
""" """
""" """
...@@ -770,6 +788,21 @@ class Subscription(Folder, SyncCode): ...@@ -770,6 +788,21 @@ class Subscription(Folder, SyncCode):
value = None value = None
self.xml_mapping = value self.xml_mapping = value
def setSynchronizeWithERP5Sites(self, synchronize_with_erp5_sites):
"""
if the synchronisation is made with another ERP5 site,
synchronize_with_erp5_sites is True, False in other case
XXX in the future, the method used to sendHttpResponse will be the same
in all cases, so this method will be useless
"""
self.synchronize_with_erp5_sites = synchronize_with_erp5_sites
def getSynchronizeWithERP5Sites(self):
"""
return True if the synchronisation is between two erp5 sites
"""
return getattr(self, 'synchronize_with_erp5_sites', None)
def checkCorrectRemoteSessionId(self, session_id): def checkCorrectRemoteSessionId(self, session_id):
""" """
We will see if the last session id was the same We will see if the last session id was the same
...@@ -886,7 +919,7 @@ class Subscription(Folder, SyncCode): ...@@ -886,7 +919,7 @@ class Subscription(Folder, SyncCode):
def setPublicationUrl(self, publication_url): def setPublicationUrl(self, publication_url):
""" """
return the publication url set the publication url
""" """
self.publication_url = publication_url self.publication_url = publication_url
...@@ -1058,6 +1091,30 @@ class Subscription(Folder, SyncCode): ...@@ -1058,6 +1091,30 @@ class Subscription(Folder, SyncCode):
break break
return o return o
def getObjectFromRid(self, rid):
"""
return the object corresponding to the id
"""
signature = self.getSignatureFromRid(rid)
destination = self.getDestination()
o = None
if signature is not None and signature.getPath() is not None:
try:
o = destination.getPortalObject().restrictedTraverse(signature.getPath())
except:
pass
return o
# def setOneWaySyncFromServer(self,value):
# """
# If this option is enabled, then we will not
# send our own modifications
# """
# self.one_way_sync_from_server = value
#
def getObjectList(self, **kw): def getObjectList(self, **kw):
""" """
This returns the list of sub-object corresponding This returns the list of sub-object corresponding
......
...@@ -75,7 +75,8 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -75,7 +75,8 @@ class SubscriptionSynchronization(XMLSyncUtils):
self.sendResponse(from_url=subscription.subscription_url, self.sendResponse(from_url=subscription.subscription_url,
to_url=subscription.publication_url, sync_id=subscription.getTitle(), to_url=subscription.publication_url, sync_id=subscription.getTitle(),
xml=xml_a,domain=subscription) xml=xml_a,domain=subscription,
content_type=subscription.getSyncContentType())
return {'has_response':1,'xml':xml_a} return {'has_response':1,'xml':xml_a}
...@@ -86,7 +87,7 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -86,7 +87,7 @@ class SubscriptionSynchronization(XMLSyncUtils):
response = None #check if subsync replies to this messages response = None #check if subsync replies to this messages
subscription = self.unrestrictedTraverse(subscription_path) subscription = self.unrestrictedTraverse(subscription_path)
if msg==None and (subscription.getSubscriptionUrl()).find('file')>=0: if msg==None and (subscription.getSubscriptionUrl()).find('file')>=0:
msg = self.readResponse(sync_id=subscription.getSourceURI(), msg = self.readResponse(sync_id=subscription.getSubscriptionUrl(),
from_url=subscription.getSubscriptionUrl()) from_url=subscription.getSubscriptionUrl())
if msg==None: if msg==None:
response = self.SubSyncInit(subscription) response = self.SubSyncInit(subscription)
...@@ -170,7 +171,8 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -170,7 +171,8 @@ class SubscriptionSynchronization(XMLSyncUtils):
self.sendResponse(from_url=subscription.subscription_url, self.sendResponse(from_url=subscription.subscription_url,
to_url=subscription.publication_url, sync_id=subscription.getTitle(), to_url=subscription.publication_url, sync_id=subscription.getTitle(),
xml=xml_a,domain=subscription) xml=xml_a,domain=subscription,
content_type=subscription.getSyncContentType())
return {'has_response':1,'xml':xml_a} return {'has_response':1,'xml':xml_a}
......
...@@ -37,8 +37,9 @@ class SyncCode(Persistent): ...@@ -37,8 +37,9 @@ class SyncCode(Persistent):
# SyncML Alert Codes # SyncML Alert Codes
TWO_WAY = 200 TWO_WAY = 200
SLOW_SYNC = 201 # This means we get the data from the publication SLOW_SYNC = 201 # This means we get the data from the publication
WAITING_DATA = 214
ONE_WAY_FROM_SERVER = 204 ONE_WAY_FROM_SERVER = 204
WAITING_DATA = 214
REFRESH_REQUIRED = 508
# SyncML Status Codes # SyncML Status Codes
SUCCESS = 200 SUCCESS = 200
...@@ -121,3 +122,9 @@ class SyncCode(Persistent): ...@@ -121,3 +122,9 @@ class SyncCode(Persistent):
MEDIA_TYPE = {} MEDIA_TYPE = {}
MEDIA_TYPE['TEXT_XML'] = 'text/xml' MEDIA_TYPE['TEXT_XML'] = 'text/xml'
MEDIA_TYPE['TEXT_VCARD'] = 'text/vcard' MEDIA_TYPE['TEXT_VCARD'] = 'text/vcard'
MEDIA_TYPE['TEXT_XVCARD'] = 'text/x-vcard'
#content types :
CONTENT_TYPE = {}
CONTENT_TYPE['SYNCML_XML'] = 'application/vnd.syncml+xml'
CONTENT_TYPE['SYNCML_WBXML'] = 'application/vnd.syncml+wbxml'
...@@ -176,10 +176,13 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -176,10 +176,13 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'manage_addPublication') 'manage_addPublication')
def manage_addPublication(self, title, publication_url, def manage_addPublication(self, title, publication_url,
destination_path, source_uri, query, xml_mapping, conduit, gpg_key, destination_path, source_uri, query, xml_mapping,
conduit, gpg_key,
synchronization_id_generator=None, gid_generator=None, synchronization_id_generator=None, gid_generator=None,
media_type=None, auth_required=0, authentication_format='', media_type=None, auth_required=0, authentication_format='',
authentication_type='', RESPONSE=None, activity_enabled = False): authentication_type='', RESPONSE=None, activity_enabled = False,
sync_content_type='application/vnd.syncml+xml',
synchronize_with_erp5_sites=True):
""" """
create a new publication create a new publication
""" """
...@@ -192,7 +195,9 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -192,7 +195,9 @@ class SynchronizationTool( SubscriptionSynchronization,
destination_path, source_uri, query, xml_mapping, destination_path, source_uri, query, xml_mapping,
conduit, gpg_key, synchronization_id_generator, conduit, gpg_key, synchronization_id_generator,
gid_generator, media_type, auth_required, gid_generator, media_type, auth_required,
authentication_format, authentication_type, activity_enabled) authentication_format, authentication_type,
activity_enabled, synchronize_with_erp5_sites,
sync_content_type)
folder._setObject( new_id, pub ) folder._setObject( new_id, pub )
#if len(self.list_publications) == 0: #if len(self.list_publications) == 0:
# self.list_publications = PersistentMapping() # self.list_publications = PersistentMapping()
...@@ -207,7 +212,10 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -207,7 +212,10 @@ class SynchronizationTool( SubscriptionSynchronization,
xml_mapping, conduit, gpg_key, xml_mapping, conduit, gpg_key,
synchronization_id_generator=None, gid_generator=None, synchronization_id_generator=None, gid_generator=None,
media_type=None, login=None, password=None, media_type=None, login=None, password=None,
RESPONSE=None, activity_enabled=False, alert_code=SyncCode.TWO_WAY): RESPONSE=None, activity_enabled=False,
alert_code=SyncCode.TWO_WAY,
synchronize_with_erp5_sites = True,
sync_content_type='application/vnd.syncml+xml'):
""" """
XXX should be renamed as addSubscription XXX should be renamed as addSubscription
create a new subscription create a new subscription
...@@ -221,7 +229,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -221,7 +229,8 @@ class SynchronizationTool( SubscriptionSynchronization,
destination_path, source_uri, target_uri, query, destination_path, source_uri, target_uri, query,
xml_mapping, conduit, gpg_key, xml_mapping, conduit, gpg_key,
synchronization_id_generator, gid_generator, media_type, synchronization_id_generator, gid_generator, media_type,
login, password, activity_enabled, alert_code) login, password, activity_enabled, alert_code,
synchronize_with_erp5_sites, sync_content_type)
folder._setObject( new_id, sub ) folder._setObject( new_id, sub )
#if len(self.list_subscriptions) == 0: #if len(self.list_subscriptions) == 0:
# self.list_subscriptions = PersistentMapping() # self.list_subscriptions = PersistentMapping()
...@@ -236,7 +245,9 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -236,7 +245,9 @@ class SynchronizationTool( SubscriptionSynchronization,
conduit, gpg_key, synchronization_id_generator, conduit, gpg_key, synchronization_id_generator,
gid_generator, media_type=None, auth_required=0, gid_generator, media_type=None, auth_required=0,
authentication_format='', authentication_type='', authentication_format='', authentication_type='',
RESPONSE=None, activity_enabled=False): RESPONSE=None, activity_enabled=False,
sync_content_type='application/vnd.syncml+xml',
synchronize_with_erp5_sites=False):
""" """
modify a publication modify a publication
""" """
...@@ -256,6 +267,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -256,6 +267,8 @@ class SynchronizationTool( SubscriptionSynchronization,
pub.setAuthentication(auth_required) pub.setAuthentication(auth_required)
pub.setAuthenticationFormat(authentication_format) pub.setAuthenticationFormat(authentication_format)
pub.setAuthenticationType(authentication_type) pub.setAuthenticationType(authentication_type)
pub.setSyncContentType(sync_content_type)
pub.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('managePublications') RESPONSE.redirect('managePublications')
...@@ -265,7 +278,9 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -265,7 +278,9 @@ class SynchronizationTool( SubscriptionSynchronization,
def manage_editSubscription(self, title, publication_url, subscription_url, def manage_editSubscription(self, title, publication_url, subscription_url,
destination_path, source_uri, target_uri, query, xml_mapping, conduit, destination_path, source_uri, target_uri, query, xml_mapping, conduit,
gpg_key, synchronization_id_generator, gid_generator, media_type=None, gpg_key, synchronization_id_generator, gid_generator, media_type=None,
login='', password='', RESPONSE=None, activity_enabled=False, alert_code=SyncCode.TWO_WAY): login='', password='', RESPONSE=None, activity_enabled=False,
alert_code=SyncCode.TWO_WAY, synchronize_with_erp5_sites=False,
sync_content_type='application/vnd.syncml+xml'):
""" """
modify a subscription modify a subscription
""" """
...@@ -286,6 +301,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -286,6 +301,8 @@ class SynchronizationTool( SubscriptionSynchronization,
sub.setMediaType(media_type) sub.setMediaType(media_type)
sub.setLogin(login) sub.setLogin(login)
sub.setPassword(password) sub.setPassword(password)
sub.setSyncContentType(sync_content_type)
sub.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
sub.setAlertCode(alert_code) sub.setAlertCode(alert_code)
if RESPONSE is not None: if RESPONSE is not None:
...@@ -833,7 +850,7 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -833,7 +850,7 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declarePublic('sendResponse') security.declarePublic('sendResponse')
def sendResponse(self, to_url=None, from_url=None, sync_id=None,xml=None, def sendResponse(self, to_url=None, from_url=None, sync_id=None,xml=None,
domain=None, send=1): domain=None, send=1, content_type='application/vnd.syncml+xml'):
""" """
We will look at the url and we will see if we need to send mail, http We will look at the url and we will see if we need to send mail, http
response, or just copy to a file. response, or just copy to a file.
...@@ -843,6 +860,12 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -843,6 +860,12 @@ class SynchronizationTool( SubscriptionSynchronization,
#LOG('sendResponse, from_url: ',0,from_url) #LOG('sendResponse, from_url: ',0,from_url)
#LOG('sendResponse, sync_id: ',0,sync_id) #LOG('sendResponse, sync_id: ',0,sync_id)
#LOG('sendResponse, xml: \n',0,xml) #LOG('sendResponse, xml: \n',0,xml)
if content_type == self.CONTENT_TYPE['SYNCML_WBXML']:
xml = self.xml2wbxml(xml)
#LOG('sendHttpResponse, xml after wbxml: \n',0,self.hexdump(xml))
if isinstance(xml, unicode): if isinstance(xml, unicode):
xml = xml.encode('utf-8') xml = xml.encode('utf-8')
if domain is not None: if domain is not None:
...@@ -878,7 +901,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -878,7 +901,8 @@ class SynchronizationTool( SubscriptionSynchronization,
self.activate(activity='RAMQueue').sendHttpResponse(sync_id=sync_id, self.activate(activity='RAMQueue').sendHttpResponse(sync_id=sync_id,
to_url=to_url, to_url=to_url,
xml=xml, xml=xml,
domain_path=domain.getPath()) domain_path=domain.getPath(),
content_type=content_type)
elif to_url.find('file://')==0: elif to_url.find('file://')==0:
filename = to_url[len('file:/'):] filename = to_url[len('file:/'):]
stream = file(filename,'w') stream = file(filename,'w')
...@@ -895,10 +919,11 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -895,10 +919,11 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declarePrivate('sendHttpResponse') security.declarePrivate('sendHttpResponse')
def sendHttpResponse(self, to_url=None, sync_id=None, xml=None, def sendHttpResponse(self, to_url=None, sync_id=None, xml=None,
domain_path=None ): domain_path=None, content_type='application/vnd.syncml+xml'):
domain = self.unrestrictedTraverse(domain_path) domain = self.unrestrictedTraverse(domain_path)
#LOG('sendHttpResponse, self.getPhysicalPath: ',0,self.getPhysicalPath()) #LOG('sendHttpResponse, self.getPhysicalPath: ',0,self.getPhysicalPath())
#LOG('sendHttpResponse, starting with domain:',0,domain) #LOG('sendHttpResponse, starting with domain:',0,domain)
#LOG('sendHttpResponse, xml:',0,xml) #LOG('sendHttpResponse, xml:',0,xml)
if domain is not None: if domain is not None:
if domain.domain_type == self.PUB and not domain.getActivityEnabled(): if domain.domain_type == self.PUB and not domain.getActivityEnabled():
...@@ -920,27 +945,46 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -920,27 +945,46 @@ class SynchronizationTool( SubscriptionSynchronization,
urllib2.install_opener(opener) urllib2.install_opener(opener)
to_encode = {} to_encode = {}
head = '<?xml version="1.0" encoding="UTF-8"?>' head = '<?xml version="1.0" encoding="UTF-8"?>'
if content_type == self.CONTENT_TYPE['SYNCML_WBXML']:
#because xml2wbxml add the head to the xml
to_encode['text'] = xml
else:
to_encode['text'] = head + xml to_encode['text'] = head + xml
to_encode['sync_id'] = sync_id to_encode['sync_id'] = sync_id
headers = {'Content-type': 'application/vnd.syncml+xml'} headers = {'User-Agent':'ERP5SyncML', 'Content-Type':content_type}
#XXX bad hack for synchronization with erp5 #XXX bad hack for synchronization with erp5
# because at this time, when we call the readResponse method, we must
# encode the data with urlencode if we want the readResponse method to
# receive the data's in parameters.
# All this should be improved to not use urlencode in all cases.
# to do this, perhaps use SOAP :
# - http://en.wikipedia.org/wiki/SOAP
# - http://www.contentmanagementsoftware.info/zope/SOAPSupport
# - http://svn.zope.org/soap/trunk/
if domain.getSynchronizeWithERP5Sites():
LOG('Synchronization with another ERP5 instance ...',0,'')
if to_url.find('readResponse')<0: if to_url.find('readResponse')<0:
to_url = to_url + '/portal_synchronizations/readResponse' to_url = to_url + '/portal_synchronizations/readResponse'
encoded = urllib.urlencode(to_encode) encoded = urllib.urlencode(to_encode)
data=encoded data=encoded
request = urllib2.Request(url=to_url, data=data) request = urllib2.Request(url=to_url, data=data)
else:
#XXX only to synchronize with other server than erp5 (must be improved):
data=head+xml
request = urllib2.Request(to_url, data, headers)
#XXX only to synchronize with other server than erp5 (must be improved):
# data=head+xml
# request = urllib2.Request(to_url, data, headers)
try: try:
result = urllib2.urlopen(request).read() url_file = urllib2.urlopen(request)
result = url_file.read()
except socket.error, msg: except socket.error, msg:
self.activate(activity='RAMQueue').sendHttpResponse(to_url=to_url, self.activate(activity='RAMQueue').sendHttpResponse(to_url=to_url,
sync_id=sync_id, xml=xml, domain_path=domain.getPath()) sync_id=sync_id, xml=xml, domain_path=domain.getPath(),
content_type=content_type)
LOG('sendHttpResponse, socket ERROR:',0,msg) LOG('sendHttpResponse, socket ERROR:',0,msg)
LOG('sendHttpResponse, url,data',0,(url, data)) #LOG('sendHttpResponse, url,data',0,(url, data))
return return
except urllib2.URLError, msg: except urllib2.URLError, msg:
LOG("sendHttpResponse, can't open url %s :" % to_url, 0, msg) LOG("sendHttpResponse, can't open url %s :" % to_url, 0, msg)
...@@ -961,7 +1005,9 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -961,7 +1005,9 @@ class SynchronizationTool( SubscriptionSynchronization,
#user = uf.getUserById('syncml').__of__(uf) #user = uf.getUserById('syncml').__of__(uf)
#newSecurityManager(None, user) #newSecurityManager(None, user)
#self.activate(activity='RAMQueue').readResponse(sync_id=sync_id,text=result) #self.activate(activity='RAMQueue').readResponse(sync_id=sync_id,text=result)
self.readResponse(sync_id=sync_id,text=result) self.readResponse(sync_id=sync_id,text=result)
return result
security.declarePublic('sync') security.declarePublic('sync')
def sync(self): def sync(self):
...@@ -986,6 +1032,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -986,6 +1032,8 @@ class SynchronizationTool( SubscriptionSynchronization,
response, or just copy to a file. response, or just copy to a file.
""" """
#LOG('readResponse, text :', 0, text) #LOG('readResponse, text :', 0, text)
#LOG('readResponse, text :', 0, self.hexdump(text))
# Login as a manager to make sure we can create objects # Login as a manager to make sure we can create objects
uf = self.acl_users uf = self.acl_users
user = uf.getUserById('syncml').__of__(uf) user = uf.getUserById('syncml').__of__(uf)
...@@ -1001,10 +1049,12 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -1001,10 +1049,12 @@ class SynchronizationTool( SubscriptionSynchronization,
for publication in self.getPublicationList(): for publication in self.getPublicationList():
if publication.getTitle()==sync_id: if publication.getTitle()==sync_id:
gpg_key = publication.getGPGKey() gpg_key = publication.getGPGKey()
domain = publication
if gpg_key == '': if gpg_key == '':
for subscription in self.getSubscriptionList(): for subscription in self.getSubscriptionList():
if subscription.getTitle()==sync_id: if subscription.getTitle()==sync_id:
gpg_key = subscription.getGPGKey() gpg_key = subscription.getGPGKey()
domain = subscription
# decrypt the message if needed # decrypt the message if needed
if gpg_key not in (None,''): if gpg_key not in (None,''):
filename = str(random.randrange(1,2147483600)) + '.txt' filename = str(random.randrange(1,2147483600)) + '.txt'
...@@ -1025,8 +1075,10 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -1025,8 +1075,10 @@ class SynchronizationTool( SubscriptionSynchronization,
# Get the target and then find the corresponding publication or # Get the target and then find the corresponding publication or
# Subscription # Subscription
#LOG('type(text) : ',0,type(text)) #LOG('type(text) : ',0,type(text))
if domain.getSyncContentType() == self.CONTENT_TYPE['SYNCML_WBXML']:
text = self.wbxml2xml(text)
#LOG('readResponse, text after wbxml :\n', 0, text)
xml = Parse(text) xml = Parse(text)
#XXX this function is not very optimized and should be improved
url = self.getTarget(xml) url = self.getTarget(xml)
for publication in self.getPublicationList(): for publication in self.getPublicationList():
if publication.getPublicationUrl()==url and \ if publication.getPublicationUrl()==url and \
...@@ -1042,6 +1094,8 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -1042,6 +1094,8 @@ class SynchronizationTool( SubscriptionSynchronization,
xml = result['xml'] xml = result['xml']
#must be commented because this method is alredy called #must be commented because this method is alredy called
#xml = self.sendResponse(xml=xml,domain=publication,send=0) #xml = self.sendResponse(xml=xml,domain=publication,send=0)
if publication.getSyncContentType() == self.CONTENT_TYPE['SYNCML_WBXML']:
xml = self.xml2wbxml(xml)
return xml return xml
for subscription in self.getSubscriptionList(): for subscription in self.getSubscriptionList():
...@@ -1096,4 +1150,41 @@ class SynchronizationTool( SubscriptionSynchronization, ...@@ -1096,4 +1150,41 @@ class SynchronizationTool( SubscriptionSynchronization,
conduit_object = getattr(conduit_module, conduit)() conduit_object = getattr(conduit_module, conduit)()
return conduit_object.addNode(**kw) return conduit_object.addNode(**kw)
def hexdump(self, raw=''):
"""
this function is used to display the raw in a readable format :
it display raw in hexadecimal format and display too the printable
characters (because if not printable characters are printed, it makes
terminal display crash)
"""
buf = ""
line = ""
start = 0
done = False
while not done:
end = start + 16
max = len(str(raw))
if end > max:
end = max
done = True
chunk = raw[start:end]
for i in xrange(len(chunk)):
if i > 0:
spacing = " "
else:
spacing = ""
buf += "%s%02x" % (spacing, ord(chunk[i]))
if done:
for i in xrange(16 - (end % 16)):
buf += " "
buf += " "
for c in chunk:
val = ord(c)
if val >= 33 and val <= 126:
buf += c
else:
buf += "."
buf += "\n"
start += 16
return buf
InitializeClass( SynchronizationTool ) InitializeClass( SynchronizationTool )
...@@ -112,7 +112,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -112,7 +112,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <LocURI>%s</LocURI>\n' % source) xml(' <LocURI>%s</LocURI>\n' % source)
xml(' </Source>\n') xml(' </Source>\n')
xml(' <Meta>\n') xml(' <Meta>\n')
xml(' <Anchor xmlns=\'syncml:metinf\'>\n') xml(' <Anchor>\n')
xml(' <Last>%s</Last>\n' % last_anchor) xml(' <Last>%s</Last>\n' % last_anchor)
xml(' <Next>%s</Next>\n' % next_anchor) xml(' <Next>%s</Next>\n' % next_anchor)
xml(' </Anchor>\n') xml(' </Anchor>\n')
...@@ -122,30 +122,87 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -122,30 +122,87 @@ class XMLSyncUtilsMixin(SyncCode):
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
return xml_a return xml_a
def SyncMLStatus(self, cmd_id, target_ref, source_ref, sync_code, def SyncMLStatus(self, remote_xml, data_code, cmd_id, next_anchor,
next_anchor=None): subscription=None):
""" """
Since the Status section is always almost the same, this is the return a status bloc with all status corresponding to the syncml
way to set one quickly. commands in remote_xml
""" """
#list of element in the SyncBody bloc
syncbody_element_list = remote_xml.xpath('//SyncBody/*')
message_id = self.getMessageId(remote_xml)
xml_list = [] xml_list = []
xml = xml_list.append xml = xml_list.append
if data_code != self.AUTH_REQUIRED:#because for AUTH_REQUIRED, SyncMLChal is #called
# status for SyncHdr
message_id = self.getMessageId(remote_xml)
xml(' <Status>\n') xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id) xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <TargetRef>%s</TargetRef>\n' % target_ref) cmd_id += 1
xml(' <SourceRef>%s</SourceRef>\n' % source_ref) xml(' <MsgRef>%s</MsgRef>\n' % self.getMessageId(remote_xml))
xml(' <Data>%s</Data>\n' % sync_code) xml(' <CmdRef>0</CmdRef>\n') #to make reference to the SyncHdr, it's 0
if next_anchor is not None: 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'))
if isinstance(data_code, int):
data_code = str(data_code)
xml(' <Data>%s</Data>\n' % data_code)
xml(' </Status>\n')
#add the status bloc corresponding to the receive command
for syncbody_element in syncbody_element_list:
#LOG('SyncMLStatus : ',0,"command:%s, subscription:%s" % (str(syncbody_element.nodeName), subscription))
if str(syncbody_element.nodeName) not in ('Status', 'Final', 'Get'):
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' % str(self.ITEM_ADDED))
elif syncbody_element.nodeName.encode('utf-8') == 'Alert' and \
syncbody_element.xpath('string(.//Data)').encode('utf-8') == \
str(self.SLOW_SYNC):
xml(' <Data>%s</Data>\n' % str(self.REFRESH_REQUIRED))
else:
xml(' <Data>%s</Data>\n' % str(self.SUCCESS))
# if syncbody_element.xpath('.//Item') not in ([], None, '') and\
# syncbody_element.xpath('.//Item.....'): #contient une ancre Next...
if str(syncbody_element.nodeName) == 'Alert':
xml(' <Item>\n') xml(' <Item>\n')
xml(' <Data>\n') xml(' <Data>\n')
xml(' <Anchor xmlns=\'syncml:metinf\'>\n') xml(' <Anchor>\n')
xml(' <Next>%s</Next>\n' % next_anchor) xml(' <Next>%s</Next>\n' % next_anchor)
xml(' </Anchor>\n') xml(' </Anchor>\n')
xml(' </Data>\n') xml(' </Data>\n')
xml(' </Item>\n') xml(' </Item>\n')
xml(' </Status>\n') xml(' </Status>\n')
if str(syncbody_element.nodeName) == 'Get' and subscription != None:
cmd_ref = syncbody_element.xpath('string(.//CmdID)').encode('utf-8')
syncml_result = self.SyncMLPut(cmd_id, subscription, markup='Results',
cmd_ref=cmd_ref, message_id=self.getMessageId(remote_xml))
xml(syncml_result)
cmd_id += 1
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
return xml_a return {'xml':xml_a, 'cmd_id':cmd_id}
def SyncMLConfirmation(self, cmd_id=None, target_ref=None, cmd=None, def SyncMLConfirmation(self, cmd_id=None, target_ref=None, cmd=None,
sync_code=None, msg_ref=None, cmd_ref=None, source_ref=None, sync_code=None, msg_ref=None, cmd_ref=None, source_ref=None,
...@@ -191,6 +248,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -191,6 +248,8 @@ class XMLSyncUtilsMixin(SyncCode):
xml = xml_list.append xml = xml_list.append
xml(' <Status>\n') xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id) xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <MsgRef>1</MsgRef>\n')
xml(' <CmdRef>0</CmdRef>\n')
xml(' <Cmd>%s</Cmd>\n' % cmd) xml(' <Cmd>%s</Cmd>\n' % cmd)
xml(' <TargetRef>%s</TargetRef>\n' % target_ref) xml(' <TargetRef>%s</TargetRef>\n' % target_ref)
xml(' <SourceRef>%s</SourceRef>\n' % source_ref) xml(' <SourceRef>%s</SourceRef>\n' % source_ref)
...@@ -205,9 +264,12 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -205,9 +264,12 @@ class XMLSyncUtilsMixin(SyncCode):
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
return xml_a return xml_a
def SyncMLPut(self, cmd_id, subscription): def SyncMLPut(self, cmd_id, subscription, markup='Put', cmd_ref=None,
message_id=None):
""" """
this is used to inform the server of the CTType version supported this is used to inform the server of the CTType version supported
but if the server use it to respond to a Get request, it's a <Result> markup
instead of <Put>
""" """
from Products.ERP5SyncML import Conduit from Products.ERP5SyncML import Conduit
# Import the conduit and get it # Import the conduit and get it
...@@ -222,14 +284,17 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -222,14 +284,17 @@ class XMLSyncUtilsMixin(SyncCode):
globals(), locals(), ['']) globals(), locals(), [''])
conduit = getattr(conduit_module, conduit_name)() conduit = getattr(conduit_module, conduit_name)()
#if the conduit support the SyncMLPut : #if the conduit support the SyncMLPut :
if hasattr(conduit, 'getCapabilitiesCTType') and \ if hasattr(conduit, 'getCapabilitiesCTTypeList') and \
hasattr(conduit, 'getCapabilitiesVerCTList') and \ hasattr(conduit, 'getCapabilitiesVerCTList') and \
hasattr(conduit, 'getPreferedCapabilitieVerCT'): hasattr(conduit, 'getPreferedCapabilitieVerCT'):
xml_list = [] xml_list = []
xml = xml_list.append xml = xml_list.append
if conduit.getCapabilitiesVerCTList() not in ([], None): xml(' <%s>\n' % markup)
xml(' <Put>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id) xml(' <CmdID>%s</CmdID>\n' % cmd_id)
if message_id not in (None, ''):
xml(' <MsgRef>%s</MsgRef>\n' % message_id)
if cmd_ref not in (None, '') :
xml(' <CmdRef>%s</CmdRef>\n' % cmd_ref)
xml(' <Meta>\n') xml(' <Meta>\n')
xml(' <Type>application/vnd.syncml-devinf+xml</Type>\n'); xml(' <Type>application/vnd.syncml-devinf+xml</Type>\n');
xml(' </Meta>\n') xml(' </Meta>\n')
...@@ -240,7 +305,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -240,7 +305,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <Data>\n') xml(' <Data>\n')
xml(' <DevInf>\n') xml(' <DevInf>\n')
xml(' <VerDTD>1.1</VerDTD>\n') xml(' <VerDTD>1.1</VerDTD>\n')
xml(' <Man>Fabien MORIN</Man>\n') xml(' <Man>Nexedi</Man>\n')
xml(' <Mod>ERP5SyncML</Mod>\n') xml(' <Mod>ERP5SyncML</Mod>\n')
xml(' <OEM>Open Source</OEM>\n') xml(' <OEM>Open Source</OEM>\n')
xml(' <SwV>0.1</SwV>\n') xml(' <SwV>0.1</SwV>\n')
...@@ -250,23 +315,31 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -250,23 +315,31 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <DataStore>\n') xml(' <DataStore>\n')
xml(' <SourceRef>%s</SourceRef>\n' % subscription.getSourceURI()) xml(' <SourceRef>%s</SourceRef>\n' % subscription.getSourceURI())
xml(' <Rx-Pref>\n') xml(' <Rx-Pref>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType()) xml(' <CTType>%s</CTType>\n' % \
xml(' <VerCT>%s</VerCT>\n' % conduit.getPreferedCapabilitieVerCT()) conduit.getPreferedCapabilitieCTType())
xml(' <VerCT>%s</VerCT>\n' % \
conduit.getPreferedCapabilitieVerCT())
xml(' </Rx-Pref>\n') xml(' </Rx-Pref>\n')
for rx_version in conduit.getCapabilitiesVerCTList(): for type in conduit.getCapabilitiesCTTypeList():
if type != self.MEDIA_TYPE['TEXT_XML']:
for rx_version in conduit.getCapabilitiesVerCTList(type):
xml(' <Rx>\n') xml(' <Rx>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType()) xml(' <CTType>%s</CTType>\n' % type)
xml(' <VerCT>%s</VerCT>\n' % rx_version) xml(' <VerCT>%s</VerCT>\n' % rx_version)
xml(' </Rx>\n') xml(' </Rx>\n')
xml(' <Tx-Pref>\n') xml(' <Tx-Pref>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType()) xml(' <CTType>%s</CTType>\n' % \
xml(' <VerCT>%s</VerCT>\n' % conduit.getPreferedCapabilitieVerCT()) conduit.getPreferedCapabilitieCTType())
xml(' <VerCT>%s</VerCT>\n' % \
conduit.getPreferedCapabilitieVerCT())
xml(' </Tx-Pref>\n') xml(' </Tx-Pref>\n')
for tx_version in conduit.getCapabilitiesVerCTList(): for type in conduit.getCapabilitiesCTTypeList():
if type != self.MEDIA_TYPE['TEXT_XML']:
for tx_version in conduit.getCapabilitiesVerCTList(type):
xml(' <Tx>\n') xml(' <Tx>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType()) xml(' <CTType>%s</CTType>\n' % type)
xml(' <VerCT>%s</VerCT>\n' % rx_version) xml(' <VerCT>%s</VerCT>\n' % tx_version)
xml(' </Tx>\n') xml(' </Tx>\n')
xml(' <SyncCap>\n') xml(' <SyncCap>\n')
...@@ -280,7 +353,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -280,7 +353,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' </DevInf>\n') xml(' </DevInf>\n')
xml(' </Data>\n') xml(' </Data>\n')
xml(' </Item>\n') xml(' </Item>\n')
xml(' </Put>\n') xml(' </%s>\n' % markup)
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
return xml_a return xml_a
return '' return ''
...@@ -333,7 +406,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -333,7 +406,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml_a = ''.join(xml_list) xml_a = ''.join(xml_list)
return xml_a return xml_a
def deleteXMLObject(self, cmd_id=0, object_gid=None, xml_object=''): def deleteXMLObject(self, cmd_id=0, object_gid=None, rid=None, xml_object=''):
""" """
Delete an object with the SyncML protocol Delete an object with the SyncML protocol
""" """
...@@ -342,6 +415,11 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -342,6 +415,11 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <Delete>\n') xml(' <Delete>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id) xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <Item>\n') xml(' <Item>\n')
if rid not in (None, ''):
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % rid)
xml(' </Target>\n')
else:
xml(' <Source>\n') xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % object_gid) xml(' <LocURI>%s</LocURI>\n' % object_gid)
xml(' </Source>\n') xml(' </Source>\n')
...@@ -353,7 +431,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -353,7 +431,7 @@ class XMLSyncUtilsMixin(SyncCode):
return xml_a return xml_a
def replaceXMLObject(self, cmd_id=0, object=None, xml_string=None, def replaceXMLObject(self, cmd_id=0, object=None, xml_string=None,
more_data=0, gid=None, media_type=None): more_data=0, gid=None, rid=None, media_type=None):
""" """
Replace an object with the SyncML protocol Replace an object with the SyncML protocol
""" """
...@@ -365,6 +443,11 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -365,6 +443,11 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <Type>%s</Type>\n' % media_type) xml(' <Type>%s</Type>\n' % media_type)
xml(' </Meta>\n') xml(' </Meta>\n')
xml(' <Item>\n') xml(' <Item>\n')
if rid is not None:
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % str(rid))
xml(' </Target>\n')
else:
xml(' <Source>\n') xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % str(gid)) xml(' <LocURI>%s</LocURI>\n' % str(gid))
xml(' </Source>\n') xml(' </Source>\n')
...@@ -429,11 +512,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -429,11 +512,7 @@ class XMLSyncUtilsMixin(SyncCode):
Return the value of the last anchor, in the Return the value of the last anchor, in the
alert section of the xml_stream alert section of the xml_stream
""" """
first_node = xml_stream.childNodes[0] last_anchor = xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Last)')
# Get informations from the body
client_body = first_node.childNodes[3]
last_anchor = client_body.xpath('string(/Alert/Item/Meta/Anchor/Last)')
last_anchor = last_anchor.encode('utf-8') last_anchor = last_anchor.encode('utf-8')
return last_anchor return last_anchor
...@@ -442,17 +521,29 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -442,17 +521,29 @@ class XMLSyncUtilsMixin(SyncCode):
Return the value of the next anchor, in the Return the value of the next anchor, in the
alert section of the xml_stream alert section of the xml_stream
""" """
first_node = xml_stream.childNodes[0] next_anchor = xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Next)')
if first_node.nodeName != "SyncML":
print "This is not a SyncML message"
# Get informations from the body
client_body = first_node.childNodes[3]
next_anchor = client_body.xpath('string(/Alert/Item/Meta/Anchor/Next)')
next_anchor = next_anchor.encode('utf-8') next_anchor = next_anchor.encode('utf-8')
return next_anchor return next_anchor
def getSourceURI(self, xml): def getSourceURI(self, xml):
"""
return the source uri of the data base
"""
source_uri = xml.xpath('string(//SyncBody/Alert/Item/Source/LocURI)')
if isinstance(source_uri, unicode):
source_uri = source_uri.encode('utf-8')
return source_uri
def getTargetURI(self, xml):
"""
return the target uri of the data base
"""
target_uri = xml.xpath('string(//SyncBody/Alert/Item/Target/LocURI)')
if isinstance(target_uri, unicode):
target_uri = target_uri.encode('utf-8')
return target_uri
def getSubscriptionUrl(self, xml):
""" """
return the source URI of the syncml header return the source URI of the syncml header
""" """
...@@ -541,6 +632,27 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -541,6 +632,27 @@ class XMLSyncUtilsMixin(SyncCode):
return True return True
return False return False
def checkMap(self, xml_stream):
"""
Check if there's a Map section in the xml_stream
"""
if xml_stream.xpath('string(SyncML/SyncBody/Map)') \
not in ('', None, []):
return True
return False
def setRidWithMap(self, xml_stream, subscriber):
"""
get all the local objects of the given target id and set them the rid with
the given source id (in the Map section)
"""
item_list = xml_stream.xpath('SyncML/SyncBody/Map/MapItem')
for map_item in item_list:
gid = map_item.xpath('string(.//Target/LocURI)').encode('utf-8')
signature = subscriber.getSignatureFromGid(gid)
rid = map_item.xpath('string(.//Source/LocURI)').encode('utf-8')
signature.setRid(rid)
def getAlertCode(self, xml_stream): def getAlertCode(self, xml_stream):
""" """
Return the value of the alert code inside the full syncml message Return the value of the alert code inside the full syncml message
...@@ -688,7 +800,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -688,7 +800,7 @@ class XMLSyncUtilsMixin(SyncCode):
if object is not None, this usually means we want to set the if object is not None, this usually means we want to set the
actual xupdate on the signature. actual xupdate on the signature.
""" """
#LOG('\ngetSyncMLData starting...',0, domain.getId()) LOG('getSyncMLData starting...',0,'')
if isinstance(conduit, str): if isinstance(conduit, str):
conduit = self.getConduitByName(conduit) conduit = self.getConduitByName(conduit)
local_gid_list = [] local_gid_list = []
...@@ -714,10 +826,10 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -714,10 +826,10 @@ class XMLSyncUtilsMixin(SyncCode):
if xml_object is not None: # This prevent to delete an object that if xml_object is not None: # This prevent to delete an object that
# we were not able to create # we were not able to create
rid = signature.getRid() rid = signature.getRid()
if rid != None: #if rid != None:
object_gid=rid #to use the remote id if it exist # object_gid=rid #to use the remote id if it exist
syncml_data += self.deleteXMLObject(xml_object=signature.getXML()\ syncml_data += self.deleteXMLObject(xml_object=signature.getXML()\
or '', object_gid=object_gid,cmd_id=cmd_id) or '', object_gid=object_gid, rid=rid,cmd_id=cmd_id)
cmd_id += 1 cmd_id += 1
#delete Signature if object does not exist anymore #delete Signature if object does not exist anymore
for known_gid in subscriber.getGidList(): for known_gid in subscriber.getGidList():
...@@ -726,7 +838,6 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -726,7 +838,6 @@ class XMLSyncUtilsMixin(SyncCode):
local_gid_list = [] local_gid_list = []
loop = 0 loop = 0
for object_path in subscriber.getRemainingObjectPathList(): for object_path in subscriber.getRemainingObjectPathList():
#LOG('getRemainingObject :',0,[[subscriber.getRemainingObjectPathList()[i][3] for i in range(5)],[subscriber.getRemainingObjectPathList()[-i][3] for i in range(5)]])
if max is not None and loop >= max: if max is not None and loop >= max:
result['finished'] = 0 result['finished'] = 0
break break
...@@ -806,9 +917,12 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -806,9 +917,12 @@ class XMLSyncUtilsMixin(SyncCode):
if not signature.checkMD5(xml_object): if not signature.checkMD5(xml_object):
set_synchronized = 0 set_synchronized = 0
# This object has changed on this side, we have to generate some xmldiff # This object has changed on this side, we have to generate some xmldiff
if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_XML']:
xml_string = self.getXupdateObject( xml_string = self.getXupdateObject(
domain.getXMLFromObject(object), domain.getXMLFromObject(object),
signature.getXML()) signature.getXML())
else: #if there is no xml, we re-send all the object
xml_string=domain.getXMLFromObject(object)
if xml_string.count('\n') > self.MAX_LINES: if xml_string.count('\n') > self.MAX_LINES:
# This make comment fails, so we need to replace # This make comment fails, so we need to replace
if xml_string.find('--') >= 0: if xml_string.find('--') >= 0:
...@@ -828,11 +942,11 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -828,11 +942,11 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setStatus(status) signature.setStatus(status)
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
xml_string = domain.getXMLFromObject(object) xml_string = domain.getXMLFromObject(object)
gid = signature.getRid()#in fisrt, we try with rid if there is one rid = signature.getRid()#in fisrt, we try with rid if there is one
if gid == None:
gid = signature.getGid() gid = signature.getGid()
syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object, syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object,
gid=gid, xml_string=xml_string, gid=gid, rid=rid,
xml_string=xml_string,
more_data=more_data, more_data=more_data,
media_type=subscriber.getMediaType()) media_type=subscriber.getMediaType())
cmd_id += 1 cmd_id += 1
...@@ -884,11 +998,10 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -884,11 +998,10 @@ class XMLSyncUtilsMixin(SyncCode):
xml_string = domain.getXMLFromObject(object) xml_string = domain.getXMLFromObject(object)
#LOG('xml_string =', 0, xml_string) #LOG('xml_string =', 0, xml_string)
if signature.getAction()=='Replace': if signature.getAction()=='Replace':
gid = signature.getRid()#in fisrt, we try with rid if there is one rid = signature.getRid()#in fisrt, we try with rid if there is one
if gid == None:
gid = signature.getGid() gid = signature.getGid()
syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object, syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object,
gid=gid, xml_string=xml_string, more_data=more_data, gid=gid, rid=rid, xml_string=xml_string, more_data=more_data,
media_type=subscriber.getMediaType()) media_type=subscriber.getMediaType())
elif signature.getAction()=='Add': elif signature.getAction()=='Add':
gid = signature.getRid()#in fisrt, we try with rid if there is one gid = signature.getRid()#in fisrt, we try with rid if there is one
...@@ -918,7 +1031,10 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -918,7 +1031,10 @@ class XMLSyncUtilsMixin(SyncCode):
has_next_action = 0 has_next_action = 0
destination = self.unrestrictedTraverse(domain.getDestinationPath()) destination = self.unrestrictedTraverse(domain.getDestinationPath())
#LOG('applyActionList args',0,'domain : %s\n subscriber : %s\n cmd_id : %s' % (domain, subscriber, cmd_id)) #LOG('applyActionList args',0,'domain : %s\n subscriber : %s\n cmd_id : %s' % (domain, subscriber, cmd_id))
LOG('applyActionList', 0, self.getSyncActionList(remote_xml))
for action in self.getSyncActionList(remote_xml): for action in self.getSyncActionList(remote_xml):
if isinstance(action, unicode):
action = action.encode('utf-8')
conflict_list = [] conflict_list = []
status_code = self.SUCCESS status_code = self.SUCCESS
# Thirst we have to check the kind of action it is # Thirst we have to check the kind of action it is
...@@ -939,6 +1055,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -939,6 +1055,8 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setRid(rid) signature.setRid(rid)
#LOG('gid == rid ?', 0, 'gid=%s, rid=%s' % (gid, rid)) #LOG('gid == rid ?', 0, 'gid=%s, rid=%s' % (gid, rid))
object = subscriber.getObjectFromGid(gid) object = subscriber.getObjectFromGid(gid)
if object == None:
object = subscriber.getObjectFromRid(rid)
#LOG('applyActionList subscriber.getObjectFromGid %s' % gid,0,object) #LOG('applyActionList subscriber.getObjectFromGid %s' % gid,0,object)
if signature is None: if signature is None:
#LOG('applyActionList, signature is None',0,signature) #LOG('applyActionList, signature is None',0,signature)
...@@ -1014,6 +1132,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -1014,6 +1132,8 @@ class XMLSyncUtilsMixin(SyncCode):
if object is not None: if object is not None:
#LOG('SyncModif',0,'object: %s will be updated...' % object.id) #LOG('SyncModif',0,'object: %s will be updated...' % object.id)
signature = subscriber.getSignatureFromGid(gid) signature = subscriber.getSignatureFromGid(gid)
if signature is None:
signature = subscriber.getSignatureFromRid(gid)
#LOG('SyncModif',0,'previous signature: %s' % str(signature)) #LOG('SyncModif',0,'previous signature: %s' % str(signature))
previous_xml = signature.getXML() previous_xml = signature.getXML()
#LOG('SyncModif',0,'previous signature: %i' % len(previous_xml)) #LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
...@@ -1103,6 +1223,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -1103,6 +1223,8 @@ class XMLSyncUtilsMixin(SyncCode):
# object_gid = status['target'] # object_gid = status['target']
#else: #else:
object_gid = status['source'] object_gid = status['source']
if object_gid in ('', None, []):
object_gid = status['target']
status_code = int(status['code']) status_code = int(status['code'])
if status_cmd in ('Add','Replace'): if status_cmd in ('Add','Replace'):
has_status_list = 1 has_status_list = 1
...@@ -1126,7 +1248,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -1126,7 +1248,8 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
elif status_cmd == 'Delete': elif status_cmd == 'Delete':
if status_code == self.SUCCESS: if status_code == self.SUCCESS:
signature = subscriber.getSignatureFromGid(object_gid) or subscriber.getSignatureFromRid(object_gid) signature = subscriber.getSignatureFromGid(object_gid) or \
subscriber.getSignatureFromRid(object_gid)
if signature is not None: if signature is not None:
subscriber.delSignature(signature.getGid()) subscriber.delSignature(signature.getGid())
return (destination_waiting_more_data, has_status_list) return (destination_waiting_more_data, has_status_list)
...@@ -1177,20 +1300,20 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -1177,20 +1300,20 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
""" """
has_response = 0 #check if syncmodif replies to this messages has_response = 0 #check if syncmodif replies to this messages
cmd_id = 1 # specifies a SyncML message-unique command identifier cmd_id = 1 # specifies a SyncML message-unique command identifier
#LOG('SyncModif',0,'Starting... domain: %s' % str(domain)) LOG('SyncModif',0,'Starting... domain: %s' % str(domain))
first_node = remote_xml.childNodes[0] first_node = remote_xml.childNodes[0]
# Get informations from the header # Get informations from the header
xml_header = first_node.childNodes[1] xml_header = first_node.childNodes[1]
if xml_header.nodeName != "SyncHdr": if xml_header.nodeName != "SyncHdr":
#LOG('PubSyncModif',0,'This is not a SyncML Header') LOG('PubSyncModif',0,'This is not a SyncML Header')
raise ValueError, "Sorry, This is not a SyncML Header" raise ValueError, "Sorry, This is not a SyncML Header"
subscriber = domain # If we are the client, this is fine subscriber = domain # If we are the client, this is fine
simulate = 0 # used by applyActionList, should be 0 for client simulate = 0 # used by applyActionList, should be 0 for client
if domain.domain_type == self.PUB: if domain.domain_type == self.PUB:
simulate = 1 simulate = 1
subscription_url = self.getSourceURI(xml_header) subscription_url = self.getSubscriptionUrl(xml_header)
subscriber = domain.getSubscriber(subscription_url) subscriber = domain.getSubscriber(subscription_url)
# We have to check if this message was not already, this can be dangerous # We have to check if this message was not already, this can be dangerous
...@@ -1198,19 +1321,21 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -1198,19 +1321,21 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
message_id = self.getMessageId(remote_xml) message_id = self.getMessageId(remote_xml)
correct_message = subscriber.checkCorrectRemoteMessageId(message_id) correct_message = subscriber.checkCorrectRemoteMessageId(message_id)
if not correct_message: # We need to send again the message if not correct_message: # We need to send again the message
#LOG('SyncModif, no correct message:',0,"sending again...") LOG('SyncModif, no correct message:',0,"sending again...")
last_xml = subscriber.getLastSentMessage() last_xml = subscriber.getLastSentMessage()
#LOG("last_xml :", 0, last_xml) LOG("last_xml :", 0, last_xml)
if last_xml != '': if last_xml != '':
has_response = 1 has_response = 1
if domain.domain_type == self.PUB: # We always reply if domain.domain_type == self.PUB: # We always reply
self.sendResponse(from_url=domain.publication_url, self.sendResponse(from_url=domain.publication_url,
to_url=subscriber.subscription_url, sync_id=domain.getTitle(), to_url=subscriber.subscription_url, sync_id=domain.getTitle(),
xml=last_xml,domain=domain) xml=last_xml,domain=domain,
content_type=domain.getSyncContentType())
elif domain.domain_type == self.SUB: elif domain.domain_type == self.SUB:
self.sendResponse(from_url=domain.subscription_url, self.sendResponse(from_url=domain.subscription_url,
to_url=domain.publication_url, sync_id=domain.getTitle(), to_url=domain.publication_url, sync_id=domain.getTitle(),
xml=last_xml, domain=domain) xml=last_xml, domain=domain,
content_type=domain.getSyncContentType())
return {'has_response':has_response,'xml':last_xml} return {'has_response':has_response,'xml':last_xml}
subscriber.setLastSentMessage('') subscriber.setLastSentMessage('')
...@@ -1250,60 +1375,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -1250,60 +1375,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
# syncml body # syncml body
xml(' <SyncBody>\n') xml(' <SyncBody>\n')
# status for SyncHdr xml_status, cmd_id = self.SyncMLStatus(remote_xml, self.SUCCESS, cmd_id,
message_id = self.getMessageId(remote_xml) subscriber.getNextAnchor(), subscription=subscriber).values()
xml(' <Status>\n') xml(xml_status)
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1
xml(' <MsgRef>%s</MsgRef>\n' % message_id)
xml(' <CmdRef>0</CmdRef>\n') #to make reference to the SyncHdr, it's 0
xml(' <Cmd>SyncHdr</Cmd>\n')
xml(' <TargetRef>%s</TargetRef>\n' \
% remote_xml.xpath('string(//SyncHdr/Target/LocURI)').encode('utf-8'))
xml(' <SourceRef>%s</SourceRef>\n' \
% remote_xml.xpath('string(//SyncHdr/Source/LocURI)').encode('utf-8'))
xml(' <Data>200</Data>\n')
xml(' </Status>\n')
#list of element in the SyncBody bloc
syncbody_element_list = remote_xml.xpath('//SyncBody/*')
#add the status bloc corresponding to the receive command
for syncbody_element in syncbody_element_list:
if str(syncbody_element.nodeName) not in ('Status', 'Final', 'Replace'):
xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1
xml(' <MsgRef>%s</MsgRef>\n' % message_id)
xml(' <CmdRef>%s</CmdRef>\n' \
% syncbody_element.xpath('string(.//CmdID)').encode('utf-8'))
xml(' <Cmd>%s</Cmd>\n' % syncbody_element.nodeName.encode('utf-8'))
target_ref = syncbody_element.xpath('string(.//Target/LocURI)').encode('utf-8')
if target_ref not in (None, ''):
xml(' <TargetRef>%s</TargetRef>\n' % target_ref )
source_ref = syncbody_element.xpath('string(.//Source/LocURI)').encode('utf-8')
if source_ref not in (None, ''):
xml(' <SourceRef>%s</SourceRef>\n' % source_ref )
#xml(' <Data>%s</Data>\n' % subscriber.getSynchronizationType())
if syncbody_element.nodeName.encode('utf-8') == 'Add':
xml(' <Data>%s</Data>\n' % '201')
else:
xml(' <Data>%s</Data>\n' % '200')
# if syncbody_element.xpath('.//Item') not in ([], None, '') and\
# syncbody_element.xpath('.//Item.....'): #contient une ancre Next...
xml(' <Item>\n')
xml(' <Data>\n')
xml(' <Anchor xmlns="syncml:metinf">\n')
xml(' <Next>%s</Next>\n' % subscriber.getNextAnchor())
xml(' </Anchor>\n')
xml(' </Data>\n')
xml(' </Item>\n')
xml(' </Status>\n')
destination_url = '' destination_url = ''
# alert message if we want more data # alert message if we want more data
...@@ -1382,7 +1456,6 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -1382,7 +1456,6 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
if syncml_data != '': if syncml_data != '':
xml(' <Sync>\n') xml(' <Sync>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id_before_getsyncmldata) xml(' <CmdID>%s</CmdID>\n' % cmd_id_before_getsyncmldata)
if domain.domain_type == self.SUB:
if subscriber.getTargetURI() not in ('', None): if subscriber.getTargetURI() not in ('', None):
xml(' <Target>\n') xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % subscriber.getTargetURI()) xml(' <LocURI>%s</LocURI>\n' % subscriber.getTargetURI())
...@@ -1391,15 +1464,6 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -1391,15 +1464,6 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
xml(' <Source>\n') xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % subscriber.getSourceURI()) xml(' <LocURI>%s</LocURI>\n' % subscriber.getSourceURI())
xml(' </Source>\n') xml(' </Source>\n')
elif domain.domain_type == self.PUB:
if domain.getTargetURI() not in ('', None):
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % domain.getTargetURI())
xml(' </Target>\n')
if domain.getSourceURI() not in ('', None):
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % domain.getSourceURI())
xml(' </Source>\n')
xml(syncml_data) xml(syncml_data)
xml(' </Sync>\n') xml(' </Sync>\n')
xml(xml_confirmation) xml(xml_confirmation)
...@@ -1412,7 +1476,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -1412,7 +1476,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
subscriber.setLastSentMessage(xml_a) subscriber.setLastSentMessage(xml_a)
self.sendResponse(from_url=domain.publication_url, self.sendResponse(from_url=domain.publication_url,
to_url=subscriber.subscription_url, sync_id=domain.getTitle(), to_url=subscriber.subscription_url, sync_id=domain.getTitle(),
xml=xml_a,domain=domain) xml=xml_a,domain=domain,
content_type=domain.getSyncContentType())
has_response = 1 has_response = 1
elif domain.domain_type == self.SUB: elif domain.domain_type == self.SUB:
if self.checkAlert(remote_xml) or \ if self.checkAlert(remote_xml) or \
...@@ -1421,6 +1486,37 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -1421,6 +1486,37 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
subscriber.setLastSentMessage(xml_a) subscriber.setLastSentMessage(xml_a)
self.sendResponse(from_url=domain.subscription_url, self.sendResponse(from_url=domain.subscription_url,
to_url=domain.publication_url, sync_id=domain.getTitle(), to_url=domain.publication_url, sync_id=domain.getTitle(),
xml=xml_a,domain=domain) xml=xml_a,domain=domain,
content_type=domain.getSyncContentType())
has_response = 1 has_response = 1
return {'has_response':has_response,'xml':xml_a} return {'has_response':has_response,'xml':xml_a}
def xml2wbxml(self, xml):
"""
convert xml string to wbxml using a temporary file
"""
LOG('xml2wbxml starting ...',0,'')
import os
f = open('/tmp/xml2wbxml', 'w')
f.write(xml)
f.close()
os.system('/usr/bin/xml2wbxml -o /tmp/xml2wbxml /tmp/xml2wbxml')
f = open('/tmp/xml2wbxml', 'r')
wbxml = f.read()
f.close()
return wbxml
def wbxml2xml(self, wbxml):
"""
convert wbxml string to xml using a temporary file
"""
LOG('wbxml2xml starting ...',0,'')
import os
f = open('/tmp/wbxml2xml', 'w')
f.write(wbxml)
f.close()
os.system('/usr/bin/wbxml2xml -o /tmp/wbxml2xml /tmp/wbxml2xml')
f = open('/tmp/wbxml2xml', 'r')
xml = f.read()
f.close()
return xml
...@@ -55,6 +55,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -55,6 +55,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<b>Query :</b> the type of objects you want to synchronize</br> <b>Query :</b> the type of objects you want to synchronize</br>
<b>XML Mapping :</b> the page template used on each object before <b>XML Mapping :</b> the page template used on each object before
an export</br> an export</br>
<b>Synchronize with other ERP5 sites :</b> The method currently used
to send data to erp5 and external sites is not the same (it will be
improved soon)</br>
<b>Content Type :</b> the type of content use to exchange data
(could be 'application/vnd.syncml+wbxml' or
'application/vnd.syncml+xml' for example)</br>
<b>Conduit :</b> the conduit used to synchronize</br> <b>Conduit :</b> the conduit used to synchronize</br>
<b>GPG key name :</b>a name of gpg key to use</br> <b>GPG key name :</b>a name of gpg key to use</br>
<b>Id Generator :</b> This set the method name wich allows to <b>Id Generator :</b> This set the method name wich allows to
...@@ -80,6 +86,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -80,6 +86,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<b>Query :</b> the type of objects you want to synchronize</br> <b>Query :</b> the type of objects you want to synchronize</br>
<b>XML Mapping :</b> the page template used on each object before <b>XML Mapping :</b> the page template used on each object before
an export</br> an export</br>
<b>Synchronize with other ERP5 sites :</b> The method currently used
to send data to erp5 and external sites is not the same (it will be
improved soon)</br>
<b>Content Type :</b> the type of content use to exchange data
(could be 'application/vnd.syncml+wbxml' or
'application/vnd.syncml+xml' for example)</br>
<b>Conduit :</b> the conduit used to synchronize</br> <b>Conduit :</b> the conduit used to synchronize</br>
<b>GPG key name :</b>a name of gpg key to use</br> <b>GPG key name :</b>a name of gpg key to use</br>
<b>Id Generator :</b> This set the method name wich allows to <b>Id Generator :</b> This set the method name wich allows to
......
...@@ -108,6 +108,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -108,6 +108,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" /> <input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Synchronize with other ERP5 sites
</label></div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="synchronize_with_erp5_sites" value="1" <dtml-if expr="getSynchronizeWithERP5Sites()">CHECKED</dtml-if>>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Content Type
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="sync_content_type" value="<dtml-var getSyncContentType>" size="40" />
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
...@@ -144,6 +144,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -144,6 +144,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" /> <input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Synchronize with other ERP5 sites
</label></div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="synchronize_with_erp5_sites" value="1" <dtml-if expr="getSynchronizeWithERP5Sites()">CHECKED</dtml-if>>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Content Type
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="sync_content_type" value="<dtml-var getSyncContentType>" size="40" />
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
...@@ -103,6 +103,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -103,6 +103,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="xml_mapping" size="40" /> <input type="text" name="xml_mapping" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Synchronize with other ERP5 sites
</label></div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="synchronize_with_erp5_sites" value="1">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Content Type
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="sync_content_type" size="40" />
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
...@@ -140,6 +140,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -140,6 +140,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="xml_mapping" size="40" /> <input type="text" name="xml_mapping" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Synchronize with other ERP5 sites
</label></div>
</td>
<td align="left" valign="top">
<input type="checkbox" name="synchronize_with_erp5_sites" value="1">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Content Type
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="sync_content_type" size="40" />
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment