Commit aa1b9145 authored by Jérome Perrin's avatar Jérome Perrin

SyncML python3 support

See merge request !1871
parents db5ed350 303dc677
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>text</string> </key> <key> <string>text</string> </key>
<value> <string>string:${object_url}/SyncMLDocument_updateURLToCurrentSite</string> </value> <value> <string>string:${object_url}/SyncMLSubscription_updateURLToCurrentSite</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>text</string> </key> <key> <string>text</string> </key>
<value> <string>string:${object_url}/SyncMLDocument_updateURLToCurrentSite</string> </value> <value> <string>string:${object_url}/SyncMLSubscription_updateURLToCurrentSite</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
############################################################################## ##############################################################################
from hashlib import md5 from hashlib import md5
import six
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
...@@ -98,7 +99,8 @@ class SyncMLSignature(XMLObject): ...@@ -98,7 +99,8 @@ class SyncMLSignature(XMLObject):
Set the XML corresponding to the object Set the XML corresponding to the object
""" """
if value: if value:
# convert the string to Pdata assert isinstance(value, bytes)
# convert the bytes to Pdata
pdata_wrapper = PdataHelper(self.getPortalObject(), value) pdata_wrapper = PdataHelper(self.getPortalObject(), value)
self._setData(pdata_wrapper) self._setData(pdata_wrapper)
self.setTemporaryData(None) # We make sure that the data will not be erased self.setTemporaryData(None) # We make sure that the data will not be erased
...@@ -113,7 +115,7 @@ class SyncMLSignature(XMLObject): ...@@ -113,7 +115,7 @@ class SyncMLSignature(XMLObject):
Get the XML corresponding to the object Get the XML corresponding to the object
""" """
if self.hasData(): if self.hasData():
return str(self._baseGetData()) return bytes(self._baseGetData())
elif default is _MARKER: elif default is _MARKER:
return self._baseGetData() return self._baseGetData()
else: else:
...@@ -139,7 +141,7 @@ class SyncMLSignature(XMLObject): ...@@ -139,7 +141,7 @@ class SyncMLSignature(XMLObject):
Return the temp xml as string Return the temp xml as string
""" """
if self.hasTemporaryData(): if self.hasTemporaryData():
return str(self._baseGetTemporaryData()) return bytes(self._baseGetTemporaryData())
elif default is _MARKER: elif default is _MARKER:
return self._baseGetTemporaryData() return self._baseGetTemporaryData()
else: else:
...@@ -153,7 +155,7 @@ class SyncMLSignature(XMLObject): ...@@ -153,7 +155,7 @@ class SyncMLSignature(XMLObject):
changed or not changed or not
Returns 1 if MD5 are equals, else it returns 0 Returns 1 if MD5 are equals, else it returns 0
""" """
if isinstance(xml_string, unicode): if six.PY2 and isinstance(xml_string, six.text_type):
xml_string = xml_string.encode('utf-8') xml_string = xml_string.encode('utf-8')
return md5(xml_string).hexdigest() == self.getContentMd5() return md5(xml_string).hexdigest() == self.getContentMd5()
...@@ -189,7 +191,7 @@ class SyncMLSignature(XMLObject): ...@@ -189,7 +191,7 @@ class SyncMLSignature(XMLObject):
Return the patial xml as string Return the patial xml as string
""" """
if self.hasPartialData(): if self.hasPartialData():
return str(self._baseGetPartialData()) return bytes(self._baseGetPartialData())
elif default is _MARKER: elif default is _MARKER:
return self._baseGetPartialData() return self._baseGetPartialData()
else: else:
...@@ -223,7 +225,7 @@ class SyncMLSignature(XMLObject): ...@@ -223,7 +225,7 @@ class SyncMLSignature(XMLObject):
rest_in_queue = partial_data[max_len:] rest_in_queue = partial_data[max_len:]
if rest_in_queue is not None: if rest_in_queue is not None:
self.setPartialData(rest_in_queue) self.setPartialData(rest_in_queue)
return str(chunk) return bytes(chunk)
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'setSubscriberXupdate') 'setSubscriberXupdate')
...@@ -244,7 +246,7 @@ class SyncMLSignature(XMLObject): ...@@ -244,7 +246,7 @@ class SyncMLSignature(XMLObject):
Return the patial xml as string Return the patial xml as string
""" """
if self.hasSubscriberXupdate(): if self.hasSubscriberXupdate():
return str(self._baseGetSubscriberXupdate()) return bytes(self._baseGetSubscriberXupdate())
elif default is _MARKER: elif default is _MARKER:
return self._baseGetSubscriberXupdate() return self._baseGetSubscriberXupdate()
else: else:
...@@ -269,7 +271,7 @@ class SyncMLSignature(XMLObject): ...@@ -269,7 +271,7 @@ class SyncMLSignature(XMLObject):
Return the partial xml as string Return the partial xml as string
""" """
if self.hasPublisherXupdate(): if self.hasPublisherXupdate():
return str(self._baseGetPublisherXupdate()) return bytes(self._baseGetPublisherXupdate())
elif default is _MARKER: elif default is _MARKER:
return self._baseGetPublisherXupdate() return self._baseGetPublisherXupdate()
else: else:
......
...@@ -29,9 +29,10 @@ ...@@ -29,9 +29,10 @@
from base64 import b16encode, b16decode from base64 import b16encode, b16decode
from logging import getLogger from logging import getLogger
from urlparse import urlparse from six.moves.urllib.parse import urlparse
from lxml import etree from lxml import etree
from copy import deepcopy from copy import deepcopy
import six
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
...@@ -53,6 +54,7 @@ from erp5.component.module.SyncMLTransportERP5 import ERP5Transport ...@@ -53,6 +54,7 @@ from erp5.component.module.SyncMLTransportERP5 import ERP5Transport
from erp5.component.module.SyncMLConstant import MAX_LEN, ADD_ACTION, \ from erp5.component.module.SyncMLConstant import MAX_LEN, ADD_ACTION, \
REPLACE_ACTION REPLACE_ACTION
from erp5.component.module.XMLSyncUtils import cutXML from erp5.component.module.XMLSyncUtils import cutXML
from six.moves import range
transport_scheme_dict = { transport_scheme_dict = {
"http" : HTTPTransport(), "http" : HTTPTransport(),
...@@ -160,7 +162,7 @@ class SyncMLSubscription(XMLObject): ...@@ -160,7 +162,7 @@ class SyncMLSubscription(XMLObject):
activate = self.activate activate = self.activate
callback_method = getattr(activate(**activate_kw), callback) callback_method = getattr(activate(**activate_kw), callback)
if generated_other_activity: if generated_other_activity:
for i in xrange(0, result_count, packet_size): for i in range(0, result_count, packet_size):
syncml_logger.info("-- getAndIndex : recursive call, generating for %s", syncml_logger.info("-- getAndIndex : recursive call, generating for %s",
r[i:i+packet_size]) r[i:i+packet_size])
callback_method(path_list=r[i:i+packet_size], callback_method(path_list=r[i:i+packet_size],
...@@ -168,7 +170,7 @@ class SyncMLSubscription(XMLObject): ...@@ -168,7 +170,7 @@ class SyncMLSubscription(XMLObject):
**method_kw) **method_kw)
else: else:
if result_count > packet_size and limit: if result_count > packet_size and limit:
for i in xrange(0, result_count-packet_size, packet_size): for i in range(0, result_count-packet_size, packet_size):
syncml_logger.info("-- getAndIndex : i %s, call, generating for %s : %s", syncml_logger.info("-- getAndIndex : i %s, call, generating for %s : %s",
i, r[i:i+packet_size], activate_kw) i, r[i:i+packet_size], activate_kw)
callback_method(path_list=r[i:i+packet_size], callback_method(path_list=r[i:i+packet_size],
...@@ -205,7 +207,7 @@ class SyncMLSubscription(XMLObject): ...@@ -205,7 +207,7 @@ class SyncMLSubscription(XMLObject):
Return the path of the subscription that will be used in sql table Return the path of the subscription that will be used in sql table
_ char must be escaped because of the LIKE behaviour _ char must be escaped because of the LIKE behaviour
""" """
return "%s/%%" % (self.getSourceValue().getPath().replace("_","\_"),) # pylint: disable=anomalous-backslash-in-string return "%s/%%" % (self.getSourceValue().getPath().replace("_", r"\_"),)
security.declarePrivate('sendSyncCommand') security.declarePrivate('sendSyncCommand')
def sendSyncCommand(self, min_gid, max_gid, message_id, activate_kw): def sendSyncCommand(self, min_gid, max_gid, message_id, activate_kw):
...@@ -225,7 +227,7 @@ class SyncMLSubscription(XMLObject): ...@@ -225,7 +227,7 @@ class SyncMLSubscription(XMLObject):
# transport failure # transport failure
# activate_kw["group_method_id"] = None # activate_kw["group_method_id"] = None
# activate_kw["group_method_cost"] = .05 # activate_kw["group_method_cost"] = .05
self.activate(**activate_kw).sendMessage(xml=str(syncml_response)) self.activate(**activate_kw).sendMessage(xml=bytes(syncml_response))
security.declarePrivate('applySyncCommand') security.declarePrivate('applySyncCommand')
def applySyncCommand(self, response_message_id, activate_kw, **kw): def applySyncCommand(self, response_message_id, activate_kw, **kw):
...@@ -250,7 +252,7 @@ class SyncMLSubscription(XMLObject): ...@@ -250,7 +252,7 @@ class SyncMLSubscription(XMLObject):
self.activate(activity="SQLQueue", self.activate(activity="SQLQueue",
# group_method_id=None, # group_method_id=None,
# group_method_cost=.05, # group_method_cost=.05,
tag=activate_kw).sendMessage(xml=str(syncml_response)) tag=activate_kw).sendMessage(xml=bytes(syncml_response))
security.declarePrivate('getAndActivate') security.declarePrivate('getAndActivate')
...@@ -310,7 +312,7 @@ class SyncMLSubscription(XMLObject): ...@@ -310,7 +312,7 @@ class SyncMLSubscription(XMLObject):
if generated_other_activity: if generated_other_activity:
# XXX Can be factorized with following code # XXX Can be factorized with following code
# upper_limit of xrange + some check ??? # upper_limit of xrange + some check ???
for i in xrange(0, result_count, packet_size): for i in range(0, result_count, packet_size):
if first_call: if first_call:
min_gid = None min_gid = None
first_call = False first_call = False
...@@ -330,7 +332,7 @@ class SyncMLSubscription(XMLObject): ...@@ -330,7 +332,7 @@ class SyncMLSubscription(XMLObject):
else: else:
i = 0 i = 0
if result_count > packet_size: if result_count > packet_size:
for i in xrange(0, result_count-packet_size, packet_size): for i in range(0, result_count-packet_size, packet_size):
if first_call: if first_call:
min_gid = None min_gid = None
first_call = False first_call = False
...@@ -531,7 +533,7 @@ class SyncMLSubscription(XMLObject): ...@@ -531,7 +533,7 @@ class SyncMLSubscription(XMLObject):
domain=self)) domain=self))
xml_document = incoming_data xml_document = incoming_data
if not isinstance(xml_document, basestring): if not isinstance(xml_document, bytes):
# XXX using deepcopy to remove parent link - must be done elsewhere # XXX using deepcopy to remove parent link - must be done elsewhere
xml_document = deepcopy(xml_document) xml_document = deepcopy(xml_document)
# Remove useless namespace # Remove useless namespace
...@@ -539,7 +541,7 @@ class SyncMLSubscription(XMLObject): ...@@ -539,7 +541,7 @@ class SyncMLSubscription(XMLObject):
xml_document = etree.tostring(xml_document, encoding='utf-8', xml_document = etree.tostring(xml_document, encoding='utf-8',
pretty_print=True) pretty_print=True)
if isinstance(xml_document, unicode): if six.PY2 and isinstance(xml_document, unicode):
xml_document = xml_document.encode('utf-8') xml_document = xml_document.encode('utf-8')
# Link the signature to the document # Link the signature to the document
if signature: if signature:
...@@ -603,12 +605,12 @@ class SyncMLSubscription(XMLObject): ...@@ -603,12 +605,12 @@ class SyncMLSubscription(XMLObject):
signature.changeToConflict() signature.changeToConflict()
# Register the data received which generated the diff # Register the data received which generated the diff
# XXX Why ? # XXX Why ?
if not isinstance(incoming_data, basestring): if not isinstance(incoming_data, bytes):
incoming_data = etree.tostring(incoming_data, incoming_data = etree.tostring(incoming_data,
encoding='utf-8') encoding='utf-8')
signature.setPartialData(incoming_data) signature.setPartialData(incoming_data)
else: else:
signature.setData(str(xml_document)) signature.setData(bytes(xml_document))
signature.synchronize() signature.synchronize()
syncml_logger.info("change state of signature to %s with %s", syncml_logger.info("change state of signature to %s with %s",
signature.getValidationState(), signature.getData()) signature.getValidationState(), signature.getData())
...@@ -649,7 +651,7 @@ class SyncMLSubscription(XMLObject): ...@@ -649,7 +651,7 @@ class SyncMLSubscription(XMLObject):
next_anchor=self.getNextAnchor()) next_anchor=self.getNextAnchor())
# Index signature with their new value # Index signature with their new value
if len(path_list): if len(path_list):
self.SQLCatalog_indexSyncMLDocumentList(path_list) self.ERP5Site_indexSyncMLDocumentList(path_list)
def _sendFinalMessage(self): def _sendFinalMessage(self):
""" """
...@@ -667,7 +669,7 @@ class SyncMLSubscription(XMLObject): ...@@ -667,7 +669,7 @@ class SyncMLSubscription(XMLObject):
} }
syncml_logger.info("Sending final message for modificationson on %s", syncml_logger.info("Sending final message for modificationson on %s",
self.getRelativeUrl()) self.getRelativeUrl())
self.activate(**final_activate_kw).sendMessage(xml=str(syncml_response)) self.activate(**final_activate_kw).sendMessage(xml=bytes(syncml_response))
security.declarePrivate('getDeletedSyncMLData') security.declarePrivate('getDeletedSyncMLData')
...@@ -700,11 +702,10 @@ class SyncMLSubscription(XMLObject): ...@@ -700,11 +702,10 @@ class SyncMLSubscription(XMLObject):
} }
syncml_logger.info("Sending final message for modificationson on %s", syncml_logger.info("Sending final message for modificationson on %s",
self.getRelativeUrl()) self.getRelativeUrl())
self.activate(**final_activate_kw).sendMessage(xml=str(syncml_response)) self.activate(**final_activate_kw).sendMessage(xml=bytes(syncml_response))
def getSearchablePath(self): def getSearchablePath(self):
return "%s%%" %(self.getPath().replace('_', '\_'),) # pylint: disable=anomalous-backslash-in-string return "%s%%" %(self.getPath().replace('_', r'\_'),)
def _generateSyncCommand(self, action, signature, data_diff ,document_data, gid, def _generateSyncCommand(self, action, signature, data_diff ,document_data, gid,
conduit, syncml_response): conduit, syncml_response):
...@@ -973,10 +974,12 @@ class SyncMLSubscription(XMLObject): ...@@ -973,10 +974,12 @@ class SyncMLSubscription(XMLObject):
# old way using the conduit # old way using the conduit
conduit = self.getConduit() conduit = self.getConduit()
raw_gid = conduit.getGidFromObject(object) raw_gid = conduit.getGidFromObject(object)
if isinstance(raw_gid, unicode): if isinstance(raw_gid, six.text_type):
raw_gid = raw_gid.encode('ascii', 'ignore') raw_gid = raw_gid.encode('ascii', 'ignore')
if encoded: if encoded:
gid = b16encode(raw_gid) gid = b16encode(raw_gid)
if six.PY3:
gid = gid.decode()
else: else:
gid = raw_gid gid = raw_gid
return gid return gid
...@@ -1146,7 +1149,7 @@ class SyncMLSubscription(XMLObject): ...@@ -1146,7 +1149,7 @@ class SyncMLSubscription(XMLObject):
""" """
object_id_list = list(self.getObjectIds()) object_id_list = list(self.getObjectIds())
object_list_len = len(object_id_list) object_list_len = len(object_id_list)
for i in xrange(0, object_list_len, MAX_OBJECTS): for i in range(0, object_list_len, MAX_OBJECTS):
current_id_list = object_id_list[i:i+MAX_OBJECTS] current_id_list = object_id_list[i:i+MAX_OBJECTS]
self.activate(activity='SQLQueue', self.activate(activity='SQLQueue',
priority=ACTIVITY_PRIORITY).manage_delObjects(current_id_list) priority=ACTIVITY_PRIORITY).manage_delObjects(current_id_list)
...@@ -1196,7 +1199,7 @@ class SyncMLSubscription(XMLObject): ...@@ -1196,7 +1199,7 @@ class SyncMLSubscription(XMLObject):
else: else:
kw = {} kw = {}
self.getAndIndex( self.getAndIndex(
callback="SQLCatalog_indexSyncMLDocumentList", callback="ERP5Site_indexSyncMLDocumentList",
method_kw={'subscription_path' : self.getRelativeUrl()}, method_kw={'subscription_path' : self.getRelativeUrl()},
activate_kw=activate_kw, activate_kw=activate_kw,
**kw **kw
...@@ -1204,7 +1207,7 @@ class SyncMLSubscription(XMLObject): ...@@ -1204,7 +1207,7 @@ class SyncMLSubscription(XMLObject):
else: else:
r = [x.getPath() for x in self.getDocumentList()] r = [x.getPath() for x in self.getDocumentList()]
syncml_logger.info("indexing data from %s : %r", self.getPath(), r) syncml_logger.info("indexing data from %s : %r", self.getPath(), r)
portal.SQLCatalog_indexSyncMLDocumentList( portal.ERP5Site_indexSyncMLDocumentList(
path_list=r[:], path_list=r[:],
subscription_path=self.getRelativeUrl()) subscription_path=self.getRelativeUrl())
......
...@@ -25,12 +25,13 @@ ...@@ -25,12 +25,13 @@
# #
############################################################################## ##############################################################################
import base64 import base64
import six
from lxml import etree from lxml import etree
from difflib import unified_diff from difflib import unified_diff
from erp5.component.module.DiffUtils import DiffFile from erp5.component.module.DiffUtils import DiffFile
def diffXML(xml_plugin="", xml_erp5="", gid="", html=True): def diffXML(xml_plugin="", xml_erp5="", gid="", html=True):
if isinstance(xml_erp5, unicode): if isinstance(xml_erp5, six.text_type):
xml_erp5 = xml_erp5.encode('utf-8') xml_erp5 = xml_erp5.encode('utf-8')
if xml_plugin == "": if xml_plugin == "":
xml_plugin="<object>Not found</object>" xml_plugin="<object>Not found</object>"
...@@ -39,15 +40,15 @@ def diffXML(xml_plugin="", xml_erp5="", gid="", html=True): ...@@ -39,15 +40,15 @@ def diffXML(xml_plugin="", xml_erp5="", gid="", html=True):
try: try:
xml = etree.fromstring(xml_erp5) xml = etree.fromstring(xml_erp5)
xml_erp5 = etree.tostring(xml, pretty_print=True, encoding="utf-8") xml_erp5 = etree.tostring(xml, pretty_print=True, encoding="utf-8")
except etree.XMLSyntaxError: except etree.XMLSyntaxError: # pylint:disable=catching-non-exception
pass pass
if isinstance(xml_plugin, unicode): if isinstance(xml_plugin, six.text_type):
xml_plugin = xml_plugin.encode('utf-8') xml_plugin = xml_plugin.encode('utf-8')
try: try:
xml = etree.fromstring(xml_plugin) xml = etree.fromstring(xml_plugin)
xml_plugin = etree.tostring(xml, pretty_print=True, encoding="utf-8") xml_plugin = etree.tostring(xml, pretty_print=True, encoding="utf-8")
except etree.XMLSyntaxError: except etree.XMLSyntaxError: # pylint:disable=catching-non-exception
pass pass
diff_list = list(unified_diff(xml_plugin.split('\n'), xml_erp5.split('\n'), tofile="erp5 xml", fromfile="plugin xml", lineterm='')) diff_list = list(unified_diff(xml_plugin.split('\n'), xml_erp5.split('\n'), tofile="erp5 xml", fromfile="plugin xml", lineterm=''))
......
...@@ -74,35 +74,6 @@ ...@@ -74,35 +74,6 @@
<key> <string>action</string> </key> <key> <string>action</string> </key>
<value> <string>validate</string> </value> <value> <string>validate</string> </value>
</item> </item>
<item>
<key> <string>actor</string> </key>
<value> <string>ERP5TypeTestCase</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1377844521.87</float>
<string>GMT+9</string>
</tuple>
</state>
</object>
</value>
</item>
<item> <item>
<key> <string>validation_state</string> </key> <key> <string>validation_state</string> </key>
<value> <string>validated</string> </value> <value> <string>validated</string> </value>
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
# #
############################################################################## ##############################################################################
import six
from logging import getLogger from logging import getLogger
from AccessControl import ClassSecurityInfo, getSecurityManager from AccessControl import ClassSecurityInfo, getSecurityManager
...@@ -155,7 +156,7 @@ class SyncMLEngineMixin(object): ...@@ -155,7 +156,7 @@ class SyncMLEngineMixin(object):
else: else:
raise ValueError("Unknown status command : %r" % (status['command'],)) raise ValueError("Unknown status command : %r" % (status['command'],))
if len(path_list): if len(path_list):
domain.SQLCatalog_indexSyncMLDocumentList(path_list) domain.ERP5Site_indexSyncMLDocumentList(path_list)
return sync_status_counter return sync_status_counter
# #
...@@ -337,8 +338,11 @@ class SyncMLEngineMixin(object): ...@@ -337,8 +338,11 @@ class SyncMLEngineMixin(object):
if syncml_request.credentials['type'] == publication.getAuthenticationType(): if syncml_request.credentials['type'] == publication.getAuthenticationType():
decoded = decode(syncml_request.credentials['format'], decoded = decode(syncml_request.credentials['format'],
syncml_request.credentials['data']) syncml_request.credentials['data'])
if decoded and ':' in decoded: if decoded and b':' in decoded:
login, password = decoded.split(':') login, password = decoded.split(b':')
if six.PY3:
login = login.decode()
password = password.decode()
# TODO: make it work for users existing anywhere # TODO: make it work for users existing anywhere
user_folder = publication.getPortalObject().acl_users user_folder = publication.getPortalObject().acl_users
for _, plugin in user_folder._getOb('plugins')\ for _, plugin in user_folder._getOb('plugins')\
...@@ -463,8 +467,8 @@ class SyncMLEngineMixin(object): ...@@ -463,8 +467,8 @@ class SyncMLEngineMixin(object):
# Wait for all reset to be done # Wait for all reset to be done
# before starting sync # before starting sync
priority=ACTIVITY_PRIORITY, priority=ACTIVITY_PRIORITY,
tag=publication.getRelativeUrl()).sendMessage(xml=str(syncml_response)) tag=publication.getRelativeUrl()).sendMessage(xml=bytes(syncml_response))
else: else:
subscriber.sendMessage(xml=str(syncml_response)) subscriber.sendMessage(xml=bytes(syncml_response))
return syncml_response return syncml_response
...@@ -42,12 +42,14 @@ from xml.sax.saxutils import unescape ...@@ -42,12 +42,14 @@ from xml.sax.saxutils import unescape
import re import re
from lxml import etree from lxml import etree
from lxml.etree import Element from lxml.etree import Element
import six
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
from xml_marshaller.xml_marshaller import Unmarshaller from xml_marshaller.xml_marshaller import Unmarshaller
from xupdate_processor import xuproc from xupdate_processor import xuproc
from base64 import standard_b64decode from base64 import standard_b64decode
from zope.interface import implementer from zope.interface import implementer
from copy import deepcopy from copy import deepcopy
from six import string_types as basestring
import logging import logging
syncml_logger = logging.getLogger('ERP5SyncML') syncml_logger = logging.getLogger('ERP5SyncML')
...@@ -229,10 +231,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -229,10 +231,10 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# /erp5/object[@gid='313730']/../workflow_action[@id=SHA(TIME + ACTOR)] # /erp5/object[@gid='313730']/../workflow_action[@id=SHA(TIME + ACTOR)]
wf_action_id = EXTRACT_ID_FROM_XPATH.findall(xpath_expression)[-1][-1] wf_action_id = EXTRACT_ID_FROM_XPATH.findall(xpath_expression)[-1][-1]
def deleteWorkflowNode(): def deleteWorkflowNode():
for wf_id, wf_history_tuple in object.workflow_history.iteritems(): for wf_id, wf_history_tuple in six.iteritems(object.workflow_history):
for wf_history_index, wf_history in enumerate(wf_history_tuple): for wf_history_index, wf_history in enumerate(wf_history_tuple):
if sha1(wf_id + str(wf_history['time']) + if sha1((wf_id + str(wf_history['time']) +
wf_history['actor']).hexdigest() == wf_action_id: wf_history['actor']).encode('utf-8')).hexdigest() == wf_action_id:
object.workflow_history[wf_id] = ( object.workflow_history[wf_id] = (
object.workflow_history[wf_id][:wf_history_index] + object.workflow_history[wf_id][:wf_history_index] +
object.workflow_history[wf_id][wf_history_index + 1:]) object.workflow_history[wf_id][wf_history_index + 1:])
...@@ -426,21 +428,21 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -426,21 +428,21 @@ class ERP5Conduit(XMLSyncUtilsMixin):
def getFormatedArgs(self, args=None): def getFormatedArgs(self, args=None):
""" """
This lookd inside the args dictionnary and then This lookd inside the args dictionnary and then
convert any unicode string to string convert any unicode string to string ( on python 2 )
""" """
new_args = {} new_args = {}
for keyword in args.keys(): for keyword in args.keys():
data = args[keyword] data = args[keyword]
if isinstance(keyword, unicode): if six.PY2 and isinstance(keyword, six.text_type):
keyword = keyword.encode(self.getEncoding()) keyword = keyword.encode(self.getEncoding())
if isinstance(data, (tuple, list)): if isinstance(data, (tuple, list)):
new_data = [] new_data = []
for item in data: for item in data:
if isinstance(item, unicode): if six.PY2 and isinstance(item, six.text_type):
item = item.encode(self.getEncoding()) item = item.encode(self.getEncoding())
new_data.append(item) new_data.append(item)
data = new_data data = new_data
if isinstance(data, unicode): if six.PY2 and isinstance(data, six.text_type):
data = data.encode(self.getEncoding()) data = data.encode(self.getEncoding())
new_args[keyword] = data new_args[keyword] = data
return new_args return new_args
...@@ -561,7 +563,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -561,7 +563,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
XXX name of method is not good, because content is not necessarily XML XXX name of method is not good, because content is not necessarily XML
return a xml with id replaced by a new id return a xml with id replaced by a new id
""" """
if isinstance(xml, str): if isinstance(xml, bytes):
xml = etree.XML(xml, parser=parser) xml = etree.XML(xml, parser=parser)
else: else:
# copy of xml object for modification # copy of xml object for modification
...@@ -623,9 +625,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -623,9 +625,10 @@ class ERP5Conduit(XMLSyncUtilsMixin):
""" """
if xml is a string, convert it to a node if xml is a string, convert it to a node
""" """
if xml is None: return None if xml is None:
if isinstance(xml, (str, unicode)): return None
if isinstance(xml, unicode): if isinstance(xml, six.string_types + (bytes, )):
if six.PY2 and isinstance(xml, six.text_type):
xml = xml.encode('utf-8') xml = xml.encode('utf-8')
xml = etree.XML(xml, parser=parser) xml = etree.XML(xml, parser=parser)
# If we have the xml from the node erp5, we just take the subnode # If we have the xml from the node erp5, we just take the subnode
...@@ -778,7 +781,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -778,7 +781,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if data_type == NONE_TYPE: if data_type == NONE_TYPE:
return None return None
data = node.text data = node.text
if data is not None and isinstance(data, unicode): if data is not None and six.PY2 and isinstance(data, six.text_type):
data = data.encode('utf-8') data = data.encode('utf-8')
elif data is None and data_type in TEXT_TYPE_LIST: elif data is None and data_type in TEXT_TYPE_LIST:
return '' return ''
...@@ -797,7 +800,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -797,7 +800,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
elif data_type in DATA_TYPE_LIST: elif data_type in DATA_TYPE_LIST:
if data is None: if data is None:
# data is splitted inside block_data nodes # data is splitted inside block_data nodes
data = ''.join([standard_b64decode(block.text) for\ data = b''.join([standard_b64decode(block.text) for\
block in node.iterchildren()]) block in node.iterchildren()])
elif data_type == DATE_TYPE: elif data_type == DATE_TYPE:
data = DateTime(data) data = DateTime(data)
...@@ -812,7 +815,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -812,7 +815,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
Parse the xupdate and then it will call the conduit Parse the xupdate and then it will call the conduit
""" """
conflict_list = [] conflict_list = []
if isinstance(xupdate, (str, unicode)): if isinstance(xupdate, six.string_types + (bytes, )):
xupdate = etree.XML(xupdate, parser=parser) xupdate = etree.XML(xupdate, parser=parser)
#LOG("applyXupdate", INFO, etree.tostring(xupdate, pretty_print=True)) #LOG("applyXupdate", INFO, etree.tostring(xupdate, pretty_print=True))
xupdate_builded = False xupdate_builded = False
...@@ -830,14 +833,16 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -830,14 +833,16 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xupdate_builded = True xupdate_builded = True
# Find the prefix used by marshaller. # Find the prefix used by marshaller.
for prefix, namespace_uri in subnode.nsmap.iteritems(): for prefix, namespace_uri in six.iteritems(subnode.nsmap):
if namespace_uri == MARSHALLER_NAMESPACE_URI: if namespace_uri == MARSHALLER_NAMESPACE_URI:
break break
# TODO add support of etree objects for xuproc to avoid # TODO add support of etree objects for xuproc to avoid
# serializing tree into string # serializing tree into string
if not isinstance(previous_xml, str): if isinstance(previous_xml, bytes):
previous_xml = etree.tostring(previous_xml) previous_xml = previous_xml.decode('utf-8')
xupdated_tree = xuproc.applyXUpdate(xml_xu_string=etree.tostring(xupdate), if not isinstance(previous_xml, six.text_type):
previous_xml = etree.tostring(previous_xml, encoding='unicode')
xupdated_tree = xuproc.applyXUpdate(xml_xu_string=etree.tostring(xupdate, encoding='unicode'),
xml_doc_string=previous_xml) xml_doc_string=previous_xml)
if MARSHALLER_NAMESPACE_URI in subnode.nsmap.values(): if MARSHALLER_NAMESPACE_URI in subnode.nsmap.values():
xpath_expression = original_xpath_expression xpath_expression = original_xpath_expression
...@@ -888,7 +893,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -888,7 +893,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
previous_xml=previous_xml, **kw) previous_xml=previous_xml, **kw)
# Now apply collected xupdated_node # Now apply collected xupdated_node
for update_dict in xpath_expression_update_dict.itervalues(): for update_dict in six.itervalues(xpath_expression_update_dict):
update_dict.update(kw) update_dict.update(kw)
conflict_list += self.updateNode(previous_xml=previous_xml, conflict_list += self.updateNode(previous_xml=previous_xml,
**update_dict) **update_dict)
...@@ -913,7 +918,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -913,7 +918,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if time <= action.get('time'): if time <= action.get('time'):
# action in the past are not appended # action in the past are not appended
addable = WORKFLOW_ACTION_INSERTABLE addable = WORKFLOW_ACTION_INSERTABLE
key_list = action.keys() key_list = list(action.keys())
key_list.remove("time") key_list.remove("time")
for key in key_list: for key in key_list:
if status[key] != action[key]: if status[key] != action[key]:
...@@ -1100,8 +1105,12 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -1100,8 +1105,12 @@ class ERP5Conduit(XMLSyncUtilsMixin):
""" """
# XXX xuproc does not support passing # XXX xuproc does not support passing
# etree objetcs # etree objetcs
if not isinstance(diff, basestring): if isinstance(diff, bytes):
diff = etree.tostring(diff) diff = diff.decode('utf-8')
elif not isinstance(diff, basestring):
diff = etree.tostring(diff, encoding='unicode')
if not isinstance(original_data, six.text_type):
original_data = six.text_type(original_data, 'utf-8')
return etree.tostring(xuproc.applyXUpdate(xml_xu_string=diff, return etree.tostring(xuproc.applyXUpdate(xml_xu_string=diff,
xml_doc_string=original_data), xml_doc_string=original_data),
encoding='utf-8') encoding='utf-8')
......
...@@ -75,7 +75,7 @@ class ERP5DocumentConduit(ERP5Conduit): ...@@ -75,7 +75,7 @@ class ERP5DocumentConduit(ERP5Conduit):
# if time <= action.get('time'): # if time <= action.get('time'):
# # action in the past are not appended # # action in the past are not appended
# addable = WORKFLOW_ACTION_INSERTABLE # addable = WORKFLOW_ACTION_INSERTABLE
key_list = action.keys() key_list = list(action.keys())
# key_list.remove("time") # key_list.remove("time")
# XXX-AUREL For document it seems that checking time != is required # XXX-AUREL For document it seems that checking time != is required
# I don't know why ? # I don't know why ?
......
...@@ -27,9 +27,9 @@ ...@@ -27,9 +27,9 @@
# #
############################################################################## ##############################################################################
from xml.dom.ext import PrettyPrint from xml.dom.ext import PrettyPrint # pylint:disable=no-name-in-module,import-error
import random import random
from cStringIO import StringIO from six.moves import cStringIO as StringIO
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from zLOG import LOG from zLOG import LOG
......
...@@ -30,6 +30,7 @@ from logging import getLogger ...@@ -30,6 +30,7 @@ from logging import getLogger
from erp5.component.mixin.SyncMLEngineMixin import SyncMLEngineMixin from erp5.component.mixin.SyncMLEngineMixin import SyncMLEngineMixin
from erp5.component.module.SyncMLConstant import ACTIVITY_PRIORITY from erp5.component.module.SyncMLConstant import ACTIVITY_PRIORITY
from Products.ERP5.ERP5Site import getSite from Products.ERP5.ERP5Site import getSite
from six.moves import range
syncml_logger = getLogger('ERP5SyncML') syncml_logger = getLogger('ERP5SyncML')
...@@ -81,7 +82,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin): ...@@ -81,7 +82,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
# Make sure it is launched after indexation step # Make sure it is launched after indexation step
self.runGetAndActivate(subscription=subscription, tag=tag, self.runGetAndActivate(subscription=subscription, tag=tag,
after_method_id=("getAndIndex", after_method_id=("getAndIndex",
"SQLCatalog_indexSyncMLDocumentList")) "ERP5Site_indexSyncMLDocumentList"))
syncml_logger.info("X-> Client is sendind modification in activities") syncml_logger.info("X-> Client is sendind modification in activities")
# As we generated all activities to send data at once, process must not # As we generated all activities to send data at once, process must not
# go back here, go into processing state thus status will be applied and # go back here, go into processing state thus status will be applied and
...@@ -113,7 +114,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin): ...@@ -113,7 +114,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
(subscription.getPath(), (subscription.getPath(),
'applySyncCommand'), 'applySyncCommand'),
after_tag=tag,).sendMessage( after_tag=tag,).sendMessage(
xml=str(syncml_response)) xml=bytes(syncml_response))
# Synchronization process is now finished # Synchronization process is now finished
syncml_logger.info("\tClient finished processing messages from server") syncml_logger.info("\tClient finished processing messages from server")
subscription.finish() subscription.finish()
...@@ -137,7 +138,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin): ...@@ -137,7 +138,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
# transport failure # transport failure
syncml_logger.info("....client sending message....") syncml_logger.info("....client sending message....")
subscription.activate(activity="SQLQueue").sendMessage( subscription.activate(activity="SQLQueue").sendMessage(
xml=str(syncml_response)) xml=bytes(syncml_response))
def processServerSynchronization(self, subscriber, syncml_request): def processServerSynchronization(self, subscriber, syncml_request):
...@@ -210,7 +211,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin): ...@@ -210,7 +211,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
# Start to send modification only once we have processed # Start to send modification only once we have processed
# all message from client # all message from client
after_method_id=('processServerSynchronization', after_method_id=('processServerSynchronization',
'SQLCatalog_indexSyncMLDocumentList') 'ERP5Site_indexSyncMLDocumentList')
# XXX after tag might also be required to make sure all data are indexed # XXX after tag might also be required to make sure all data are indexed
tag = (tag, "%s_reset" % subscriber.getPath(),) tag = (tag, "%s_reset" % subscriber.getPath(),)
# Do not continue in elif, as sending modifications is done in the same # Do not continue in elif, as sending modifications is done in the same
...@@ -241,7 +242,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin): ...@@ -241,7 +242,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
subscriber.activate(activity="SQLQueue", subscriber.activate(activity="SQLQueue",
after_method_id=after_method_id, after_method_id=after_method_id,
after_tag=tag).sendMessage( after_tag=tag).sendMessage(
xml=str(syncml_response)) xml=bytes(syncml_response))
def runGetAndActivate(self, subscription, tag, after_method_id=None): def runGetAndActivate(self, subscription, tag, after_method_id=None):
""" """
...@@ -282,7 +283,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin): ...@@ -282,7 +283,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
response_id_list.reverse() response_id_list.reverse()
else: else:
response_id_list = [None for _ in response_id_list = [None for _ in
xrange(len(syncml_request.sync_command_list))] range(len(syncml_request.sync_command_list))]
split = getSite().portal_preferences.getPreferredSyncActionPerActivityCount() split = getSite().portal_preferences.getPreferredSyncActionPerActivityCount()
if not split: # We do not use activities if not split: # We do not use activities
if send_response: if send_response:
...@@ -294,7 +295,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin): ...@@ -294,7 +295,7 @@ class SyncMLAsynchronousEngine(SyncMLEngineMixin):
subscription.activate( subscription.activate(
activity="SQLQueue", activity="SQLQueue",
priority=ACTIVITY_PRIORITY, priority=ACTIVITY_PRIORITY,
tag=subscription.getRelativeUrl()).sendMessage(xml=str(syncml_response)) tag=subscription.getRelativeUrl()).sendMessage(xml=bytes(syncml_response))
else: else:
# XXX For now always split by one # XXX For now always split by one
activate = subscription.activate activate = subscription.activate
......
...@@ -123,8 +123,9 @@ class SyncMLSynchronousEngine(SyncMLEngineMixin): ...@@ -123,8 +123,9 @@ class SyncMLSynchronousEngine(SyncMLEngineMixin):
subscription._edit(authenticated_user=None) subscription._edit(authenticated_user=None)
# Send the message # Send the message
subscription.sendMessage(xml=str(syncml_response)) subscription.sendMessage(xml=bytes(syncml_response))
return str(syncml_response)
return bytes(syncml_response)
def processServerSynchronization(self, subscriber, syncml_request): def processServerSynchronization(self, subscriber, syncml_request):
...@@ -222,10 +223,10 @@ class SyncMLSynchronousEngine(SyncMLEngineMixin): ...@@ -222,10 +223,10 @@ class SyncMLSynchronousEngine(SyncMLEngineMixin):
subscriber.logout() subscriber.logout()
subscriber._edit(authenticated_user=None, subscriber._edit(authenticated_user=None,
remaining_object_path_list=None) remaining_object_path_list=None)
syncml_response = "" # XXX This is expected by unit test only syncml_response = b"" # XXX This is expected by unit test only
# Body must be sent even when there is no data to notify client # Body must be sent even when there is no data to notify client
subscriber.sendMessage(xml=str(syncml_response)) subscriber.sendMessage(xml=bytes(syncml_response))
# Return message for unit test purpose # Return message for unit test purpose
return str(syncml_response) return bytes(syncml_response)
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
from lxml.builder import ElementMaker from lxml.builder import ElementMaker
from lxml.etree import Element from lxml.etree import Element
from lxml import etree from lxml import etree
import six
from erp5.component.module.XMLSyncUtils import resolveSyncmlStatusCode, \ from erp5.component.module.XMLSyncUtils import resolveSyncmlStatusCode, \
encode, resolveSyncmlAlertCode encode, resolveSyncmlAlertCode
...@@ -60,13 +61,13 @@ class SyncMLResponse(object): ...@@ -60,13 +61,13 @@ class SyncMLResponse(object):
def __len__(self): def __len__(self):
# To check if it has to be done on whole message or only the body # To check if it has to be done on whole message or only the body
return len(etree.tostring(self.body, encoding='utf-8', return len(bytes(self))
xml_declaration=True,
pretty_print=True))
def __str__(self): def __bytes__(self):
return etree.tostring(self.data, encoding='utf-8', xml_declaration=True, return etree.tostring(self.data, encoding='utf-8', xml_declaration=True,
pretty_print=True) pretty_print=True)
if six.PY2:
__str__ = __bytes__
def _getNextCommandId(self): def _getNextCommandId(self):
""" """
...@@ -117,7 +118,7 @@ class SyncMLResponse(object): ...@@ -117,7 +118,7 @@ class SyncMLResponse(object):
if authentication_type == 'syncml:auth-basic': if authentication_type == 'syncml:auth-basic':
# base64 formating of "userid:password" # base64 formating of "userid:password"
credential = "%s:%s" % (user_id, password) credential = "%s:%s" % (user_id, password)
credential = encode(authentication_format, credential) credential = encode(authentication_format, credential).decode()
elif authentication_type == "syncml:auth-md5": elif authentication_type == "syncml:auth-md5":
# base64 coded md5 for user "XXX", password "XXX", nonce "XXX" # base64 coded md5 for user "XXX", password "XXX", nonce "XXX"
raise NotImplementedError("MD5 authentication not supported") raise NotImplementedError("MD5 authentication not supported")
...@@ -345,7 +346,7 @@ class SyncMLResponse(object): ...@@ -345,7 +346,7 @@ class SyncMLResponse(object):
data_node = E.Data() data_node = E.Data()
# XXX to be remove later to use only CDATA # XXX to be remove later to use only CDATA
if media_type == 'text/xml': if media_type == 'text/xml':
if isinstance(data, basestring): if isinstance(data, bytes):
data_node.append(etree.XML(data, parser=parser)) data_node.append(etree.XML(data, parser=parser))
elif isinstance(data, etree.CDATA): elif isinstance(data, etree.CDATA):
# data could be Data element if partial XML # data could be Data element if partial XML
...@@ -421,7 +422,7 @@ class SyncMLRequest(object): ...@@ -421,7 +422,7 @@ class SyncMLRequest(object):
""" SyncMLRequest represent a message received by the client or server""" """ SyncMLRequest represent a message received by the client or server"""
def __init__(self, xml): def __init__(self, xml):
if isinstance(xml, basestring): if isinstance(xml, bytes):
self.data = etree.XML(xml, parser=parser) self.data = etree.XML(xml, parser=parser)
else: else:
raise ValueError("Do not know how to initialize message with data %r" raise ValueError("Do not know how to initialize message with data %r"
...@@ -435,9 +436,11 @@ class SyncMLRequest(object): ...@@ -435,9 +436,11 @@ class SyncMLRequest(object):
self.isFinal = False self.isFinal = False
self.parse() self.parse()
def __str__(self): def __bytes__(self):
return etree.tostring(self.data, encoding='utf-8', xml_declaration=True, return etree.tostring(self.data, encoding='utf-8', xml_declaration=True,
pretty_print=True) pretty_print=True)
if six.PY2:
__str__ = __bytes__
def parse(self): def parse(self):
""" """
...@@ -590,7 +593,8 @@ class SyncMLRequest(object): ...@@ -590,7 +593,8 @@ class SyncMLRequest(object):
parser_ = etree.XMLParser(strip_cdata=False) parser_ = etree.XMLParser(strip_cdata=False)
cdata = etree.XML(data, parser_) cdata = etree.XML(data, parser_)
data = cdata.text data = cdata.text
# XXX this is unicode and can be a problem for activity if six.PY3:
data = data.encode()
sync_command_kw["raw_data"] = data sync_command_kw["raw_data"] = data
append(sync_command_kw) append(sync_command_kw)
...@@ -32,8 +32,7 @@ class FileTransport: ...@@ -32,8 +32,7 @@ class FileTransport:
def send(self, to_url, data, sync_id, content_type): def send(self, to_url, data, sync_id, content_type):
filename = to_url[len('file:/'):] filename = to_url[len('file:/'):]
try: try:
stream = open(filename, 'w') with open(filename, 'wb') as stream:
stream.write(data) stream.write(data)
stream.close()
except IOError: except IOError:
raise ConnectionError raise ConnectionError
...@@ -38,7 +38,7 @@ from hashlib import md5 ...@@ -38,7 +38,7 @@ from hashlib import md5
from ZPublisher.HTTPRequest import FileUpload from ZPublisher.HTTPRequest import FileUpload
from OFS.Image import Pdata from OFS.Image import Pdata
from StringIO import StringIO from io import BytesIO
import transaction import transaction
class PdataHelper(persistent.Persistent): class PdataHelper(persistent.Persistent):
...@@ -68,14 +68,14 @@ class PdataHelper(persistent.Persistent): ...@@ -68,14 +68,14 @@ class PdataHelper(persistent.Persistent):
n = self._max_len n = self._max_len
if isinstance(value, (str, unicode)): if isinstance(value, six.string_types + (six.binary_type, )):
if isinstance(value, unicode): if not isinstance(value, six.binary_type):
value = value.encode('utf-8') value = value.encode('utf-8')
size=len(value) size = len(value)
if size < n: if size < n:
return Pdata(value), size return Pdata(value), size
# Big string: cut it into smaller chunks # Big data: cut it into smaller chunks
value = StringIO(value) value = BytesIO(value)
if isinstance(value, FileUpload) and not value: if isinstance(value, FileUpload) and not value:
raise ValueError('File not specified') raise ValueError('File not specified')
...@@ -161,11 +161,13 @@ class PdataHelper(persistent.Persistent): ...@@ -161,11 +161,13 @@ class PdataHelper(persistent.Persistent):
""" """
return self.size return self.size
def __str__(self): def __bytes__(self):
"""Return string concatenation """Return string concatenation
of all Pdata parts of all Pdata parts
""" """
return str(self._data) return bytes(self._data)
if six.PY2:
__str__ = __bytes__
def getContentMd5(self): def getContentMd5(self):
""" """
...@@ -176,26 +178,20 @@ class PdataHelper(persistent.Persistent): ...@@ -176,26 +178,20 @@ class PdataHelper(persistent.Persistent):
self.md5sum = md5sum self.md5sum = md5sum
return md5sum return md5sum
def __getslice__(self, i, j): def __getitem__(self, i):
"""XXX Could be improved to avoid loading """XXX Could be improved to avoid loading
into memory all Pdata objects into memory all Pdata objects
""" """
return self.__str__()[i:j] return self.__bytes__()[i]
def getLastPdata(self): def getLastPdata(self):
"""return the last Pdata element """return the last Pdata element
of a Pdata chains of a Pdata chains
""" """
pdata = self._data pdata = self._data
if six.PY2: next_ = pdata.next
next_ = pdata.next
else:
next_ = pdata.__next__
while next_ is not None: while next_ is not None:
pdata = next_ pdata = next_
if six.PY2: next_ = pdata.next
next_ = pdata.next
else:
next_ = pdata.__next__
return pdata return pdata
...@@ -31,6 +31,7 @@ from erp5.component.module.ERP5Conduit import ERP5Conduit ...@@ -31,6 +31,7 @@ from erp5.component.module.ERP5Conduit import ERP5Conduit
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
import difflib import difflib
import six
from zLOG import LOG from zLOG import LOG
...@@ -56,7 +57,7 @@ class VCardConduit(ERP5Conduit): ...@@ -56,7 +57,7 @@ class VCardConduit(ERP5Conduit):
""" """
#LOG('VCardConduit',0,'addNode, object=%s, object_id=%s, sub_object:%s, \ #LOG('VCardConduit',0,'addNode, object=%s, object_id=%s, sub_object:%s, \
#xml:\n%s' % (str(object), str(object_id), str(sub_object), xml)) #xml:\n%s' % (str(object), str(object_id), str(sub_object), xml))
if not isinstance(xml, str): if not isinstance(xml, bytes):
xml = self.nodeToString(xml) xml = self.nodeToString(xml)
portal_type = 'Person' #the VCard can just use Person portal_type = 'Person' #the VCard can just use Person
if sub_object is None: if sub_object is None:
...@@ -161,7 +162,9 @@ class VCardConduit(ERP5Conduit): ...@@ -161,7 +162,9 @@ class VCardConduit(ERP5Conduit):
convert_dict['N'] = 'last_name' convert_dict['N'] = 'last_name'
convert_dict['TEL'] = 'default_telephone_text' convert_dict['TEL'] = 'default_telephone_text'
edit_dict = {} edit_dict = {}
vcard_list = vcard.split('\n') if isinstance(vcard, bytes):
vcard = vcard.decode('utf-8')
vcard_list = vcard.splitlines()
for vcard_line in vcard_list: for vcard_line in vcard_list:
if ':' in vcard_line: if ':' in vcard_line:
property_, property_value = vcard_line.split(':') property_, property_value = vcard_line.split(':')
...@@ -192,12 +195,12 @@ class VCardConduit(ERP5Conduit): ...@@ -192,12 +195,12 @@ class VCardConduit(ERP5Conduit):
else: else:
property_name=property_ property_name=property_
if isinstance(property_name, unicode): if six.PY2 and isinstance(property_name, six.text_type):
property_name = property_name.encode('utf-8') property_name = property_name.encode('utf-8')
tmp = [] tmp = []
for property_value in property_value_list: for property_value in property_value_list:
if isinstance(property_value, unicode): if six.PY2 and isinstance(property_value, six.text_type):
property_value = property_value.encode('utf-8') property_value = property_value.encode('utf-8')
tmp.append(property_value) tmp.append(property_value)
property_value_list = tmp property_value_list = tmp
...@@ -226,6 +229,10 @@ class VCardConduit(ERP5Conduit): ...@@ -226,6 +229,10 @@ class VCardConduit(ERP5Conduit):
def generateDiff(self, new_data, former_data): def generateDiff(self, new_data, former_data):
"""return unified diff for plain-text documents """return unified diff for plain-text documents
""" """
if isinstance(new_data, bytes):
new_data = new_data.decode('utf-8')
if isinstance(former_data, bytes):
former_data = former_data.decode('utf-8')
diff = '\n'.join(difflib.unified_diff(new_data.splitlines(), diff = '\n'.join(difflib.unified_diff(new_data.splitlines(),
former_data.splitlines())) former_data.splitlines()))
return diff return diff
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
import smtplib import smtplib
import os import os
import six
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
from lxml import etree from lxml import etree
from lxml.builder import ElementMaker from lxml.builder import ElementMaker
...@@ -58,7 +59,10 @@ def encode(format, string_to_encode): # pylint: disable=redefined-builtin ...@@ -58,7 +59,10 @@ def encode(format, string_to_encode): # pylint: disable=redefined-builtin
if not format: if not format:
return string_to_encode return string_to_encode
if format == 'b64': if format == 'b64':
return b64encode(string_to_encode) if not isinstance(string_to_encode, six.binary_type):
string_to_encode = string_to_encode.encode()
encoded = b64encode(string_to_encode)
return encoded
#elif format is .... put here the other formats #elif format is .... put here the other formats
else:#if there is no format corresponding with format, raise an error else:#if there is no format corresponding with format, raise an error
LOG('encode : unknown or not implemented format : ', INFO, format) LOG('encode : unknown or not implemented format : ', INFO, format)
...@@ -180,11 +184,10 @@ def getXupdateObject(object_xml=None, old_xml=None): ...@@ -180,11 +184,10 @@ def getXupdateObject(object_xml=None, old_xml=None):
""" """
erp5diff = ERP5Diff() erp5diff = ERP5Diff()
erp5diff.compare(old_xml, object_xml) erp5diff.compare(old_xml, object_xml)
#Upper version of ERP5Diff produce valid XML.
if erp5diff._getResultRoot(): if erp5diff._getResultRoot():
xupdate = erp5diff.outputString() xupdate = erp5diff.outputBytes(encoding="utf-8")
#omit xml declaration # omit xml declaration
xupdate = xupdate[xupdate.find('<xupdate:modifications'):] xupdate = xupdate[xupdate.find(b'<xupdate:modifications'):]
return xupdate return xupdate
def cutXML(xml_string, length=None): def cutXML(xml_string, length=None):
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
############################################################################## ##############################################################################
from erp5.component.module.XMLSyncUtils import XMLSyncUtilsMixin from erp5.component.module.XMLSyncUtils import XMLSyncUtilsMixin
from xml.dom.ext.reader.Sax2 import FromXml from xml.dom.ext.reader.Sax2 import FromXml # pylint:disable=no-name-in-module,import-error
class XupdateUtils(XMLSyncUtilsMixin): class XupdateUtils(XMLSyncUtilsMixin):
""" """
......
from six.moves import range
if not len(path_list): if not len(path_list):
return return
restrictedTraverse = context.getPortalObject().restrictedTraverse restrictedTraverse = context.getPortalObject().restrictedTraverse
...@@ -14,16 +15,16 @@ def generateParameterList(): ...@@ -14,16 +15,16 @@ def generateParameterList():
parameter_append_list = [] parameter_append_list = []
append = parameter_append_list.append append = parameter_append_list.append
parameter_dict = {} parameter_dict = {}
for property in method.arguments_src.split(): for prop in method.arguments_src.split():
parameter_dict[property] = parameter_value_list = [] parameter_dict[prop] = parameter_value_list = []
if property == 'getData': if prop == 'getData':
getter = getData getter = getData
elif property == 'getId': elif prop == 'getId':
getter = getId getter = getId
else: else:
getter = None getter = None
if getter is None: if getter is None:
getter = lambda obj, property=property: getattr(obj, property)() getter = lambda obj, prop=prop: getattr(obj, prop)()
append((parameter_value_list, getter)) append((parameter_value_list, getter))
return parameter_dict, parameter_append_list return parameter_dict, parameter_append_list
...@@ -35,7 +36,7 @@ for path in path_list: ...@@ -35,7 +36,7 @@ for path in path_list:
object_list = obj() object_list = obj()
else: else:
object_list = [obj,] object_list = [obj,]
for x in xrange(0, len(object_list), MAX_PER_QUERY): for x in range(0, len(object_list), MAX_PER_QUERY):
parameter_dict, parameter_append_list = generateParameterList() parameter_dict, parameter_append_list = generateParameterList()
for obj in object_list[x:x+MAX_PER_QUERY]: for obj in object_list[x:x+MAX_PER_QUERY]:
for value_list, getter in parameter_append_list: for value_list, getter in parameter_append_list:
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>SQLCatalog_indexSyncMLDocumentList</string> </value> <value> <string>ERP5Site_indexSyncMLDocumentList</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -3,24 +3,17 @@ first_name = context.getFirstName() ...@@ -3,24 +3,17 @@ first_name = context.getFirstName()
last_name = context.getLastName() last_name = context.getLastName()
tel = context.getDefaultTelephoneTelephoneNumber() tel = context.getDefaultTelephoneTelephoneNumber()
if same_type(first_name, u'a'):
first_name = first_name.encode('utf-8')
if same_type(last_name, u'a'):
last_name = last_name.encode('utf-8')
if same_type(tel, u'a') :
tel = tel.encode('utf-8')
parameters_FN = '' parameters_FN = ''
parameters_N = '' parameters_N = ''
try: try:
first_name.encode('utf-8') first_name.encode('utf-8')
except: except UnicodeEncodeError:
parameters_FN = ';ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8' parameters_FN = ';ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8'
try: try:
last_name.encode('utf-8') last_name.encode('utf-8')
except: except UnicodeEncodeError:
parameters_N = ';ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8' parameters_N = ';ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8'
append = vcard_string_list.append append = vcard_string_list.append
......
from Products.CMFCore.utils import getToolByName
context.resetSubscriberList() context.resetSubscriberList()
message = context.Base_translateString('Publication reseting') message = context.Base_translateString('Publication reset')
return context.Base_redirect(form_id, keep_items={'portal_status_message' : message}, **kw) return context.Base_redirect(form_id, keep_items={'portal_status_message' : message}, **kw)
...@@ -74,9 +74,7 @@ ...@@ -74,9 +74,7 @@
<item> <item>
<key> <string>center</string> </key> <key> <string>center</string> </key>
<value> <value>
<list> <list/>
<string>my_validatation_state</string>
</list>
</value> </value>
</item> </item>
<item> <item>
...@@ -98,6 +96,7 @@ ...@@ -98,6 +96,7 @@
<value> <value>
<list> <list>
<string>my_data</string> <string>my_data</string>
<string>my_translated_validatation_state_title</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>my_validatation_state</string> </value> <value> <string>my_translated_validatation_state_title</string> </value>
</item> </item>
<item> <item>
<key> <string>message_values</string> </key> <key> <string>message_values</string> </key>
......
...@@ -9,8 +9,8 @@ if active_process is None: ...@@ -9,8 +9,8 @@ if active_process is None:
portal_type='Active Process', portal_type='Active Process',
sort_on=(('creation_date', 'DESC'), sort_on=(('creation_date', 'DESC'),
# XXX Work around poor resolution of MySQL dates. # XXX Work around poor resolution of MySQL dates.
('CONVERT(`catalog`.`id`, UNSIGNED)', 'DESC')), ('id', 'DESC', 'UNSIGNED')),
causality_uid=sub.getUid()) causality_uid=context.getUid())
else: else:
active_process = context.getPortalObject().restrictedTraverse(active_process) active_process = context.getPortalObject().restrictedTraverse(active_process)
......
from Products.CMFCore.utils import getToolByName
context.resetSignatureList() context.resetSignatureList()
context.resetAnchorList() context.resetAnchorList()
message = context.Base_translateString('Subscription reseting') message = context.Base_translateString('Subscription reset')
return context.Base_redirect(form_id, keep_items={'portal_status_message' : message}, **kw) return context.Base_redirect(form_id, keep_items={'portal_status_message' : message}, **kw)
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>SyncML_searchFolder</string> </value> <value> <string>SyncMLSubscription_searchFolder</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>SyncMLDocument_updateURLToCurrentSite</string> </value> <value> <string>SyncMLSubscription_updateURLToCurrentSite</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
<string>my_next_anchor</string> <string>my_next_anchor</string>
<string>my_translated_portal_type</string> <string>my_translated_portal_type</string>
<string>my_translated_validation_state_title</string> <string>my_translated_validation_state_title</string>
<string>my_synchronization_state_title</string> <string>my_translated_synchronization_state_title</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>my_synchronization_state_title</string> </value> <value> <string>my_translated_synchronization_state_title</string> </value>
</item> </item>
<item> <item>
<key> <string>message_values</string> </key> <key> <string>message_values</string> </key>
......
from six.moves import range
sub_path = method_kw.get("subscription_path") sub_path = method_kw.get("subscription_path")
sub = context.getPortalObject().restrictedTraverse(sub_path) sub = context.getPortalObject().restrictedTraverse(sub_path)
search_kw = dict(kw) search_kw = dict(kw)
...@@ -17,7 +18,7 @@ if result_count: ...@@ -17,7 +18,7 @@ if result_count:
r = [x.getId() for x in r] r = [x.getId() for x in r]
callback_method = getattr(sub.activate(**activate_kw), callback) callback_method = getattr(sub.activate(**activate_kw), callback)
for i in xrange(0, result_count, packet_size): for i in range(0, result_count, packet_size):
callback_method(id_list=r[i:i+packet_size], callback_method(id_list=r[i:i+packet_size],
**method_kw) **method_kw)
......
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>SynchronizationTool_viewPointFixe</string> </value> <value> <string>SynchronizationTool_viewPointFixeDialog</string> </value>
</item> </item>
<item> <item>
<key> <string>method</string> </key> <key> <string>method</string> </key>
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
</item> </item>
<item> <item>
<key> <string>name</string> </key> <key> <string>name</string> </key>
<value> <string>SynchronizationTool_viewPointFixe</string> </value> <value> <string>SynchronizationTool_viewPointFixeDialog</string> </value>
</item> </item>
<item> <item>
<key> <string>pt</string> </key> <key> <string>pt</string> </key>
......
...@@ -55,9 +55,9 @@ class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin): ...@@ -55,9 +55,9 @@ class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin):
nb_objects = 10 nb_objects = 10
#for objects #for objects
ids = range(1, nb_objects+1) ids = list(range(1, nb_objects+1))
#id_max_text : number of document text #id_max_text : number of document text
id_max_text = nb_objects/2 id_max_text = nb_objects // 2
id1 = '2' id1 = '2'
id2 = '3' id2 = '3'
#for documents (encoding in unicode for utf-8) #for documents (encoding in unicode for utf-8)
...@@ -644,7 +644,7 @@ class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin): ...@@ -644,7 +644,7 @@ class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin):
publication = portal_sync[self.pub_id] publication = portal_sync[self.pub_id]
self.assertEqual(len(publication['1']), nb_document) self.assertEqual(len(publication['1']), nb_document)
gid = self.reference1 + '-' + self.version1 + '-' + self.language1 # ie the title '' gid = self.reference1 + '-' + self.version1 + '-' + self.language1 # ie the title ''
gid = b16encode(gid) gid = b16encode(gid.encode()).decode()
document_c1 = subscription1.getDocumentFromGid(gid) document_c1 = subscription1.getDocumentFromGid(gid)
document_s = publication.getSubscriber(self.subscription_url['two_way']).getDocumentFromGid(gid) document_s = publication.getSubscriber(self.subscription_url['two_way']).getDocumentFromGid(gid)
id_s = document_s.getId() id_s = document_s.getId()
......
...@@ -31,6 +31,7 @@ import unittest ...@@ -31,6 +31,7 @@ import unittest
from base64 import b64encode, b64decode, b16encode from base64 import b64encode, b64decode, b16encode
from lxml import etree from lxml import etree
from unittest import expectedFailure from unittest import expectedFailure
from six import string_types as basestring
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from ERP5Diff import ERP5Diff from ERP5Diff import ERP5Diff
...@@ -43,6 +44,7 @@ from erp5.component.module.SyncMLConstant import MAX_LEN ...@@ -43,6 +44,7 @@ from erp5.component.module.SyncMLConstant import MAX_LEN
from erp5.component.document import SyncMLSubscription from erp5.component.document import SyncMLSubscription
from erp5.component.module.testERP5SyncMLMixin import TestERP5SyncMLMixin \ from erp5.component.module.testERP5SyncMLMixin import TestERP5SyncMLMixin \
as TestMixin as TestMixin
from six.moves import range
class TestERP5SyncMLMixin(TestMixin): class TestERP5SyncMLMixin(TestMixin):
...@@ -255,7 +257,7 @@ class TestERP5SyncMLMixin(TestMixin): ...@@ -255,7 +257,7 @@ class TestERP5SyncMLMixin(TestMixin):
# only first call will return an answer # only first call will return an answer
result = portal_sync.processServerSynchronization(publication.getPath()) result = portal_sync.processServerSynchronization(publication.getPath())
self.tic() self.tic()
for _ in xrange(2): for _ in range(2):
portal_sync.processServerSynchronization(publication.getPath()) portal_sync.processServerSynchronization(publication.getPath())
self.tic() self.tic()
nb_message += 1 nb_message += 1
...@@ -263,7 +265,7 @@ class TestERP5SyncMLMixin(TestMixin): ...@@ -263,7 +265,7 @@ class TestERP5SyncMLMixin(TestMixin):
break break
result = portal_sync.processClientSynchronization(subscription.getPath()) result = portal_sync.processClientSynchronization(subscription.getPath())
self.tic() self.tic()
for _ in xrange(2): for _ in range(2):
portal_sync.processClientSynchronization(subscription.getPath()) portal_sync.processClientSynchronization(subscription.getPath())
self.tic() self.tic()
nb_message += 1 nb_message += 1
...@@ -1095,7 +1097,7 @@ return [context[%r]] ...@@ -1095,7 +1097,7 @@ return [context[%r]]
publication = portal_sync[self.pub_id] publication = portal_sync[self.pub_id]
self.assertEqual(len(publication.getDocumentList()), nb_person) self.assertEqual(len(publication.getDocumentList()), nb_person)
gid = self.first_name1 + ' ' + self.last_name1 # ie the title 'Sebastien Robin' gid = self.first_name1 + ' ' + self.last_name1 # ie the title 'Sebastien Robin'
gid = b16encode(gid) gid = b16encode(gid.encode()).decode()
person_c1 = subscription1.getDocumentFromGid(gid) person_c1 = subscription1.getDocumentFromGid(gid)
person_s = publication.getSubscriber(self.subscription_url1).getDocumentFromGid(gid) person_s = publication.getSubscriber(self.subscription_url1).getDocumentFromGid(gid)
id_s = person_s.getId() id_s = person_s.getId()
...@@ -1620,7 +1622,7 @@ return [context[%r]] ...@@ -1620,7 +1622,7 @@ return [context[%r]]
# Check same person on client & server side # Check same person on client & server side
client_person_module = self.getPersonClient1() client_person_module = self.getPersonClient1()
server_person_module = self.getPersonServer() server_person_module = self.getPersonServer()
for x in xrange(1, 61): for x in range(1, 61):
client_person = client_person_module._getOb(str(x)) client_person = client_person_module._getOb(str(x))
server_person = server_person_module._getOb(str(x)) server_person = server_person_module._getOb(str(x))
self.assertEqual(client_person.getFirstName(), self.first_name1) self.assertEqual(client_person.getFirstName(), self.first_name1)
...@@ -1643,7 +1645,7 @@ return [context[%r]] ...@@ -1643,7 +1645,7 @@ return [context[%r]]
self.assertEqual(len(subscription1), 0) self.assertEqual(len(subscription1), 0)
self.assertEqual(len(subscriber), 0) self.assertEqual(len(subscriber), 0)
for x in xrange(1, 61): for x in range(1, 61):
client_person = client_person_module._getOb(str(x)) client_person = client_person_module._getOb(str(x))
server_person = server_person_module._getOb(str(x)) server_person = server_person_module._getOb(str(x))
self.assertEqual(client_person.getFirstName(), self.first_name2) self.assertEqual(client_person.getFirstName(), self.first_name2)
...@@ -1663,7 +1665,7 @@ return [context[%r]] ...@@ -1663,7 +1665,7 @@ return [context[%r]]
self.assertEqual(len(subscription1), 0) self.assertEqual(len(subscription1), 0)
self.assertEqual(len(subscriber), 0) self.assertEqual(len(subscriber), 0)
for x in xrange(1, 61): for x in range(1, 61):
client_person = client_person_module._getOb(str(x)) client_person = client_person_module._getOb(str(x))
server_person = server_person_module._getOb(str(x)) server_person = server_person_module._getOb(str(x))
self.assertEqual(client_person.getLastName(), self.last_name2) self.assertEqual(client_person.getLastName(), self.last_name2)
...@@ -1687,31 +1689,26 @@ return [context[%r]] ...@@ -1687,31 +1689,26 @@ return [context[%r]]
self.test_08_FirstSynchronization() self.test_08_FirstSynchronization()
#define some strings : #define some strings :
python = 'www.python.org' python = b'www.python.org'
awaited_result_python = "d3d3LnB5dGhvbi5vcmc=" awaited_result_python = b"d3d3LnB5dGhvbi5vcmc="
long_string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO\ long_string = u"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZéèçà@^~µ&²0123456789"\
PQRSTUVWXYZéèçà@^~µ&²0123456789!@#0^&*();:<>,. []{}\xc3\xa7sdf__\ u"!@#0^&*();:<>,. []{}çsdf__sdfççç_df___&&é]]]°°°°°°'".encode('utf-8')
sdf\xc3\xa7\xc3\xa7\xc3\xa7_df___&&\xc3\xa9]]]\xc2\xb0\xc2\xb0\xc2\ awaited_result_long_string = b'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZH'\
\xb0\xc2\xb0\xc2\xb0\xc2\xb0" b'SElKS0xNTk9QUVJTVFVWV1hZWsOpw6jDp8OgQF5+wrUmwrIwMTIzNDU2Nzg5IUAjMF4mKigpO'\
#= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZéèçà@^~µ&²012345 b'zo8PiwuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsCc='
#6789!@#0^&*();:<>,. []{}çsdf__sdfççç_df___&&é]]]°°°°°°'"
awaited_result_long_string = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZH\
SElKS0xNTk9QUVJTVFVWV1hZWsOpw6jDp8OgQF5+wrUmwrIwMTIzNDU2Nzg5IUAjMF4mKigpOzo8Pi\
wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=="
#test just b64encode #test just b64encode
self.assertEqual(b64encode(python), awaited_result_python) self.assertEqual(b64encode(python), awaited_result_python)
self.assertEqual(b64encode(""), "") self.assertEqual(b64encode(b""), b"")
self.assertEqual(b64encode(long_string), awaited_result_long_string) self.assertEqual(b64encode(long_string), awaited_result_long_string)
self.assertEqual(b64decode(awaited_result_python), python) self.assertEqual(b64decode(awaited_result_python), python)
self.assertEqual(b64decode(""), "") self.assertEqual(b64decode(b""), b"")
self.assertEqual(b64decode(awaited_result_long_string), long_string) self.assertEqual(b64decode(awaited_result_long_string), long_string)
# test with the ERP5 functions # test with the ERP5 functions
string_encoded = encode('b64', python) string_encoded = encode('b64', python)
self.assertEqual(string_encoded, awaited_result_python) self.assertEqual(string_encoded, awaited_result_python)
string_decoded = decode('b64', awaited_result_python) string_decoded = decode('b64', awaited_result_python.decode())
self.assertEqual(string_decoded, python) self.assertEqual(string_decoded, python)
self.assertTrue(isDecodeEncodeTheSame(string_encoded, self.assertTrue(isDecodeEncodeTheSame(string_encoded,
python, 'b64')) python, 'b64'))
...@@ -1720,17 +1717,17 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA==" ...@@ -1720,17 +1717,17 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=="
string_encoded = encode('b64', long_string) string_encoded = encode('b64', long_string)
self.assertEqual(string_encoded, awaited_result_long_string) self.assertEqual(string_encoded, awaited_result_long_string)
string_decoded = decode('b64', awaited_result_long_string) string_decoded = decode('b64', awaited_result_long_string.decode())
self.assertEqual(string_decoded, long_string) self.assertEqual(string_decoded, long_string)
self.assertTrue(isDecodeEncodeTheSame(string_encoded, self.assertTrue(isDecodeEncodeTheSame(string_encoded,
long_string, 'b64')) long_string, 'b64'))
self.assertTrue(isDecodeEncodeTheSame(string_encoded, self.assertTrue(isDecodeEncodeTheSame(string_encoded,
string_decoded, 'b64')) string_decoded, 'b64'))
self.assertEqual(encode('b64', ''), '') self.assertEqual(encode('b64', b''), b'')
self.assertEqual(decode('b64', ''), '') self.assertEqual(decode('b64', ''), b'')
self.assertTrue(isDecodeEncodeTheSame( self.assertTrue(isDecodeEncodeTheSame(
encode('b64', ''), '', 'b64')) encode('b64', ''), b'', 'b64'))
def test_35_authentication(self): def test_35_authentication(self):
""" """
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
############################################################################## ##############################################################################
from logging import getLogger from logging import getLogger
import six
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
...@@ -184,8 +185,7 @@ class SynchronizationTool(BaseTool): ...@@ -184,8 +185,7 @@ class SynchronizationTool(BaseTool):
# #
security.declarePublic('readResponse') security.declarePublic('readResponse')
def readResponse(self, text='', sync_id=None, from_url=None): def readResponse(self, text='', sync_id=None, from_url=None):
""" """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.
""" """
syncml_logger.info('readResponse sync_id %s, text %s', sync_id, text) syncml_logger.info('readResponse sync_id %s, text %s', sync_id, text)
...@@ -193,6 +193,9 @@ class SynchronizationTool(BaseTool): ...@@ -193,6 +193,9 @@ class SynchronizationTool(BaseTool):
# we are still anonymous at this time, use unrestrictedSearchResults # we are still anonymous at this time, use unrestrictedSearchResults
# to fetch the Subcribers # to fetch the Subcribers
catalog_tool = self.getPortalObject().portal_catalog.unrestrictedSearchResults catalog_tool = self.getPortalObject().portal_catalog.unrestrictedSearchResults
if isinstance(text, six.text_type):
text = text.encode('utf-8')
syncml_request = SyncMLRequest(text) syncml_request = SyncMLRequest(text)
# It is assumed that client & server does not share the same database ID # It is assumed that client & server does not share the same database ID
...@@ -233,12 +236,12 @@ class SynchronizationTool(BaseTool): ...@@ -233,12 +236,12 @@ class SynchronizationTool(BaseTool):
% (sync_id, syncml_request.header['target'])) % (sync_id, syncml_request.header['target']))
# we use from only if we have a file # we use from only if we have a file
elif isinstance(from_url, basestring): elif isinstance(from_url, six.string_types):
if from_url.startswith('file:'): if from_url.startswith('file:'):
filename = from_url[len('file:'):] filename = from_url[len('file:'):]
xml = None xml = None
try: try:
stream = open(filename, 'r') stream = open(filename, 'rb')
except IOError: except IOError:
# XXX-Aurel : Why raising here make unit tests to fail ? # XXX-Aurel : Why raising here make unit tests to fail ?
# raise ValueError("Impossible to read file %s, error is %s" # raise ValueError("Impossible to read file %s, error is %s"
...@@ -313,7 +316,7 @@ class SynchronizationTool(BaseTool): ...@@ -313,7 +316,7 @@ class SynchronizationTool(BaseTool):
raise NotImplementedError("Starting sync process from server is forbidden") raise NotImplementedError("Starting sync process from server is forbidden")
# Return message for unit test purpose # Return message for unit test purpose
return str(syncml_response) return bytes(syncml_response)
# #
# Following methods are related to client (subscription) # Following methods are related to client (subscription)
...@@ -360,12 +363,12 @@ class SynchronizationTool(BaseTool): ...@@ -360,12 +363,12 @@ class SynchronizationTool(BaseTool):
activity="SQLQueue", activity="SQLQueue",
after_method_id=('processServerSynchronization', after_method_id=('processServerSynchronization',
'getAndIndex', 'getAndIndex',
'SQLCatalog_indexSyncMLDocumentList'), 'ERP5Site_indexSyncMLDocumentList'),
priority=ACTIVITY_PRIORITY, priority=ACTIVITY_PRIORITY,
tag=subscription.getRelativeUrl()).sendMessage(str(syncml_response)) tag=subscription.getRelativeUrl()).sendMessage(bytes(syncml_response))
else: else:
subscription.sendMessage(str(syncml_response)) subscription.sendMessage(bytes(syncml_response))
return str(syncml_response) return bytes(syncml_response)
InitializeClass(SynchronizationTool) InitializeClass(SynchronizationTool)
...@@ -114,7 +114,7 @@ ...@@ -114,7 +114,7 @@
</item> </item>
<item> <item>
<key> <string>list_method_id</string> </key> <key> <string>list_method_id</string> </key>
<value> <string>SyncML_searchFolder</string> </value> <value> <string>SyncMLSubscription_searchFolder</string> </value>
</item> </item>
<item> <item>
<key> <string>portal_type</string> </key> <key> <string>portal_type</string> </key>
......
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
</item> </item>
<item> <item>
<key> <string>list_method_id</string> </key> <key> <string>list_method_id</string> </key>
<value> <string>SyncML_searchFolder</string> </value> <value> <string>SyncMLSubscription_searchFolder</string> </value>
</item> </item>
<item> <item>
<key> <string>next_anchor</string> </key> <key> <string>next_anchor</string> </key>
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
from erp5.component.module.testERP5SyncMLMixin import TestERP5SyncMLMixin from erp5.component.module.testERP5SyncMLMixin import TestERP5SyncMLMixin
from six.moves import range
class testSyncMLAsynchronousEngine(TestERP5SyncMLMixin): class testSyncMLAsynchronousEngine(TestERP5SyncMLMixin):
...@@ -100,7 +101,7 @@ class testSyncMLAsynchronousEngine(TestERP5SyncMLMixin): ...@@ -100,7 +101,7 @@ class testSyncMLAsynchronousEngine(TestERP5SyncMLMixin):
def _fillModule(self, module, nb_objects): def _fillModule(self, module, nb_objects):
self.title_list = [] self.title_list = []
append = self.title_list.append append = self.title_list.append
for x in xrange(nb_objects): for x in range(nb_objects):
module.newContent(title=str(x)) module.newContent(title=str(x))
append(str(x)) append(str(x))
......
...@@ -123,7 +123,7 @@ def Base_asXML(object, root=None): ...@@ -123,7 +123,7 @@ def Base_asXML(object, root=None):
# Create blocks to represent data # Create blocks to represent data
# <data><block>ZERD</block><block>OEJJM</block></data> # <data><block>ZERD</block><block>OEJJM</block></data>
size_block = 60 size_block = 60
if isinstance(value, str): if isinstance(value, bytes):
for index in xrange(0, len(value), size_block): for index in xrange(0, len(value), size_block):
content = value[index:index + size_block] content = value[index:index + size_block]
data_encoded = standard_b64encode(content) data_encoded = standard_b64encode(content)
...@@ -136,7 +136,10 @@ def Base_asXML(object, root=None): ...@@ -136,7 +136,10 @@ def Base_asXML(object, root=None):
for word in value] for word in value]
sub_object.append(marshaller(value)) sub_object.append(marshaller(value))
elif prop_type in ('text', 'string',): elif prop_type in ('text', 'string',):
sub_object.text = six.text_type(escape(value), 'utf-8') value = escape(value)
if six.PY2:
value = six.text_type(value, 'utf-8')
sub_object.text = value
elif prop_type != 'None': elif prop_type != 'None':
sub_object.text = str(value) sub_object.text = str(value)
...@@ -160,15 +163,22 @@ def Base_asXML(object, root=None): ...@@ -160,15 +163,22 @@ def Base_asXML(object, root=None):
variable_node = SubElement(workflow_node, workflow_variable, variable_node = SubElement(workflow_node, workflow_variable,
attrib=dict(type=variable_type)) attrib=dict(type=variable_type))
if variable_type != 'None': if variable_type != 'None':
variable_node.text = six.text_type(str(variable_node_text), 'utf-8') variable_node_text = str(variable_node_text)
if six.PY2:
variable_node_text = six.text_type(str(variable_node_text), 'utf-8')
variable_node.text = variable_node_text
if workflow_variable == 'time': if workflow_variable == 'time':
time = variable_node.text time = variable_node.text
elif workflow_variable == 'actor': elif workflow_variable == 'actor':
actor = variable_node.text actor = variable_node.text
workflow_node.attrib['id'] = sha1(workflow_id + time + if six.PY2 and isinstance(actor, six.text_type):
str(actor.encode('utf-8'))).hexdigest() actor = actor.encode('utf-8')
workflow_transition_id = workflow_id + time + actor
if six.PY3:
workflow_transition_id = workflow_transition_id.encode()
workflow_node.attrib['id'] = sha1(workflow_transition_id).hexdigest()
# We should now describe security settings # We should now describe security settings
for user_role in self.get_local_roles(): for user_role in self.get_local_roles():
...@@ -177,7 +187,7 @@ def Base_asXML(object, root=None): ...@@ -177,7 +187,7 @@ def Base_asXML(object, root=None):
#convert local_roles in string because marshaller can't do it #convert local_roles in string because marshaller can't do it
role_list = [] role_list = []
for role in user_role[1]: for role in user_role[1]:
if isinstance(role, six.text_type): if six.PY2 and isinstance(role, six.text_type):
role = role.encode('utf-8') role = role.encode('utf-8')
role_list.append(role) role_list.append(role)
local_role_node.append(marshaller(tuple(role_list))) local_role_node.append(marshaller(tuple(role_list)))
......
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