Commit ee110eaa authored by Sebastien Robin's avatar Sebastien Robin

Commit the work of Fabien

- use list of string instead of adding string to strings
  in order to improve performance
- use message id instead of session id. Increase message id
  for each message
- start to implement authentication


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@14280 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent b3833444
...@@ -39,13 +39,22 @@ from zLOG import LOG ...@@ -39,13 +39,22 @@ from zLOG import LOG
class PublicationSynchronization(XMLSyncUtils): class PublicationSynchronization(XMLSyncUtils):
def PubSyncInit(self, publication=None, xml_client=None, subscriber=None, sync_type=None): def PubSyncInit(self, publication=None, xml_client=None, subscriber=None,
sync_type=None, auth_required=0):
""" """
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
subscriber.setSessionId(self.getSessionId(xml_client))
# for a new session, the message Id must be reset
subscriber.resetMessageId()
#the last_message_id is 1 because the message that
#we are about to send is the message 1
subscriber.initLastMessageId(1)
alert = None alert = None
# Get informations from the body # Get informations from the body
if xml_client is not None: # We have received a message if xml_client is not None: # We have received a message
...@@ -53,55 +62,91 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -53,55 +62,91 @@ class PublicationSynchronization(XMLSyncUtils):
next_anchor = self.getAlertNextAnchor(xml_client) next_anchor = self.getAlertNextAnchor(xml_client)
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)
#XXX this is in developement, it's just for tests
if not cred and auth_required:
LOG('PubSyncInit',0,'authentication required')
# 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.incrementMessageId(), subscriber.getSubscriptionUrl(),
publication.getPublicationUrl()))
# syncml body
xml(' <SyncBody>\n')
# chal message
xml(self.SyncMLChal(cmd_id, "SyncHdr", publication.getPublicationUrl(),
subscriber.getSubscriptionUrl(), "b64", "syncml:auth-basic",
self.UNAUTHORIZED))
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 slow sync, then resend everything # If slow sync, then resend everything
if alert_code == self.SLOW_SYNC: if alert_code == self.SLOW_SYNC:
LOG('Warning !!!, reseting client synchronization for subscriber:',0,subscriber) LOG('Warning !!!, reseting client synchronization for subscriber:',0,
subscriber)
subscriber.resetAllSignatures() subscriber.resetAllSignatures()
# Check if the last time synchronization is the same as the client one # 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 subscriber.getNextAnchor() != last_anchor:
if last_anchor == None: if last_anchor == None:
LOG('PubSyncInit',0,'anchor null') LOG('PubSyncInit',0,'anchor null')
raise ValueError, "Sorry, the anchor was null"
else: else:
message = "bad anchors in PubSyncInit! " + subscriber.getNextAnchor() + \ message = "bad anchors in PubSyncInit! " + subscriber.getNextAnchor() + \
" and " + last_anchor " and " + last_anchor
LOG('PubSyncInit',0,message) LOG('PubSyncInit',0,message)
else: else:
subscriber.setNextAnchor(next_anchor) 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 resolution)
pass pass
xml = "" if alert is not None and auth_required==0:
#if alert is not None: #if 1:
if 1:
# 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 cmd_id = 1 # specifies a SyncML message-unique command identifier
xml = "" xml_list = []
xml += '<SyncML>\n' xml = xml_list.append
xml('<SyncML>\n')
# syncml header # syncml header
xml += self.SyncMLHeader(subscriber.getSessionId(), "1", xml(self.SyncMLHeader(subscriber.getSessionId(),
subscriber.getSubscriptionUrl(), publication.getPublicationUrl()) subscriber.incrementMessageId(), subscriber.getSubscriptionUrl(),
publication.getPublicationUrl()))
# syncml body # syncml body
xml += ' <SyncBody>\n' xml(' <SyncBody>\n')
# alert message # alert message
xml += self.SyncMLAlert(cmd_id, sync_type, subscriber.getSubscriptionUrl(), xml(self.SyncMLAlert(cmd_id, sync_type, subscriber.getSubscriptionUrl(),
publication.getPublicationUrl(), subscriber.getLastAnchor(), subscriber.getNextAnchor()) publication.getPublicationUrl(), subscriber.getLastAnchor(),
subscriber.getNextAnchor()))
cmd_id += 1 cmd_id += 1
xml += ' </SyncBody>\n' xml(' </SyncBody>\n')
xml('</SyncML>\n')
xml += '</SyncML>\n' xml_a = ''.join(xml_list)
self.sendResponse(from_url=publication.getPublicationUrl(), self.sendResponse(from_url=publication.getPublicationUrl(),
to_url=subscriber.getSubscriptionUrl(),sync_id=publication.getTitle(),xml=xml, to_url=subscriber.getSubscriptionUrl(), sync_id=publication.getTitle(),
domain=publication) xml=xml_a, domain=publication)
return {'has_response':1,'xml':xml} return {'has_response':1,'xml':xml_a}
def PubSync(self, id, msg=None, RESPONSE=None, subscriber=None): def PubSync(self, id, msg=None, RESPONSE=None, subscriber=None):
...@@ -118,23 +163,27 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -118,23 +163,27 @@ class PublicationSynchronization(XMLSyncUtils):
publication = self.getPublication(id) publication = self.getPublication(id)
if xml_client is not None: if xml_client is not None:
if type(xml_client) in (type('a'),type(u'a')): if isinstance(xml_client, str) or isinstance(xml_client, unicode):
xml_client = parseString(xml_client) xml_client = parseString(xml_client)
first_node = xml_client.childNodes[0] first_node = xml_client.childNodes[0]
if first_node.nodeName != "SyncML": if first_node.nodeName != "SyncML":
LOG('PubSync',0,'This is not a SyncML Message') LOG('PubSync',0,'This is not a SyncML Message')
return raise ValueError, "Sorry, This is not a SyncML Message"
alert_code = self.getAlertCode(xml_client) alert_code = self.getAlertCode(xml_client)
# Get informations from the header # Get informations from the header
client_header = first_node.childNodes[1] client_header = first_node.childNodes[1]
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')
return raise ValueError, "Sorry, This is not a SyncML Header"
for subnode in client_header.childNodes: for subnode in client_header.childNodes:
if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Source": if subnode.nodeType == subnode.ELEMENT_NODE and \
subscription_url = str(subnode.childNodes[0].data) subnode.nodeName == "Source":
for subnode2 in subnode.childNodes:
if subnode2.nodeType == subnode2.ELEMENT_NODE and \
subnode2.nodeName == "LocURI":
subscription_url = str(subnode2.childNodes[0].data)
# 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:
...@@ -142,19 +191,21 @@ class PublicationSynchronization(XMLSyncUtils): ...@@ -142,19 +191,21 @@ class PublicationSynchronization(XMLSyncUtils):
subscriber.setXMLMapping(publication.getXMLMapping()) subscriber.setXMLMapping(publication.getXMLMapping())
publication.addSubscriber(subscriber) publication.addSubscriber(subscriber)
# first synchronization # first synchronization
result = self.PubSyncInit(publication,xml_client,subscriber=subscriber,sync_type=self.SLOW_SYNC) result = self.PubSyncInit(publication,xml_client,subscriber=subscriber,
sync_type=self.SLOW_SYNC)
elif self.checkAlert(xml_client) and alert_code in (self.TWO_WAY,self.SLOW_SYNC): elif self.checkAlert(xml_client) and \
alert_code in (self.TWO_WAY,self.SLOW_SYNC):
result = self.PubSyncInit(publication=publication, result = self.PubSyncInit(publication=publication,
xml_client=xml_client, subscriber=subscriber,sync_type=alert_code) xml_client=xml_client, subscriber=subscriber, sync_type=alert_code)
else: else:
result = self.PubSyncModif(publication, xml_client) result = self.PubSyncModif(publication, xml_client)
elif subscriber is not None: elif subscriber is not None:
# This looks like we are starting a synchronization after # This looks like we are starting a synchronization after
# a conflict resolution by the user # a conflict resolution by the user
result = self.PubSyncInit(publication=publication, result = self.PubSyncInit(publication=publication, xml_client=None,
xml_client=None, subscriber=subscriber,sync_type=self.TWO_WAY) subscriber=subscriber,sync_type=self.TWO_WAY)
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('managePublications') RESPONSE.redirect('managePublications')
......
...@@ -723,6 +723,26 @@ class Subscription(Folder, SyncCode): ...@@ -723,6 +723,26 @@ class Subscription(Folder, SyncCode):
self.last_session_id = session_id self.last_session_id = session_id
return 1 return 1
def checkCorrectRemoteMessageId(self, message_id):
"""
We will see if the last message id was the same
wich means that the same message was sent again
return 1 if the message id was not seen, 0 if already seen
"""
last_message_id = getattr(self,'last_message_id',None)
LOG('checkCorrectRemoteMessageId last_message_id =',0,last_message_id)
LOG('checkCorrectRemoteMessageId message_id =',0,message_id)
if last_message_id == message_id:
return 0
self.last_message_id = message_id
return 1
def initLastMessageId(self, last_message_id=None):
"""
set the last message id to 0
"""
self.last_message_id=last_message_id
def getLastSentMessage(self): def getLastSentMessage(self):
""" """
...@@ -926,7 +946,7 @@ class Subscription(Folder, SyncCode): ...@@ -926,7 +946,7 @@ class Subscription(Folder, SyncCode):
query_list = [] query_list = []
if query is None: if query is None:
return query_list return query_list
if type(query) is type('a'): if isinstance(query, str):
query_method = getattr(destination,query,None) query_method = getattr(destination,query,None)
if query_method is not None: if query_method is not None:
query_list = query_method() query_list = query_method()
...@@ -1010,13 +1030,50 @@ class Subscription(Folder, SyncCode): ...@@ -1010,13 +1030,50 @@ class Subscription(Folder, SyncCode):
""" """
return self return self
def setSessionId(self, session_id):
"""
set the session id
"""
self.session_id = session_id
def getSessionId(self): def getSessionId(self):
""" """
return the session id return the session id
""" """
#self.session_id += 1 #to be commented
return self.session_id
def incrementSessionId(self):
"""
increment and return the session id
"""
self.session_id += 1 self.session_id += 1
self.resetMessageId() # for a new session, the message Id must be reset
return self.session_id return self.session_id
def incrementMessageId(self):
"""
return the message id
"""
#self.message_id += 1
#return self.message_id
#return 5
value = getattr(self, 'message_id', 0)
self.message_id = value +1
return self.message_id
def getMessageId(self):
"""
increment and return the message id
"""
return self.message_id
def resetMessageId(self):
"""
set the message id to 0
"""
self.message_id = 0
def getLastAnchor(self): def getLastAnchor(self):
""" """
return the id of the last synchronisation return the id of the last synchronisation
......
...@@ -44,38 +44,43 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -44,38 +44,43 @@ class SubscriptionSynchronization(XMLSyncUtils):
""" """
LOG('SubSyncInit',0,'starting....') LOG('SubSyncInit',0,'starting....')
cmd_id = 1 # specifies a SyncML message-unique command identifier cmd_id = 1 # specifies a SyncML message-unique command identifier
xml = "" xml_list = []
xml += '<SyncML>\n' xml = xml_list.append
xml('<SyncML>\n')
# syncml header # syncml header
xml += self.SyncMLHeader(subscription.getSessionId(), "1", xml(self.SyncMLHeader(subscription.incrementSessionId(),
subscription.getPublicationUrl(), subscription.getSubscriptionUrl()) subscription.incrementMessageId(), subscription.getPublicationUrl(),
subscription.getSubscriptionUrl()))
# syncml body # syncml body
xml += ' <SyncBody>\n' xml(' <SyncBody>\n')
subscription.NewAnchor() subscription.NewAnchor()
subscription.initLastMessageId()
# We have to set every object as NOT_SYNCHRONIZED # We have to set every object as NOT_SYNCHRONIZED
subscription.startSynchronization() subscription.startSynchronization()
# alert message # alert message
xml += self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(), xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(),
subscription.getPublicationUrl(), subscription.getPublicationUrl(),
subscription.getDestinationPath(), subscription.getDestinationPath(),
subscription.getLastAnchor(), subscription.getNextAnchor()) subscription.getLastAnchor(),
subscription.getNextAnchor()))
cmd_id += 1 cmd_id += 1
xml += ' <Put>\n' xml(' <Put>\n')
xml += ' <CmdID>%s</CmdID>\n' % cmd_id ; cmd_id += 1 xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml += ' </Put>\n' cmd_id += 1
xml += ' </SyncBody>\n' xml(' </Put>\n')
xml(' </SyncBody>\n')
xml += '</SyncML>\n' xml('</SyncML>\n')
xml_a = ''.join(xml_list)
self.sendResponse(from_url=subscription.subscription_url, to_url=subscription.publication_url, self.sendResponse(from_url=subscription.subscription_url,
sync_id=subscription.getTitle(), xml=xml,domain=subscription) to_url=subscription.publication_url, sync_id=subscription.getTitle(),
xml=xml_a,domain=subscription)
return {'has_response':1,'xml':xml} return {'has_response':1,'xml':xml_a}
def SubSync(self, id, msg=None, RESPONSE=None): def SubSync(self, id, msg=None, RESPONSE=None):
""" """
...@@ -83,17 +88,17 @@ class SubscriptionSynchronization(XMLSyncUtils): ...@@ -83,17 +88,17 @@ class SubscriptionSynchronization(XMLSyncUtils):
""" """
LOG('SubSync',0,'starting... id: %s' % str(id)) LOG('SubSync',0,'starting... id: %s' % str(id))
LOG('SubSync',0,'starting... msg: %s' % str(msg)) LOG('SubSync',0,'starting... msg: %s' % str(msg))
response = None #check if subsync replies to this messages response = None #check if subsync replies to this messages
subscription = self.getSubscription(id) subscription = self.getSubscription(id)
if msg==None and (subscription.getSubscriptionUrl()).find('file')>=0: if msg==None and (subscription.getSubscriptionUrl()).find('file')>=0:
msg = self.readResponse(sync_id=id,from_url=subscription.getSubscriptionUrl()) msg = self.readResponse(sync_id=id,
from_url=subscription.getSubscriptionUrl())
if msg==None: if msg==None:
response = self.SubSyncInit(self.getSubscription(id)) response = self.SubSyncInit(self.getSubscription(id))
else: else:
xml_client = msg xml_client = msg
if type(xml_client) in (type('a'),type(u'a')): if isinstance(xml_client, str) or isinstance(xml_client, unicode):
xml_client = parseString(xml_client) xml_client = parseString(xml_client)
response = self.SubSyncModif(self.getSubscription(id),xml_client) response = self.SubSyncModif(self.getSubscription(id),xml_client)
......
...@@ -47,6 +47,9 @@ class SyncCode(Persistent): ...@@ -47,6 +47,9 @@ class SyncCode(Persistent):
# whatever is needed to change(replace) # whatever is needed to change(replace)
CONFLICT_CLIENT_WIN = 208 # The client is the "winner", we keep CONFLICT_CLIENT_WIN = 208 # The client is the "winner", we keep
# the version of the client # the version of the client
UNAUTHORIZED = 401
AUTH_REQUIRED = 407
AUTH_ACCEPTED = 212
# Difference between publication and subscription # Difference between publication and subscription
PUB = 1 PUB = 1
......
...@@ -734,9 +734,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati ...@@ -734,9 +734,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
""" """
if context is None: if context is None:
return context return context
elif type(context) is type(()): elif isinstance(context, tuple):
return context return context
elif type(context) is type('a'): elif isinstance(context, tuple):
return tuple(context.split('/')) return tuple(context.split('/'))
else: else:
return context.getPhysicalPath() return context.getPhysicalPath()
...@@ -751,7 +751,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati ...@@ -751,7 +751,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
LOG('sendResponse, to_url: ',0,to_url) LOG('sendResponse, to_url: ',0,to_url)
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: ',0,xml) LOG('sendResponse, xml: \n',0,xml)
if domain is not None: if domain is not None:
gpg_key = domain.getGPGKey() gpg_key = domain.getGPGKey()
if gpg_key not in ('',None): if gpg_key not in ('',None):
...@@ -768,14 +768,14 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati ...@@ -768,14 +768,14 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
commands.getstatusoutput('rm -f /tmp/%s.gz' % filename) commands.getstatusoutput('rm -f /tmp/%s.gz' % filename)
commands.getstatusoutput('rm -f /tmp/%s.gz.gpg' % filename) commands.getstatusoutput('rm -f /tmp/%s.gz.gpg' % filename)
if send: if send:
if type(to_url) is type('a'): if isinstance(to_url, str):
if to_url.find('http://')==0: if to_url.find('http://')==0:
# XXX Make sure this is not a problem # XXX Make sure this is not a problem
if domain.domain_type == self.PUB: if domain.domain_type == self.PUB:
return None return None
# we will send an http response # we will send an http response
domain = aq_base(domain) domain = aq_base(domain)
LOG('sendResponse, will start sendHttpResponse, xml',0,xml) LOG('sendResponse, will start sendHttpResponse, xml\n',0,xml)
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, domain=domain) xml=xml, domain=domain)
...@@ -907,30 +907,38 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati ...@@ -907,30 +907,38 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati
commands.getstatusoutput('rm -f /tmp/%s.gz.gpg' % filename) commands.getstatusoutput('rm -f /tmp/%s.gz.gpg' % filename)
# Get the target and then find the corresponding publication or # Get the target and then find the corresponding publication or
# Subscription # Subscription
LOG('readResponse, xml before parseSTring',0,text) LOG('readResponse, xml before parseSTring\n',0,text)
xml = parseString(text) xml = parseString(text)
url = ''
for subnode in self.getElementNodeList(xml): #XXX this function is not very optimized and should be improved
if subnode.nodeName == 'SyncML': url = self.getTarget(xml)
for subnode1 in self.getElementNodeList(subnode):
if subnode1.nodeName == 'SyncHdr':
for subnode2 in self.getElementNodeList(subnode1):
if subnode2.nodeName == 'Target':
url = subnode2.childNodes[0].data
for publication in self.getPublicationList(): for publication in self.getPublicationList():
if publication.getPublicationUrl()==url and publication.getTitle()==sync_id: if publication.getPublicationUrl()==url and publication.getTitle()==sync_id:
result = self.PubSync(sync_id,xml) result = self.PubSync(sync_id,xml)
# Then encrypt the message # Then encrypt the message
xml = result['xml'] xml = result['xml']
xml = self.sendResponse(xml=xml,domain=publication,send=0)
#must be commented because this method is alredy called
#xml = self.sendResponse(xml=xml,domain=publication,send=0)
return xml return xml
for subscription in self.getSubscriptionList(): for subscription in self.getSubscriptionList():
if subscription.getSubscriptionUrl()==url and subscription.getTitle()==sync_id: if subscription.getSubscriptionUrl()==url and \
subscription.getTitle()==sync_id:
next_status = self.getNextSyncBodyStatus(xml, None)
if next_status is not None:
status_code = self.getStatusCode(next_status)
LOG('readResponse status code :',0,status_code)
if status_code == self.UNAUTHORIZED or \
status_code == self.AUTH_REQUIRED:
LOG('readResponse', 0, 'Authentication required')
raise ValueError, "Authentication required"
else:
result = self.activate(activity='RAMQueue').SubSync(sync_id,xml) result = self.activate(activity='RAMQueue').SubSync(sync_id,xml)
#result = self.SubSync(sync_id,xml) #result = self.SubSync(sync_id,xml)
# we use from only if we have a file # we use from only if we have a file
elif type(from_url) is type('a'): elif isinstance(from_url, str):
if from_url.find('file://')==0: if from_url.find('file://')==0:
try: try:
filename = from_url[len('file:/'):] filename = from_url[len('file:/'):]
......
This diff is collapsed.
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