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):
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):
"""
We send all the updated documents (ie. documents not marked
......@@ -155,7 +145,8 @@ class Publication(Subscription):
def __init__(self, id, title, publication_url, destination_path,
source_uri, query, xml_mapping, conduit, gpg_key, id_generator,
gid_generator, media_type, auth_required, authentication_format,
authentication_type, activity_enabled):
authentication_type, activity_enabled, synchronize_with_erp5_sites,
sync_content_type):
"""
constructor
"""
......@@ -178,6 +169,8 @@ class Publication(Subscription):
self.auth_required = auth_required
self.authentication_format = authentication_format
self.authentication_type = authentication_type
self.setSyncContentType(sync_content_type)
self.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
def getPublicationUrl(self):
"""
......@@ -238,6 +231,7 @@ class Publication(Subscription):
"""
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)
self.delSubscriber(subscriber.getSubscriptionUrl())
new_id = subscriber.getId()
......
......@@ -52,7 +52,7 @@ class PublicationSynchronization(XMLSyncUtils):
Read the client xml message
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))
......@@ -72,153 +72,145 @@ class PublicationSynchronization(XMLSyncUtils):
alert = self.checkAlert(xml_client)
alert_code = self.getAlertCode(xml_client)
cred = self.checkCred(xml_client)
#XXX this is in developement, it's just for tests
#the source and the target of the subscriber are reversed compared
# to those of the publication :
subscriber.setSourceURI(self.getTargetURI(xml_client))
subscriber.setTargetURI(self.getSourceURI(xml_client))
# 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 = xml_list.append
cmd_id = 1 # specifies a SyncML message-unique command identifier
xml('<SyncML>\n')
# syncml header
xml(self.SyncMLHeader(subscriber.getSessionId(),
subscriber.getMessageId(),
subscriber.getSubscriptionUrl(),
publication.getPublicationUrl()))
# syncml body
xml(' <SyncBody>\n')
if publication.isAuthenticationRequired():
#at the begining, the code is initialised at UNAUTHORIZED
auth_code=self.UNAUTHORIZED
LOG('PubSyncInit',0,'authentication required')
if not cred:
#LOG('PubSyncInit',0,'authentication required')
auth_code=self.AUTH_REQUIRED
LOG("there's no credential !!!",0,'')
# 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')
# chal message
xml(self.SyncMLChal(cmd_id, "SyncHdr",
publication.getPublicationUrl(), subscriber.getSubscriptionUrl(),
publication.getAuthenticationFormat(),
publication.getAuthenticationType(), self.AUTH_REQUIRED))
publication.getAuthenticationType(), auth_code))
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
(authentication_format, authentication_type, data) = self.getCred(xml_client)
#at the begining, the code is initialised at UNAUTHORIZED
auth_code=self.UNAUTHORIZED
if authentication_format == publication.getAuthenticationFormat():
if authentication_type == publication.getAuthenticationType():
decoded = subscriber.decode(authentication_format, data)
if decoded not in ('', None) and ':' in decoded:
(login, password) = decoded.split(':')
uf = self.getPortalObject().acl_users
for plugin_name, plugin in uf._getOb('plugins').listPlugins(
IAuthenticationPlugin ):
if plugin.authenticateCredentials(
{'login':login, 'password':password}) is not None:
subscriber.setAuthenticated(True)
auth_code=self.AUTH_ACCEPTED
#here we must log in with the user authenticated :
user = uf.getUserById(login).__of__(uf)
newSecurityManager(None, user)
subscriber.setUser(login)
break
else:
auth_code=self.UNAUTHORIZED
# 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():
authentication_format = publication.getAuthenticationFormat()
decoded = subscriber.decode(authentication_format, data)
if decoded not in ('', None) and ':' in decoded:
(login, password) = decoded.split(':')
uf = self.getPortalObject().acl_users
for plugin_name, plugin in uf._getOb('plugins').listPlugins(
IAuthenticationPlugin ):
if plugin.authenticateCredentials(
{'login':login, 'password':password}) is not None:
subscriber.setAuthenticated(True)
auth_code=self.AUTH_ACCEPTED
#here we must log in with the user authenticated :
user = uf.getUserById(login).__of__(uf)
newSecurityManager(None, user)
subscriber.setUser(login)
break
else:
auth_code=self.UNAUTHORIZED
#in all others cases, the auth_code is set to UNAUTHORIZED
# 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:
xml_status, cmd_id = self.SyncMLStatus(xml_client, auth_code,
cmd_id, next_anchor, subscription=subscriber).values()
xml(xml_status)
# alert message
xml(self.SyncMLAlert(cmd_id, sync_type,
subscriber.getSubscriptionUrl(), publication.getPublicationUrl(),
subscriber.getLastAnchor(), subscriber.getNextAnchor()))
xml(self.SyncMLAlert(cmd_id, sync_type, subscriber.getTargetURI(),
subscriber.getSourceURI(), subscriber.getLastAnchor(),
next_anchor))
cmd_id += 1
else:
# chal message
xml(self.SyncMLChal(cmd_id, "SyncHdr",
publication.getPublicationUrl(), subscriber.getSubscriptionUrl(),
publication.getAuthenticationFormat(),
publication.getAuthenticationType(), self.AUTH_REQUIRED))
publication.getAuthenticationType(), auth_code))
cmd_id += 1
xml(' </SyncBody>\n')
xml('</SyncML>\n')
xml_a = ''.join(xml_list)
xml_status, cmd_id = self.SyncMLStatus(xml_client,
self.AUTH_REQUIRED, cmd_id, next_anchor,
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)
elif alert is not None: #if no identification is required :
# syncml header
xml_status, cmd_id = self.SyncMLStatus(xml_client, self.AUTH_ACCEPTED,
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
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
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 == 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
subscriber.startSynchronization()
else:
# We have started the sync from the server (may be for a conflict resolution)
pass
if alert is not None and not publication.isAuthenticationRequired():
# 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
# We have started the sync from the server (may be for a conflict
# resolution)
raise ValueError, "the syncml message is None. Maybe a synchronisation \
has been started from the server (forbiden)"
# a synchronisation is always starded from a client and can't be from
# a server !
xml('<SyncML>\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('</SyncML>\n')
xml_a = ''.join(xml_list)
xml(' <Final/>\n')
xml(' </SyncBody>\n')
xml('</SyncML>\n')
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(),
to_url=subscriber.getSubscriptionUrl(), sync_id=publication.getTitle(),
xml=xml_a, domain=publication,
content_type=publication.getSyncContentType())
self.sendResponse(from_url=publication.getPublicationUrl(),
to_url=subscriber.getSubscriptionUrl(), sync_id=publication.getTitle(),
xml=xml_a, domain=publication)
return {'has_response':1,'xml':xml_a}
......@@ -226,7 +218,7 @@ class PublicationSynchronization(XMLSyncUtils):
"""
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
publication = self.unrestrictedTraverse(publication_path)
xml_client = msg
......@@ -244,18 +236,19 @@ class PublicationSynchronization(XMLSyncUtils):
#LOG('PubSync',0,'This is not a SyncML Message')
raise ValueError, "Sorry, This is not a SyncML Message"
alert_code = self.getAlertCode(xml_client)
# Get informations from the header
client_header = first_node.childNodes[1]
if client_header.nodeName != "SyncHdr":
#LOG('PubSync',0,'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
subscriber = publication.getSubscriber(subscription_url)
if subscriber == None:
subscriber = Subscriber(publication.generateNewId(),subscription_url)
subscriber.setXMLMapping(publication.getXMLMapping())
subscriber.setConduit(publication.getConduit())
publication.addSubscriber(subscriber)
# first synchronization
result = self.PubSyncInit(publication,xml_client,subscriber=subscriber,
......@@ -266,6 +259,8 @@ class PublicationSynchronization(XMLSyncUtils):
xml_client=xml_client, subscriber=subscriber, sync_type=alert_code)
else:
#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 subscriber.isAuthenticated():
uf = self.getPortalObject().acl_users
......
......@@ -575,6 +575,7 @@ def addSubscription( self, id, title='', REQUEST=None ):
"""
Add a new Subscribption
"""
LOG('addSubscription starting...',0,'')
o = Subscription( id ,'','','','','','')
self._setObject( id, o )
if REQUEST is not None:
......@@ -648,7 +649,8 @@ class Subscription(Folder, SyncCode):
def __init__(self, id, title, publication_url, subscription_url,
destination_path, source_uri, target_uri, query, xml_mapping,
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
signatures of documents which belong to the synchronisation
......@@ -679,7 +681,8 @@ class Subscription(Folder, SyncCode):
self.setConduit(conduit)
Folder.__init__(self, id)
self.title = title
self.setSyncContentType(sync_content_type)
self.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
#self.signatures = PersitentMapping()
def getAlertCodeList(self):
......@@ -727,7 +730,7 @@ class Subscription(Folder, SyncCode):
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)
......@@ -739,11 +742,26 @@ class Subscription(Folder, SyncCode):
def getTargetURI(self):
"""
getter for the target_uri (the distant Publication we want to synchronize
with)
getter for the target_uri (the distant Publication data base we want to
synchronize with)
"""
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):
"""
"""
......@@ -770,6 +788,21 @@ class Subscription(Folder, SyncCode):
value = None
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):
"""
We will see if the last session id was the same
......@@ -886,7 +919,7 @@ class Subscription(Folder, SyncCode):
def setPublicationUrl(self, publication_url):
"""
return the publication url
set the publication url
"""
self.publication_url = publication_url
......@@ -1058,6 +1091,30 @@ class Subscription(Folder, SyncCode):
break
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):
"""
This returns the list of sub-object corresponding
......
......@@ -75,7 +75,8 @@ class SubscriptionSynchronization(XMLSyncUtils):
self.sendResponse(from_url=subscription.subscription_url,
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}
......@@ -86,7 +87,7 @@ class SubscriptionSynchronization(XMLSyncUtils):
response = None #check if subsync replies to this messages
subscription = self.unrestrictedTraverse(subscription_path)
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())
if msg==None:
response = self.SubSyncInit(subscription)
......@@ -170,7 +171,8 @@ class SubscriptionSynchronization(XMLSyncUtils):
self.sendResponse(from_url=subscription.subscription_url,
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}
......
......@@ -37,8 +37,9 @@ class SyncCode(Persistent):
# SyncML Alert Codes
TWO_WAY = 200
SLOW_SYNC = 201 # This means we get the data from the publication
WAITING_DATA = 214
ONE_WAY_FROM_SERVER = 204
WAITING_DATA = 214
REFRESH_REQUIRED = 508
# SyncML Status Codes
SUCCESS = 200
......@@ -121,3 +122,9 @@ class SyncCode(Persistent):
MEDIA_TYPE = {}
MEDIA_TYPE['TEXT_XML'] = 'text/xml'
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,
security.declareProtected(Permissions.ModifyPortalContent,
'manage_addPublication')
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,
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
"""
......@@ -192,7 +195,9 @@ class SynchronizationTool( SubscriptionSynchronization,
destination_path, source_uri, query, xml_mapping,
conduit, gpg_key, synchronization_id_generator,
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 )
#if len(self.list_publications) == 0:
# self.list_publications = PersistentMapping()
......@@ -207,7 +212,10 @@ class SynchronizationTool( SubscriptionSynchronization,
xml_mapping, conduit, gpg_key,
synchronization_id_generator=None, gid_generator=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
create a new subscription
......@@ -221,7 +229,8 @@ class SynchronizationTool( SubscriptionSynchronization,
destination_path, source_uri, target_uri, query,
xml_mapping, conduit, gpg_key,
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 )
#if len(self.list_subscriptions) == 0:
# self.list_subscriptions = PersistentMapping()
......@@ -236,7 +245,9 @@ class SynchronizationTool( SubscriptionSynchronization,
conduit, gpg_key, synchronization_id_generator,
gid_generator, media_type=None, auth_required=0,
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
"""
......@@ -256,6 +267,8 @@ class SynchronizationTool( SubscriptionSynchronization,
pub.setAuthentication(auth_required)
pub.setAuthenticationFormat(authentication_format)
pub.setAuthenticationType(authentication_type)
pub.setSyncContentType(sync_content_type)
pub.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
if RESPONSE is not None:
RESPONSE.redirect('managePublications')
......@@ -265,7 +278,9 @@ class SynchronizationTool( SubscriptionSynchronization,
def manage_editSubscription(self, title, publication_url, subscription_url,
destination_path, source_uri, target_uri, query, xml_mapping, conduit,
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
"""
......@@ -286,6 +301,8 @@ class SynchronizationTool( SubscriptionSynchronization,
sub.setMediaType(media_type)
sub.setLogin(login)
sub.setPassword(password)
sub.setSyncContentType(sync_content_type)
sub.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
sub.setAlertCode(alert_code)
if RESPONSE is not None:
......@@ -833,7 +850,7 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declarePublic('sendResponse')
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
response, or just copy to a file.
......@@ -843,6 +860,12 @@ class SynchronizationTool( SubscriptionSynchronization,
#LOG('sendResponse, from_url: ',0,from_url)
#LOG('sendResponse, sync_id: ',0,sync_id)
#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):
xml = xml.encode('utf-8')
if domain is not None:
......@@ -878,7 +901,8 @@ class SynchronizationTool( SubscriptionSynchronization,
self.activate(activity='RAMQueue').sendHttpResponse(sync_id=sync_id,
to_url=to_url,
xml=xml,
domain_path=domain.getPath())
domain_path=domain.getPath(),
content_type=content_type)
elif to_url.find('file://')==0:
filename = to_url[len('file:/'):]
stream = file(filename,'w')
......@@ -895,10 +919,11 @@ class SynchronizationTool( SubscriptionSynchronization,
security.declarePrivate('sendHttpResponse')
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)
#LOG('sendHttpResponse, self.getPhysicalPath: ',0,self.getPhysicalPath())
#LOG('sendHttpResponse, starting with domain:',0,domain)
#LOG('sendHttpResponse, xml:',0,xml)
if domain is not None:
if domain.domain_type == self.PUB and not domain.getActivityEnabled():
......@@ -920,27 +945,46 @@ class SynchronizationTool( SubscriptionSynchronization,
urllib2.install_opener(opener)
to_encode = {}
head = '<?xml version="1.0" encoding="UTF-8"?>'
to_encode['text'] = head + xml
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['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
if to_url.find('readResponse')<0:
to_url = to_url + '/portal_synchronizations/readResponse'
encoded = urllib.urlencode(to_encode)
data=encoded
request = urllib2.Request(url=to_url, data=data)
#XXX only to synchronize with other server than erp5 (must be improved):
# data=head+xml
# request = urllib2.Request(to_url, data, headers)
# 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:
to_url = to_url + '/portal_synchronizations/readResponse'
encoded = urllib.urlencode(to_encode)
data=encoded
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)
try:
result = urllib2.urlopen(request).read()
url_file = urllib2.urlopen(request)
result = url_file.read()
except socket.error, msg:
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, url,data',0,(url, data))
#LOG('sendHttpResponse, url,data',0,(url, data))
return
except urllib2.URLError, msg:
LOG("sendHttpResponse, can't open url %s :" % to_url, 0, msg)
......@@ -961,7 +1005,9 @@ class SynchronizationTool( SubscriptionSynchronization,
#user = uf.getUserById('syncml').__of__(uf)
#newSecurityManager(None, user)
#self.activate(activity='RAMQueue').readResponse(sync_id=sync_id,text=result)
self.readResponse(sync_id=sync_id,text=result)
return result
security.declarePublic('sync')
def sync(self):
......@@ -986,6 +1032,8 @@ class SynchronizationTool( SubscriptionSynchronization,
response, or just copy to a file.
"""
#LOG('readResponse, text :', 0, text)
#LOG('readResponse, text :', 0, self.hexdump(text))
# Login as a manager to make sure we can create objects
uf = self.acl_users
user = uf.getUserById('syncml').__of__(uf)
......@@ -1001,10 +1049,12 @@ class SynchronizationTool( SubscriptionSynchronization,
for publication in self.getPublicationList():
if publication.getTitle()==sync_id:
gpg_key = publication.getGPGKey()
domain = publication
if gpg_key == '':
for subscription in self.getSubscriptionList():
if subscription.getTitle()==sync_id:
gpg_key = subscription.getGPGKey()
domain = subscription
# decrypt the message if needed
if gpg_key not in (None,''):
filename = str(random.randrange(1,2147483600)) + '.txt'
......@@ -1025,8 +1075,10 @@ class SynchronizationTool( SubscriptionSynchronization,
# Get the target and then find the corresponding publication or
# Subscription
#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)
#XXX this function is not very optimized and should be improved
url = self.getTarget(xml)
for publication in self.getPublicationList():
if publication.getPublicationUrl()==url and \
......@@ -1042,6 +1094,8 @@ class SynchronizationTool( SubscriptionSynchronization,
xml = result['xml']
#must be commented because this method is alredy called
#xml = self.sendResponse(xml=xml,domain=publication,send=0)
if publication.getSyncContentType() == self.CONTENT_TYPE['SYNCML_WBXML']:
xml = self.xml2wbxml(xml)
return xml
for subscription in self.getSubscriptionList():
......@@ -1096,4 +1150,41 @@ class SynchronizationTool( SubscriptionSynchronization,
conduit_object = getattr(conduit_module, conduit)()
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 )
......@@ -112,7 +112,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <LocURI>%s</LocURI>\n' % source)
xml(' </Source>\n')
xml(' <Meta>\n')
xml(' <Anchor xmlns=\'syncml:metinf\'>\n')
xml(' <Anchor>\n')
xml(' <Last>%s</Last>\n' % last_anchor)
xml(' <Next>%s</Next>\n' % next_anchor)
xml(' </Anchor>\n')
......@@ -122,30 +122,87 @@ class XMLSyncUtilsMixin(SyncCode):
xml_a = ''.join(xml_list)
return xml_a
def SyncMLStatus(self, cmd_id, target_ref, source_ref, sync_code,
next_anchor=None):
def SyncMLStatus(self, remote_xml, data_code, cmd_id, next_anchor,
subscription=None):
"""
Since the Status section is always almost the same, this is the
way to set one quickly.
return a status bloc with all status corresponding to the syncml
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 = xml_list.append
xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <TargetRef>%s</TargetRef>\n' % target_ref)
xml(' <SourceRef>%s</SourceRef>\n' % source_ref)
xml(' <Data>%s</Data>\n' % sync_code)
if next_anchor is not None:
xml(' <Item>\n')
xml(' <Data>\n')
xml(' <Anchor xmlns=\'syncml:metinf\'>\n')
xml(' <Next>%s</Next>\n' % next_anchor)
xml(' </Anchor>\n')
xml(' </Data>\n')
xml(' </Item>\n')
xml(' </Status>\n')
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(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1
xml(' <MsgRef>%s</MsgRef>\n' % self.getMessageId(remote_xml))
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'))
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(' <Data>\n')
xml(' <Anchor>\n')
xml(' <Next>%s</Next>\n' % next_anchor)
xml(' </Anchor>\n')
xml(' </Data>\n')
xml(' </Item>\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)
return xml_a
return {'xml':xml_a, 'cmd_id':cmd_id}
def SyncMLConfirmation(self, cmd_id=None, target_ref=None, cmd=None,
sync_code=None, msg_ref=None, cmd_ref=None, source_ref=None,
......@@ -191,6 +248,8 @@ class XMLSyncUtilsMixin(SyncCode):
xml = xml_list.append
xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <MsgRef>1</MsgRef>\n')
xml(' <CmdRef>0</CmdRef>\n')
xml(' <Cmd>%s</Cmd>\n' % cmd)
xml(' <TargetRef>%s</TargetRef>\n' % target_ref)
xml(' <SourceRef>%s</SourceRef>\n' % source_ref)
......@@ -205,9 +264,12 @@ class XMLSyncUtilsMixin(SyncCode):
xml_a = ''.join(xml_list)
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
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
# Import the conduit and get it
......@@ -222,65 +284,76 @@ class XMLSyncUtilsMixin(SyncCode):
globals(), locals(), [''])
conduit = getattr(conduit_module, conduit_name)()
#if the conduit support the SyncMLPut :
if hasattr(conduit, 'getCapabilitiesCTType') and \
if hasattr(conduit, 'getCapabilitiesCTTypeList') and \
hasattr(conduit, 'getCapabilitiesVerCTList') and \
hasattr(conduit, 'getPreferedCapabilitieVerCT'):
xml_list = []
xml = xml_list.append
if conduit.getCapabilitiesVerCTList() not in ([], None):
xml(' <Put>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <Meta>\n')
xml(' <Type>application/vnd.syncml-devinf+xml</Type>\n');
xml(' </Meta>\n')
xml(' <Item>\n')
xml(' <Source>\n')
xml(' <LocURI>./devinf11</LocURI>\n')
xml(' </Source>\n')
xml(' <Data>\n')
xml(' <DevInf>\n')
xml(' <VerDTD>1.1</VerDTD>\n')
xml(' <Man>Fabien MORIN</Man>\n')
xml(' <Mod>ERP5SyncML</Mod>\n')
xml(' <OEM>Open Source</OEM>\n')
xml(' <SwV>0.1</SwV>\n')
xml(' <DevID>%s</DevID>\n' % subscription.getSubscriptionUrl())
xml(' <DevTyp>workstation</DevTyp>\n')
xml(' <UTC/>\n')
xml(' <DataStore>\n')
xml(' <SourceRef>%s</SourceRef>\n' % subscription.getSourceURI())
xml(' <Rx-Pref>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
xml(' <VerCT>%s</VerCT>\n' % conduit.getPreferedCapabilitieVerCT())
xml(' </Rx-Pref>\n')
for rx_version in conduit.getCapabilitiesVerCTList():
xml(' <Rx>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
xml(' <VerCT>%s</VerCT>\n' % rx_version)
xml(' </Rx>\n')
xml(' <Tx-Pref>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
xml(' <VerCT>%s</VerCT>\n' % conduit.getPreferedCapabilitieVerCT())
xml(' </Tx-Pref>\n')
for tx_version in conduit.getCapabilitiesVerCTList():
xml(' <Tx>\n')
xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
xml(' <VerCT>%s</VerCT>\n' % rx_version)
xml(' </Tx>\n')
xml(' <SyncCap>\n')
xml(' <SyncType>2</SyncType>\n')
xml(' <SyncType>1</SyncType>\n')
xml(' <SyncType>4</SyncType>\n')
xml(' <SyncType>6</SyncType>\n')
xml(' </SyncCap>\n')
xml(' </DataStore>\n')
xml(' </DevInf>\n')
xml(' </Data>\n')
xml(' </Item>\n')
xml(' </Put>\n')
xml(' <%s>\n' % markup)
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(' <Type>application/vnd.syncml-devinf+xml</Type>\n');
xml(' </Meta>\n')
xml(' <Item>\n')
xml(' <Source>\n')
xml(' <LocURI>./devinf11</LocURI>\n')
xml(' </Source>\n')
xml(' <Data>\n')
xml(' <DevInf>\n')
xml(' <VerDTD>1.1</VerDTD>\n')
xml(' <Man>Nexedi</Man>\n')
xml(' <Mod>ERP5SyncML</Mod>\n')
xml(' <OEM>Open Source</OEM>\n')
xml(' <SwV>0.1</SwV>\n')
xml(' <DevID>%s</DevID>\n' % subscription.getSubscriptionUrl())
xml(' <DevTyp>workstation</DevTyp>\n')
xml(' <UTC/>\n')
xml(' <DataStore>\n')
xml(' <SourceRef>%s</SourceRef>\n' % subscription.getSourceURI())
xml(' <Rx-Pref>\n')
xml(' <CTType>%s</CTType>\n' % \
conduit.getPreferedCapabilitieCTType())
xml(' <VerCT>%s</VerCT>\n' % \
conduit.getPreferedCapabilitieVerCT())
xml(' </Rx-Pref>\n')
for type in conduit.getCapabilitiesCTTypeList():
if type != self.MEDIA_TYPE['TEXT_XML']:
for rx_version in conduit.getCapabilitiesVerCTList(type):
xml(' <Rx>\n')
xml(' <CTType>%s</CTType>\n' % type)
xml(' <VerCT>%s</VerCT>\n' % rx_version)
xml(' </Rx>\n')
xml(' <Tx-Pref>\n')
xml(' <CTType>%s</CTType>\n' % \
conduit.getPreferedCapabilitieCTType())
xml(' <VerCT>%s</VerCT>\n' % \
conduit.getPreferedCapabilitieVerCT())
xml(' </Tx-Pref>\n')
for type in conduit.getCapabilitiesCTTypeList():
if type != self.MEDIA_TYPE['TEXT_XML']:
for tx_version in conduit.getCapabilitiesVerCTList(type):
xml(' <Tx>\n')
xml(' <CTType>%s</CTType>\n' % type)
xml(' <VerCT>%s</VerCT>\n' % tx_version)
xml(' </Tx>\n')
xml(' <SyncCap>\n')
xml(' <SyncType>2</SyncType>\n')
xml(' <SyncType>1</SyncType>\n')
xml(' <SyncType>4</SyncType>\n')
xml(' <SyncType>6</SyncType>\n')
xml(' </SyncCap>\n')
xml(' </DataStore>\n')
xml(' </DevInf>\n')
xml(' </Data>\n')
xml(' </Item>\n')
xml(' </%s>\n' % markup)
xml_a = ''.join(xml_list)
return xml_a
return ''
......@@ -333,7 +406,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml_a = ''.join(xml_list)
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
"""
......@@ -342,9 +415,14 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <Delete>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
xml(' <Item>\n')
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % object_gid)
xml(' </Source>\n')
if rid not in (None, ''):
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % rid)
xml(' </Target>\n')
else:
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % object_gid)
xml(' </Source>\n')
#xml(' <Data>\n') #this 2 lines seems to be useless
#xml(' </Data>\n')
xml(' </Item>\n')
......@@ -353,7 +431,7 @@ class XMLSyncUtilsMixin(SyncCode):
return xml_a
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
"""
......@@ -365,9 +443,14 @@ class XMLSyncUtilsMixin(SyncCode):
xml(' <Type>%s</Type>\n' % media_type)
xml(' </Meta>\n')
xml(' <Item>\n')
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % str(gid))
xml(' </Source>\n')
if rid is not None:
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % str(rid))
xml(' </Target>\n')
else:
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % str(gid))
xml(' </Source>\n')
xml(' <Data>')
xml(xml_string)
xml(' </Data>\n')
......@@ -429,11 +512,7 @@ class XMLSyncUtilsMixin(SyncCode):
Return the value of the last anchor, in the
alert section of the xml_stream
"""
first_node = xml_stream.childNodes[0]
# Get informations from the body
client_body = first_node.childNodes[3]
last_anchor = client_body.xpath('string(/Alert/Item/Meta/Anchor/Last)')
last_anchor = xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Last)')
last_anchor = last_anchor.encode('utf-8')
return last_anchor
......@@ -442,17 +521,29 @@ class XMLSyncUtilsMixin(SyncCode):
Return the value of the next anchor, in the
alert section of the xml_stream
"""
first_node = xml_stream.childNodes[0]
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 = xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Next)')
next_anchor = next_anchor.encode('utf-8')
return next_anchor
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
"""
......@@ -541,6 +632,27 @@ class XMLSyncUtilsMixin(SyncCode):
return True
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):
"""
Return the value of the alert code inside the full syncml message
......@@ -688,7 +800,7 @@ class XMLSyncUtilsMixin(SyncCode):
if object is not None, this usually means we want to set the
actual xupdate on the signature.
"""
#LOG('\ngetSyncMLData starting...',0, domain.getId())
LOG('getSyncMLData starting...',0,'')
if isinstance(conduit, str):
conduit = self.getConduitByName(conduit)
local_gid_list = []
......@@ -714,10 +826,10 @@ class XMLSyncUtilsMixin(SyncCode):
if xml_object is not None: # This prevent to delete an object that
# we were not able to create
rid = signature.getRid()
if rid != None:
object_gid=rid #to use the remote id if it exist
#if rid != None:
# object_gid=rid #to use the remote id if it exist
syncml_data += self.deleteXMLObject(xml_object=signature.getXML()\
or '', object_gid=object_gid,cmd_id=cmd_id)
or '', object_gid=object_gid, rid=rid,cmd_id=cmd_id)
cmd_id += 1
#delete Signature if object does not exist anymore
for known_gid in subscriber.getGidList():
......@@ -726,7 +838,6 @@ class XMLSyncUtilsMixin(SyncCode):
local_gid_list = []
loop = 0
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:
result['finished'] = 0
break
......@@ -806,9 +917,12 @@ class XMLSyncUtilsMixin(SyncCode):
if not signature.checkMD5(xml_object):
set_synchronized = 0
# This object has changed on this side, we have to generate some xmldiff
xml_string = self.getXupdateObject(
if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_XML']:
xml_string = self.getXupdateObject(
domain.getXMLFromObject(object),
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:
# This make comment fails, so we need to replace
if xml_string.find('--') >= 0:
......@@ -828,11 +942,11 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setStatus(status)
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
xml_string = domain.getXMLFromObject(object)
gid = signature.getRid()#in fisrt, we try with rid if there is one
if gid == None:
gid = signature.getGid()
rid = signature.getRid()#in fisrt, we try with rid if there is one
gid = signature.getGid()
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,
media_type=subscriber.getMediaType())
cmd_id += 1
......@@ -884,11 +998,10 @@ class XMLSyncUtilsMixin(SyncCode):
xml_string = domain.getXMLFromObject(object)
#LOG('xml_string =', 0, xml_string)
if signature.getAction()=='Replace':
gid = signature.getRid()#in fisrt, we try with rid if there is one
if gid == None:
gid = signature.getGid()
rid = signature.getRid()#in fisrt, we try with rid if there is one
gid = signature.getGid()
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())
elif signature.getAction()=='Add':
gid = signature.getRid()#in fisrt, we try with rid if there is one
......@@ -918,7 +1031,10 @@ class XMLSyncUtilsMixin(SyncCode):
has_next_action = 0
destination = self.unrestrictedTraverse(domain.getDestinationPath())
#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):
if isinstance(action, unicode):
action = action.encode('utf-8')
conflict_list = []
status_code = self.SUCCESS
# Thirst we have to check the kind of action it is
......@@ -939,6 +1055,8 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setRid(rid)
#LOG('gid == rid ?', 0, 'gid=%s, rid=%s' % (gid, rid))
object = subscriber.getObjectFromGid(gid)
if object == None:
object = subscriber.getObjectFromRid(rid)
#LOG('applyActionList subscriber.getObjectFromGid %s' % gid,0,object)
if signature is None:
#LOG('applyActionList, signature is None',0,signature)
......@@ -1014,6 +1132,8 @@ class XMLSyncUtilsMixin(SyncCode):
if object is not None:
#LOG('SyncModif',0,'object: %s will be updated...' % object.id)
signature = subscriber.getSignatureFromGid(gid)
if signature is None:
signature = subscriber.getSignatureFromRid(gid)
#LOG('SyncModif',0,'previous signature: %s' % str(signature))
previous_xml = signature.getXML()
#LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
......@@ -1103,6 +1223,8 @@ class XMLSyncUtilsMixin(SyncCode):
# object_gid = status['target']
#else:
object_gid = status['source']
if object_gid in ('', None, []):
object_gid = status['target']
status_code = int(status['code'])
if status_cmd in ('Add','Replace'):
has_status_list = 1
......@@ -1126,7 +1248,8 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setStatus(self.SYNCHRONIZED)
elif status_cmd == 'Delete':
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:
subscriber.delSignature(signature.getGid())
return (destination_waiting_more_data, has_status_list)
......@@ -1177,20 +1300,20 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
"""
has_response = 0 #check if syncmodif replies to this messages
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]
# Get informations from the header
xml_header = first_node.childNodes[1]
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"
subscriber = domain # If we are the client, this is fine
simulate = 0 # used by applyActionList, should be 0 for client
if domain.domain_type == self.PUB:
simulate = 1
subscription_url = self.getSourceURI(xml_header)
subscription_url = self.getSubscriptionUrl(xml_header)
subscriber = domain.getSubscriber(subscription_url)
# We have to check if this message was not already, this can be dangerous
......@@ -1198,19 +1321,21 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
message_id = self.getMessageId(remote_xml)
correct_message = subscriber.checkCorrectRemoteMessageId(message_id)
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()
#LOG("last_xml :", 0, last_xml)
LOG("last_xml :", 0, last_xml)
if last_xml != '':
has_response = 1
if domain.domain_type == self.PUB: # We always reply
self.sendResponse(from_url=domain.publication_url,
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:
self.sendResponse(from_url=domain.subscription_url,
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}
subscriber.setLastSentMessage('')
......@@ -1250,60 +1375,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
# syncml body
xml(' <SyncBody>\n')
# status for SyncHdr
message_id = self.getMessageId(remote_xml)
xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1
xml(' <MsgRef>%s</MsgRef>\n' % message_id)
xml(' <CmdRef>0</CmdRef>\n') #to make reference to the SyncHdr, it's 0
xml(' <Cmd>SyncHdr</Cmd>\n')
xml(' <TargetRef>%s</TargetRef>\n' \
% remote_xml.xpath('string(//SyncHdr/Target/LocURI)').encode('utf-8'))
xml(' <SourceRef>%s</SourceRef>\n' \
% remote_xml.xpath('string(//SyncHdr/Source/LocURI)').encode('utf-8'))
xml(' <Data>200</Data>\n')
xml(' </Status>\n')
#list of element in the SyncBody bloc
syncbody_element_list = remote_xml.xpath('//SyncBody/*')
#add the status bloc corresponding to the receive command
for syncbody_element in syncbody_element_list:
if str(syncbody_element.nodeName) not in ('Status', 'Final', 'Replace'):
xml(' <Status>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id)
cmd_id += 1
xml(' <MsgRef>%s</MsgRef>\n' % message_id)
xml(' <CmdRef>%s</CmdRef>\n' \
% syncbody_element.xpath('string(.//CmdID)').encode('utf-8'))
xml(' <Cmd>%s</Cmd>\n' % syncbody_element.nodeName.encode('utf-8'))
target_ref = syncbody_element.xpath('string(.//Target/LocURI)').encode('utf-8')
if target_ref not in (None, ''):
xml(' <TargetRef>%s</TargetRef>\n' % target_ref )
source_ref = syncbody_element.xpath('string(.//Source/LocURI)').encode('utf-8')
if source_ref not in (None, ''):
xml(' <SourceRef>%s</SourceRef>\n' % source_ref )
#xml(' <Data>%s</Data>\n' % subscriber.getSynchronizationType())
if syncbody_element.nodeName.encode('utf-8') == 'Add':
xml(' <Data>%s</Data>\n' % '201')
else:
xml(' <Data>%s</Data>\n' % '200')
# if syncbody_element.xpath('.//Item') not in ([], None, '') and\
# syncbody_element.xpath('.//Item.....'): #contient une ancre Next...
xml(' <Item>\n')
xml(' <Data>\n')
xml(' <Anchor xmlns="syncml:metinf">\n')
xml(' <Next>%s</Next>\n' % subscriber.getNextAnchor())
xml(' </Anchor>\n')
xml(' </Data>\n')
xml(' </Item>\n')
xml(' </Status>\n')
xml_status, cmd_id = self.SyncMLStatus(remote_xml, self.SUCCESS, cmd_id,
subscriber.getNextAnchor(), subscription=subscriber).values()
xml(xml_status)
destination_url = ''
# alert message if we want more data
......@@ -1382,24 +1456,14 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
if syncml_data != '':
xml(' <Sync>\n')
xml(' <CmdID>%s</CmdID>\n' % cmd_id_before_getsyncmldata)
if domain.domain_type == self.SUB:
if subscriber.getTargetURI() not in ('', None):
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % subscriber.getTargetURI())
xml(' </Target>\n')
if subscriber.getSourceURI() not in ('', None):
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % subscriber.getSourceURI())
xml(' </Source>\n')
elif domain.domain_type == self.PUB:
if domain.getTargetURI() not in ('', None):
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % domain.getTargetURI())
xml(' </Target>\n')
if domain.getSourceURI() not in ('', None):
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % domain.getSourceURI())
xml(' </Source>\n')
if subscriber.getTargetURI() not in ('', None):
xml(' <Target>\n')
xml(' <LocURI>%s</LocURI>\n' % subscriber.getTargetURI())
xml(' </Target>\n')
if subscriber.getSourceURI() not in ('', None):
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % subscriber.getSourceURI())
xml(' </Source>\n')
xml(syncml_data)
xml(' </Sync>\n')
xml(xml_confirmation)
......@@ -1412,7 +1476,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
subscriber.setLastSentMessage(xml_a)
self.sendResponse(from_url=domain.publication_url,
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
elif domain.domain_type == self.SUB:
if self.checkAlert(remote_xml) or \
......@@ -1421,6 +1486,37 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
subscriber.setLastSentMessage(xml_a)
self.sendResponse(from_url=domain.subscription_url,
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
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.
<b>Query :</b> the type of objects you want to synchronize</br>
<b>XML Mapping :</b> the page template used on each object before
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>GPG key name :</b>a name of gpg key to use</br>
<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.
<b>Query :</b> the type of objects you want to synchronize</br>
<b>XML Mapping :</b> the page template used on each object before
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>GPG key name :</b>a name of gpg key to use</br>
<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.
<input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
</td>
</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>
<td align="left" valign="top">
<div class="form-label">
......
......@@ -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" />
</td>
</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>
<td align="left" valign="top">
<div class="form-label">
......
......@@ -103,6 +103,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="xml_mapping" size="40" />
</td>
</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>
<td align="left" valign="top">
<div class="form-label">
......
......@@ -140,6 +140,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="xml_mapping" size="40" />
</td>
</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>
<td align="left" valign="top">
<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