Commit ab628670 authored by Hanno Schlichting's avatar Hanno Schlichting

Make webdav/ftp methods conditionally available based on ZServer presence.

parent ed87506a
......@@ -20,6 +20,7 @@ import warnings
from AccessControl.class_init import InitializeClass
from AccessControl.SecurityInfo import ClassSecurityInfo
from Acquisition import Explicit
from App import bbb
from App.Common import package_home
from App.Common import rfc1123_date
from App.config import getConfiguration
......@@ -119,6 +120,7 @@ class ImageFile(Explicit):
return filestream_iterator(self.path, mode='rb')
if bbb.HAS_ZSERVER:
security.declarePublic('HEAD')
def HEAD(self, REQUEST, RESPONSE):
""" """
......
......@@ -10,45 +10,11 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""FTP Support for Zope classes.
Preliminary FTP support interface. Note, most FTP functions are
provided by existing methods such as PUT and manage_delObjects.
import pkg_resources
All FTP methods should be governed by a single permission:
'FTP access'.
"""
from zope.interface import implements
from interfaces import IFTPAccess
class FTPInterface:
"Interface for FTP objects"
implements(IFTPAccess)
# XXX The stat and list marshal format should probably
# be XML, not marshal, maybe Andrew K's xml-marshal.
# This will probably be changed later.
def manage_FTPstat(self, REQUEST):
"""Returns a stat-like tuple. (marshalled to a string) Used by
FTP for directory listings, and MDTM and SIZE"""
def manage_FTPlist(self, REQUEST):
"""Returns a directory listing consisting of a tuple of
(id,stat) tuples, marshaled to a string. Note, the listing it
should include '..' if there is a Folder above the current
one.
In the case of non-foldoid objects it should return a single
tuple (id,stat) representing itself."""
# Optional method to support FTP download.
# Should not be implemented by Foldoid objects.
def manage_FTPget(self):
"""Returns the source content of an object. For example, the
source text of a Document, or the data of a file."""
HAS_ZSERVER = True
try:
dist = pkg_resources.get_distribution('ZServer')
except pkg_resources.DistributionNotFound:
HAS_ZSERVER = False
......@@ -133,6 +133,7 @@ class Application(ApplicationDefaultPermissions, Folder.Folder):
"""Utility function to return current date/time"""
return DateTime(*args)
if bbb.HAS_ZSERVER:
def DELETE(self, REQUEST, RESPONSE):
"""Delete a resource object."""
self.dav__init(REQUEST, RESPONSE)
......
......@@ -31,6 +31,7 @@ from AccessControl.requestmethod import requestmethod
from AccessControl.tainted import TaintedString
from DocumentTemplate.permissions import change_dtml_methods
from DocumentTemplate.security import RestrictedDTML
from OFS import bbb
from OFS.Cache import Cacheable
from OFS.History import Historical
from OFS.History import html_diff
......@@ -366,6 +367,7 @@ class DTMLMethod(RestrictedDTML,
RESPONSE.setHeader('Content-Type', 'text/plain')
return self.read()
if bbb.HAS_ZSERVER:
security.declareProtected(change_dtml_methods, 'PUT')
def PUT(self, REQUEST, RESPONSE):
""" Handle FTP / HTTP PUT requests.
......
......@@ -37,6 +37,7 @@ from zope.contenttype import guess_content_type
from zope.interface import implementedBy
from zope.interface import implements
from OFS import bbb
from OFS.Cache import Cacheable
from OFS.interfaces import IWriteLock
from OFS.PropertyManager import PropertyManager
......@@ -595,23 +596,6 @@ class File(Persistent, Implicit, PropertyManager,
return next, size
security.declareProtected(change_images_and_files, 'PUT')
def PUT(self, REQUEST, RESPONSE):
"""Handle HTTP PUT requests"""
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
type = REQUEST.get_header('content-type', None)
file = REQUEST['BODYFILE']
data, size = self._read_data(file)
content_type = self._get_content_type(file, data, self.__name__,
type or self.content_type)
self.update_data(data, content_type, size)
RESPONSE.setStatus(204)
return RESPONSE
security.declareProtected(View, 'get_size')
def get_size(self):
# Get the size of a file or image.
......@@ -636,6 +620,24 @@ class File(Persistent, Implicit, PropertyManager,
def __len__(self):
return 1
if bbb.HAS_ZSERVER:
security.declareProtected(change_images_and_files, 'PUT')
def PUT(self, REQUEST, RESPONSE):
"""Handle HTTP PUT requests"""
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
type = REQUEST.get_header('content-type', None)
file = REQUEST['BODYFILE']
data, size = self._read_data(file)
content_type = self._get_content_type(file, data, self.__name__,
type or self.content_type)
self.update_data(data, content_type, size)
RESPONSE.setStatus(204)
return RESPONSE
security.declareProtected(ftp_access, 'manage_FTPstat')
security.declareProtected(ftp_access, 'manage_FTPlist')
......@@ -647,11 +649,11 @@ class File(Persistent, Implicit, PropertyManager,
if self.ZCacheable_isCachingEnabled():
result = self.ZCacheable_get(default=None)
if result is not None:
# We will always get None from RAMCacheManager but we will get
# something implementing the IStreamIterator interface
# We will always get None from RAMCacheManager but we will
# get something implementing the IStreamIterator interface
# from FileCacheManager.
# the content-length is required here by HTTPResponse, even
# though FTP doesn't use it.
# the content-length is required here by HTTPResponse,
# even though FTP doesn't use it.
RESPONSE.setHeader('Content-Length', self.size)
return result
......
......@@ -596,8 +596,15 @@ class ObjectManager(CopyContainer,
listing.sort()
return listing
# FTP support methods
security.declareProtected(ftp_access, 'manage_hasId')
def manage_hasId(self, REQUEST):
""" check if the folder has an object with REQUEST['id'] """
if not REQUEST['id'] in self.objectIds():
raise KeyError(REQUEST['id'])
if bbb.HAS_ZSERVER:
# FTP support methods
security.declareProtected(ftp_access, 'manage_FTPlist')
def manage_FTPlist(self, REQUEST):
"""Directory listing for FTP.
......@@ -608,7 +615,8 @@ class ObjectManager(CopyContainer,
ob = self
while 1:
if is_acquired(ob):
raise ValueError('FTP List not supported on acquired objects')
raise ValueError(
'FTP List not supported on acquired objects')
if not hasattr(ob, '__parent__'):
break
ob = aq_parent(ob)
......@@ -652,13 +660,6 @@ class ObjectManager(CopyContainer,
out = out + ((k, stat),)
return marshal.dumps(out)
security.declareProtected(ftp_access, 'manage_hasId')
def manage_hasId(self, REQUEST):
""" check if the folder has an object with REQUEST['id'] """
if not REQUEST['id'] in self.objectIds():
raise KeyError(REQUEST['id'])
security.declareProtected(ftp_access, 'manage_FTPstat')
def manage_FTPstat(self, REQUEST):
"""Psuedo stat, used by FTP for directory listings.
......
......@@ -270,6 +270,7 @@ class Item(Base,
return ()
objectIds = objectItems = objectValues
if bbb.HAS_ZSERVER:
# FTP support methods
def manage_FTPstat(self, REQUEST):
......
......@@ -15,6 +15,7 @@
from zope.component.interfaces import IPossibleSite
from zope.container.interfaces import IContainer
from zope.deferredimport import deprecated
from zope.interface import Attribute
from zope.interface import Interface
from zope.interface.interfaces import IObjectEvent
......@@ -171,27 +172,6 @@ class ICopySource(Interface):
"""
# XXX: might contain non-API methods and outdated comments;
# not synced with ZopeBook API Reference;
# based on OFS.FTPInterface.FTPInterface
class IFTPAccess(Interface):
"""Provide support for FTP access"""
def manage_FTPstat(REQUEST):
"""Returns a stat-like tuple. (marshalled to a string) Used by
FTP for directory listings, and MDTM and SIZE"""
def manage_FTPlist(REQUEST):
"""Returns a directory listing consisting of a tuple of
(id,stat) tuples, marshaled to a string. Note, the listing it
should include '..' if there is a Folder above the current
one.
In the case of non-foldoid objects it should return a single
tuple (id,stat) representing itself."""
# XXX: might contain non-API methods and outdated comments;
# not synced with ZopeBook API Reference;
# based on OFS.Traversable.Traversable
......@@ -519,7 +499,7 @@ class ILockItem(Interface):
# XXX: might contain non-API methods and outdated comments;
# not synced with ZopeBook API Reference;
# based on OFS.SimpleItem.Item
class IItem(IZopeObject, IManageable, IFTPAccess,
class IItem(IZopeObject, IManageable,
ICopySource, ITraversable, IOwned):
__name__ = BytesLine(title=u"Name")
......@@ -1057,3 +1037,10 @@ class IObjectClonedEvent(IObjectEvent):
event.object is the copied object, already added to its container.
Note that this event is dispatched to all sublocations.
"""
# BBB Zope 5.0
deprecated(
'Please import from webdav.interfaces.',
IFTPAccess='webdav.interfaces:IFTPAccess',
)
import unittest
class TestFTPInterface(unittest.TestCase):
def test_interfaces(self):
from OFS.interfaces import IFTPAccess
from OFS.FTPInterface import FTPInterface
from zope.interface.verify import verifyClass
verifyClass(IFTPAccess, FTPInterface)
......@@ -257,27 +257,6 @@ class FileTests(unittest.TestCase):
self.assertEqual(resp.getStatus(), 200)
self.assertEqual(data, str(self.file.data))
def testPUT(self):
s = '# some python\n'
# with content type
data = StringIO(s)
req = aputrequest(data, 'text/x-python')
req.processInputs()
self.file.PUT(req, req.RESPONSE)
self.assertEqual(self.file.content_type, 'text/x-python')
self.assertEqual(str(self.file.data), s)
# without content type
data.seek(0)
req = aputrequest(data, '')
req.processInputs()
self.file.PUT(req, req.RESPONSE)
self.assertEqual(self.file.content_type, 'text/x-python')
self.assertEqual(str(self.file.data), s)
def testIndexHtmlWithPdata(self):
self.file.manage_upload('a' * (2 << 16)) # 128K
self.file.index_html(self.app.REQUEST, self.app.REQUEST.RESPONSE)
......
......@@ -37,6 +37,7 @@ from Shared.DC.Scripts.Script import Script
from Shared.DC.Scripts.Signature import FuncCode
from zExceptions import ResourceLockedError
from Products.PageTemplates import bbb
from Products.PageTemplates.PageTemplate import PageTemplate
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PageTemplates.PageTemplateFile import guess_type
......@@ -344,6 +345,7 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
'manage_beforeHistoryCopy',
'manage_afterHistoryCopy')
if bbb.HAS_ZSERVER:
security.declareProtected(change_page_templates, 'PUT')
def PUT(self, REQUEST, RESPONSE):
""" Handle HTTP PUT requests """
......@@ -359,7 +361,8 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
security.declareProtected(change_page_templates, 'manage_FTPput')
manage_FTPput = PUT
security.declareProtected(ftp_access, 'manage_FTPstat', 'manage_FTPlist')
security.declareProtected(ftp_access, 'manage_FTPstat')
security.declareProtected(ftp_access, 'manage_FTPlist')
security.declareProtected(ftp_access, 'manage_FTPget')
def manage_FTPget(self):
"Get source for FTP download"
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import pkg_resources
HAS_ZSERVER = True
try:
dist = pkg_resources.get_distribution('ZServer')
except pkg_resources.DistributionNotFound:
HAS_ZSERVER = False
......@@ -15,6 +15,7 @@ from zope.publisher.http import HTTPCharsets
from Testing.makerequest import makerequest
from Testing.ZopeTestCase import ZopeTestCase, installProduct
from Products.PageTemplates.PageTemplateFile import guess_type
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
from Products.PageTemplates.ZopePageTemplate import manage_addPageTemplate
from Products.PageTemplates.utils import encodingFromXMLPreamble
......@@ -308,9 +309,8 @@ class ZopePageTemplateFileTests(ZopeTestCase):
def _put(self, text):
zpt = self._createZPT()
REQUEST = self.app.REQUEST
REQUEST.set('BODY', text)
zpt.PUT(REQUEST, REQUEST.RESPONSE)
content_type = guess_type('', text)
zpt.pt_edit(text, content_type)
return zpt
def testPutHTMLIso8859_15WithCharsetInfo(self):
......@@ -417,14 +417,6 @@ class ZPTRegressions(unittest.TestCase):
pt = self.app.pt1
self.assertEqual(pt.document_src(), self.text)
def testFTPGet(self):
# check for bug #2269
request = self.app.REQUEST
text = '<span tal:content="string:foobar"></span>'
self._addPT('pt1', text=text, REQUEST=request)
result = self.app.pt1.manage_FTPget()
self.assertEqual(result, text)
class ZPTMacros(zope.component.testing.PlacelessSetup, unittest.TestCase):
......
......@@ -120,18 +120,6 @@ class TestFunctional(ZopeTestCase.FunctionalTestCase):
self.assertEqual(response.getStatus(), 200)
self.assertEqual(self.folder.index_html.title_or_id(), 'Foo')
def testPUTExisting(self):
# FTP new data into an existing object
self.setPermissions([change_dtml_documents])
put_data = StringIO('foo')
response = self.publish(self.folder_path + '/index_html',
request_method='PUT', stdin=put_data,
basic=self.basic_auth)
self.assertEqual(response.getStatus(), 204)
self.assertEqual(self.folder.index_html(), 'foo')
def testHEAD(self):
# HEAD should work without passing stdin
response = self.publish(self.folder_path + '/index_html',
......
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