Commit 88f4bed7 authored by 's avatar

cleanup

parent 5edd9bec
...@@ -85,10 +85,11 @@ ...@@ -85,10 +85,11 @@
"""WebDAV support - collection objects.""" """WebDAV support - collection objects."""
__version__='$Revision: 1.1 $'[11:-2] __version__='$Revision: 1.2 $'[11:-2]
import sys, os, string, mimetypes import sys, os, string
from Resource import Resource from Resource import Resource
from common import urlfix
class Collection(Resource): class Collection(Resource):
...@@ -96,8 +97,9 @@ class Collection(Resource): ...@@ -96,8 +97,9 @@ class Collection(Resource):
collection objects. It provides default implementations collection objects. It provides default implementations
for all supported WebDAV HTTP methods. The behaviors of some for all supported WebDAV HTTP methods. The behaviors of some
WebDAV HTTP methods for collections are slightly different WebDAV HTTP methods for collections are slightly different
than those for non-collection resources. than those for non-collection resources."""
"""
__dav_collection__=1
def redirect_check(self, req, rsp): def redirect_check(self, req, rsp):
# By the spec, we are not supposed to accept /foo for a # By the spec, we are not supposed to accept /foo for a
...@@ -126,14 +128,13 @@ class Collection(Resource): ...@@ -126,14 +128,13 @@ class Collection(Resource):
"""Delete a collection resource. For collection resources, DELETE """Delete a collection resource. For collection resources, DELETE
may return either 200 (OK) or 204 (No Content) to indicate total may return either 200 (OK) or 204 (No Content) to indicate total
success, or may return 207 (Multistatus) to indicate partial success, or may return 207 (Multistatus) to indicate partial
success.""" success. Note that in Zope a DELETE never returns 207."""
self.init_headers(RESPONSE) self.init_headers(RESPONSE)
self.redirect_check(REQUEST, RESPONSE) self.redirect_check(REQUEST, RESPONSE)
if self.dav__is_acquired(): url=urlfix(REQUEST['URL'], 'DELETE')
raise 'Not Found', 'The requested resource does not exist.' name=filter(None, string.split(url, '/'))[-1]
path=filter(None, string.split(REQUEST['PATH_INFO'], '/')) # TODO: add lock checking here
name=path[-1]
# TODO: add lock check here
self.aq_parent._delObject(name) self.aq_parent._delObject(name)
RESPONSE.setStatus(204) RESPONSE.setStatus(204)
return RESPONSE return RESPONSE
...@@ -85,22 +85,25 @@ ...@@ -85,22 +85,25 @@
"""WebDAV support - null resource objects.""" """WebDAV support - null resource objects."""
__version__='$Revision: 1.1 $'[11:-2] __version__='$Revision: 1.2 $'[11:-2]
import sys, os, string, mimetypes import sys, os, string, mimetypes
import Acquisition, OFS.content_types import Acquisition, OFS.content_types
from Resource import Resource, aq_base from common import absattr, aq_base, urlfix
from Resource import Resource
from Globals import Persistent
class NullResource(Acquisition.Implicit, Resource): class NullResource(Persistent, Acquisition.Implicit, Resource):
"""Null resources are used to handle HTTP method calls on """Null resources are used to handle HTTP method calls on
objects which do not yet exist in the url namespace.""" objects which do not yet exist in the url namespace."""
_isNullResource=1
__dav_null__=1
def __init__(self, parent, id): def __init__(self, parent, id):
self.id=id self.id=id
self.__parent__=parent self.__parent__=parent
self.__roles__=None # fix this!! self.__roles__=parent.__roles__
def HEAD(self, REQUEST, RESPONSE): def HEAD(self, REQUEST, RESPONSE):
"""Retrieve resource information without a response message body.""" """Retrieve resource information without a response message body."""
...@@ -143,8 +146,7 @@ class NullResource(Acquisition.Implicit, Resource): ...@@ -143,8 +146,7 @@ class NullResource(Acquisition.Implicit, Resource):
parent=self.__parent__ parent=self.__parent__
if hasattr(aq_base(parent), self.id): if hasattr(aq_base(parent), self.id):
raise 'Method Not Allowed', 'The name %s is in use.' % self.id raise 'Method Not Allowed', 'The name %s is in use.' % self.id
if (not hasattr(parent.aq_base, 'isAnObjectManager')) or \ if not hasattr(parent, '__dav_collection__'):
(not parent.isAnObjectManager):
raise 'Forbidden', 'Unable to create collection resource.' raise 'Forbidden', 'Unable to create collection resource.'
# This should probably do self.__class__(id, ...), except Folder # This should probably do self.__class__(id, ...), except Folder
# doesn't currently have a constructor. # doesn't currently have a constructor.
...@@ -161,5 +163,4 @@ class NullResource(Acquisition.Implicit, Resource): ...@@ -161,5 +163,4 @@ class NullResource(Acquisition.Implicit, Resource):
def UNLOCK(self): def UNLOCK(self):
"""Remove a lock-null resource.""" """Remove a lock-null resource."""
self.init_headers(RESPONSE) self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.' raise 'Method Not Allowed', 'Method not supported for this resource.'
...@@ -85,13 +85,12 @@ ...@@ -85,13 +85,12 @@
"""WebDAV support - resource objects.""" """WebDAV support - resource objects."""
__version__='$Revision: 1.1 $'[11:-2] __version__='$Revision: 1.2 $'[11:-2]
import sys, os, string, time import sys, os, string, time
import mimetypes, xmlcmds import mimetypes, xmlcmds
from common import absattr, aq_base
zpns='http://www.zope.org/propertysets/default/' from common import urlfix, rfc1123_date
class Resource: class Resource:
...@@ -101,13 +100,12 @@ class Resource: ...@@ -101,13 +100,12 @@ class Resource:
such as PUT should be overridden to ensure correct behavior in such as PUT should be overridden to ensure correct behavior in
the context of the object type.""" the context of the object type."""
__dav_resource__=1
__http_methods__=('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS', __http_methods__=('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS',
'TRACE', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'TRACE', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY',
'MOVE', 'MOVE',
) )
__dav_resource__=1
def init_headers(self, r): def init_headers(self, r):
# Init expected HTTP 1.1 / WebDAV headers which are not # Init expected HTTP 1.1 / WebDAV headers which are not
# currently set by the response object automagically. # currently set by the response object automagically.
...@@ -130,35 +128,16 @@ class Resource: ...@@ -130,35 +128,16 @@ class Resource:
lock=Lock('xxxx', 'xxxx') lock=Lock('xxxx', 'xxxx')
return self.dav__locks + (lock,) return self.dav__locks + (lock,)
def dav__is_acquired(self, ob=None):
# Return true if this object is not a direct
# subobject of its aq_parent object.
if ob is None: ob=self
if not hasattr(ob, 'aq_parent'):
return 0
if hasattr(aq_base(ob.aq_parent), absattr(ob.id)):
return 0
if hasattr(aq_base(ob), 'isTopLevelPrincipiaApplicationObject'):
return 0
return 1
# WebDAV class 1 support # WebDAV class 1 support
def HEAD(self, REQUEST, RESPONSE): def HEAD(self, REQUEST, RESPONSE):
"""Retrieve resource information without a response message """Retrieve resource information without a response body."""
body. It would be great if we had a standard way to ask an
arbitrary object for its headers -- that would allow the
default HEAD implementation to handle most needs."""
self.init_headers(RESPONSE) self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.' raise 'Method Not Allowed', 'Method not supported for this resource.'
def PUT(self, REQUEST, RESPONSE): def PUT(self, REQUEST, RESPONSE):
"""Replace the GET response entity of an existing resource. """Replace the GET response entity of an existing resource.
Because this is often object-dependent, objects which handle Because this is often object-dependent, objects which handle
PUT should override the default PUT implementation with an PUT should override the default PUT implementation with an
object-specific implementation. By default, PUT requests object-specific implementation. By default, PUT requests
...@@ -176,7 +155,7 @@ class Resource: ...@@ -176,7 +155,7 @@ class Resource:
def TRACE(self, REQUEST, RESPONSE): def TRACE(self, REQUEST, RESPONSE):
"""Return the HTTP message received back to the client as the """Return the HTTP message received back to the client as the
entity-body of a 200 (OK) response. This will often actually entity-body of a 200 (OK) response. This will often usually
be intercepted by the web server in use. If not, the TRACE be intercepted by the web server in use. If not, the TRACE
request will fail with a 405 (Method Not Allowed), since it request will fail with a 405 (Method Not Allowed), since it
is not often possible to reproduce the HTTP request verbatim is not often possible to reproduce the HTTP request verbatim
...@@ -188,12 +167,8 @@ class Resource: ...@@ -188,12 +167,8 @@ class Resource:
"""Delete a resource. For non-collection resources, DELETE may """Delete a resource. For non-collection resources, DELETE may
return either 200 or 204 (No Content) to indicate success.""" return either 200 or 204 (No Content) to indicate success."""
self.init_headers(RESPONSE) self.init_headers(RESPONSE)
if self.dav__is_acquired(): url=urlfix(REQUEST['URL'], 'DELETE')
raise 'Not Found', 'The requested resource does not exist.' name=filter(None, string.split(url, '/'))[-1]
path=filter(None, string.split(REQUEST['URL'], '/'))
if path[-1]=='DELETE':
del path[-1]
name=path[-1]
# TODO: add lock checking here # TODO: add lock checking here
self.aq_parent._delObject(name) self.aq_parent._delObject(name)
RESPONSE.setStatus(204) RESPONSE.setStatus(204)
...@@ -202,11 +177,9 @@ class Resource: ...@@ -202,11 +177,9 @@ class Resource:
def PROPFIND(self, REQUEST, RESPONSE): def PROPFIND(self, REQUEST, RESPONSE):
"""Retrieve properties defined on the resource.""" """Retrieve properties defined on the resource."""
self.init_headers(RESPONSE) self.init_headers(RESPONSE)
if self.dav__is_acquired(): try: cmd=xmlcmds.PropFind(REQUEST)
raise 'Not Found', 'The requested resource does not exist.'
try: request=xmlcmds.PropFind(REQUEST)
except: raise 'Bad Request', 'Invalid xml request.' except: raise 'Bad Request', 'Invalid xml request.'
result=request.apply(self) result=cmd.apply(self)
RESPONSE.setStatus(207) RESPONSE.setStatus(207)
RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"') RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"')
RESPONSE.setBody(result) RESPONSE.setBody(result)
...@@ -215,15 +188,13 @@ class Resource: ...@@ -215,15 +188,13 @@ class Resource:
def PROPPATCH(self, REQUEST, RESPONSE): def PROPPATCH(self, REQUEST, RESPONSE):
"""Set and/or remove properties defined on the resource.""" """Set and/or remove properties defined on the resource."""
self.init_headers(RESPONSE) self.init_headers(RESPONSE)
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
if not hasattr(self, '__propsets__'): if not hasattr(self, '__propsets__'):
raise 'Method Not Allowed', ( raise 'Method Not Allowed', (
'Method not supported for this resource.') 'Method not supported for this resource.')
# TODO: add lock checking here # TODO: add lock checking here
try: request=xmlcmds.PropPatch(REQUEST) try: cmd=xmlcmds.PropPatch(REQUEST)
except: raise 'Bad Request', 'Invalid xml request.' except: raise 'Bad Request', 'Invalid xml request.'
result=request.apply(self) result=cmd.apply(self)
RESPONSE.setStatus(207) RESPONSE.setStatus(207)
RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"') RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"')
RESPONSE.setBody(result) RESPONSE.setBody(result)
...@@ -245,8 +216,6 @@ class Resource: ...@@ -245,8 +216,6 @@ class Resource:
if not hasattr(aq_base(self), 'cb_isCopyable') or \ if not hasattr(aq_base(self), 'cb_isCopyable') or \
not self.cb_isCopyable(): not self.cb_isCopyable():
raise 'Method Not Allowed', 'This object may not be copied.' raise 'Method Not Allowed', 'This object may not be copied.'
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
depth=REQUEST.get_header('Depth', 'infinity') depth=REQUEST.get_header('Depth', 'infinity')
dest=REQUEST.get_header('Destination', '') dest=REQUEST.get_header('Destination', '')
if not dest: raise 'Bad Request', 'No destination given' if not dest: raise 'Bad Request', 'No destination given'
...@@ -261,7 +230,7 @@ class Resource: ...@@ -261,7 +230,7 @@ class Resource:
except 'Not Found': except 'Not Found':
raise 'Conflict', 'The resource %s must exist.' % path raise 'Conflict', 'The resource %s must exist.' % path
except: raise sys.exc_type, sys.exc_value except: raise sys.exc_type, sys.exc_value
if hasattr(parent, '_isNullResource'): if hasattr(parent, '__dav_null__'):
raise 'Conflict', 'The resource %s must exist.' % path raise 'Conflict', 'The resource %s must exist.' % path
if self.dav__is_acquired(parent): if self.dav__is_acquired(parent):
raise 'Conflict', 'The resource %s must exist.' % path raise 'Conflict', 'The resource %s must exist.' % path
...@@ -296,8 +265,6 @@ class Resource: ...@@ -296,8 +265,6 @@ class Resource:
if not hasattr(aq_base(self), 'cb_isMoveable') or \ if not hasattr(aq_base(self), 'cb_isMoveable') or \
not self.cb_isMoveable(): not self.cb_isMoveable():
raise 'Method Not Allowed', 'This object may not be moved.' raise 'Method Not Allowed', 'This object may not be moved.'
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
dest=REQUEST.get_header('Destination', '') dest=REQUEST.get_header('Destination', '')
if not dest: raise 'Bad Request', 'No destination given' if not dest: raise 'Bad Request', 'No destination given'
flag=REQUEST.get_header('Overwrite', 'F') flag=REQUEST.get_header('Overwrite', 'F')
...@@ -311,7 +278,7 @@ class Resource: ...@@ -311,7 +278,7 @@ class Resource:
except 'Not Found': except 'Not Found':
raise 'Conflict', 'The resource %s must exist.' % path raise 'Conflict', 'The resource %s must exist.' % path
except: raise sys.exc_type, sys.exc_value except: raise sys.exc_type, sys.exc_value
if hasattr(parent, '_isNullResource'): if hasattr(parent, '__dav_null__'):
raise 'Conflict', 'The resource %s must exist.' % path raise 'Conflict', 'The resource %s must exist.' % path
if self.dav__is_acquired(parent): if self.dav__is_acquired(parent):
raise 'Conflict', 'The resource %s must exist.' % path raise 'Conflict', 'The resource %s must exist.' % path
...@@ -377,25 +344,3 @@ class Lock: ...@@ -377,25 +344,3 @@ class Lock:
'<d:href>opaquelocktoken:%(token)s</d:href>\n' \ '<d:href>opaquelocktoken:%(token)s</d:href>\n' \
'</d:locktoken>\n' \ '</d:locktoken>\n' \
'</d:activelock>\n' % self.__dict__ '</d:activelock>\n' % self.__dict__
def absattr(attr):
if callable(attr):
return attr()
return attr
def aq_base(ob):
if hasattr(ob, 'aq_base'):
return ob.aq_base
return ob
def rfc1123_date(ts=None):
# Return an RFC 1123 format date string, required for
# use in HTTP Date headers per the HTTP 1.1 spec.
if ts is None: ts=time.time()
ts=time.asctime(time.gmtime(ts))
ts=string.split(ts)
return '%s, %s %s %s %s GMT' % (ts[0],ts[2],ts[1],ts[3],ts[4])
...@@ -84,7 +84,10 @@ ...@@ -84,7 +84,10 @@
############################################################################## ##############################################################################
"""The webdav package provides WebDAV class 1 functionality within """The webdav package provides WebDAV class 1 functionality within
the Zope environment. Based on RFC 2518.""" the Zope environment. Based on:
__version__='$Revision: 1.2 $'[11:-2] [WebDAV] Y. Y. Goland, E. J. Whitehead, Jr., A. Faizi, S. R. Carter, D.
Jensen, "HTTP Extensions for Distributed Authoring - WebDAV." RFC 2518.
Microsoft, U.C. Irvine, Netscape, Novell. February, 1999."""
__version__='$Revision: 1.3 $'[11:-2]
This diff is collapsed.
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
"""Commonly used functions for WebDAV support modules."""
__version__='$Revision: 1.1 $'[11:-2]
import string, time
def absattr(attr):
if callable(attr):
return attr()
return attr
def aq_base(ob):
if hasattr(ob, 'aq_base'):
return ob.aq_base
return ob
def urlfix(url, s):
n=len(s)
if url[-n:]==s: url=url[:-n]
if len(url) > 1 and url[-1]=='/':
url=url[:-1]
return url
def is_acquired(ob):
# Return true if this object is not a direct
# subobject of its aq_parent object.
if not hasattr(ob, 'aq_parent'):
return 0
if hasattr(aq_base(ob.aq_parent), absattr(ob.id)):
return 0
if hasattr(aq_base(ob), 'isTopLevelPrincipiaApplicationObject'):
return 0
return 1
def rfc1123_date(ts=None):
# Return an RFC 1123 format date string, required for
# use in HTTP Date headers per the HTTP 1.1 spec.
if ts is None: ts=time.time()
ts=time.asctime(time.gmtime(ts))
ts=string.split(ts)
return '%s, %s %s %s %s GMT' % (ts[0],ts[2],ts[1],ts[3],ts[4])
...@@ -83,24 +83,20 @@ ...@@ -83,24 +83,20 @@
# #
############################################################################## ##############################################################################
"""WebDAV XML request objects.""" """WebDAV xml request objects."""
__version__='$Revision: 1.1 $'[11:-2]
import sys, os, string, xmllib __version__='$Revision: 1.2 $'[11:-2]
import sys, os, string
from common import absattr, aq_base, urlfix
from xmltools import XmlParser from xmltools import XmlParser
from cStringIO import StringIO from cStringIO import StringIO
zope_id='http://www.zope.org/propsets/default'
dav_id='DAV:'
def compact(self, data):
root=XmlParser().parse(data)
class PropFind: class PropFind:
"""Model a PROPFIND request."""
def __init__(self, request): def __init__(self, request):
self.request=request self.request=request
data=request.get('BODY', '') data=request.get('BODY', '')
...@@ -110,17 +106,17 @@ class PropFind: ...@@ -110,17 +106,17 @@ class PropFind:
self.propnames=[] self.propnames=[]
self.parse(data) self.parse(data)
def parse(self, data): def parse(self, data, dav='DAV:'):
if not data: return if not data: return
root=XmlParser().parse(data) root=XmlParser().parse(data)
e=root.elements('propfind', ns=dav_id)[0] e=root.elements('propfind', ns=dav)[0]
if e.elements('allprop', ns=dav_id): if e.elements('allprop', ns=dav):
self.allprop=1 self.allprop=1
return return
if e.elements('propname', ns=dav_id): if e.elements('propname', ns=dav):
self.propname=1 self.propname=1
return return
prop=e.elements('prop', ns=dav_id)[0] prop=e.elements('prop', ns=dav)[0]
for val in prop.elements(): for val in prop.elements():
self.propnames.append((val.name(), val.namespace())) self.propnames.append((val.name(), val.namespace()))
return return
...@@ -129,19 +125,15 @@ class PropFind: ...@@ -129,19 +125,15 @@ class PropFind:
if result is None: if result is None:
result=StringIO() result=StringIO()
depth=self.depth depth=self.depth
url=self.request['URL'] url=urlfix(self.request['URL'], 'PROPFIND')
if url[-9:]=='/PROPFIND':
url=url[:-9]
result.write('<?xml version="1.0" encoding="utf-8"?>\n' \ result.write('<?xml version="1.0" encoding="utf-8"?>\n' \
'<d:multistatus xmlns:d="DAV:" ' \ '<d:multistatus xmlns:d="DAV:">\n'
'xmlns:z="%s">\n' % zope_id)
iscol=hasattr(aq_base(obj), 'isAnObjectManager') and \ iscol=hasattr(aq_base(obj), 'isAnObjectManager') and \
obj.isAnObjectManager obj.isAnObjectManager
if iscol and url[-1] != '/': url=url+'/' if iscol and url[-1] != '/': url=url+'/'
result.write('<d:response>\n<d:href>%s</d:href>\n' % url) result.write('<d:response>\n<d:href>%s</d:href>\n' % url)
if hasattr(obj, '__propsets__'): if hasattr(obj, '__propsets__'):
for ps in obj.propertysheets.items(): for ps in obj.propertysheets.values():
if hasattr(aq_base(ps), 'dav__propstat'): if hasattr(aq_base(ps), 'dav__propstat'):
stat=ps.dav__propstat(self.allprop, self.propnames) stat=ps.dav__propstat(self.allprop, self.propnames)
result.write(stat) result.write(stat)
...@@ -160,18 +152,19 @@ class PropFind: ...@@ -160,18 +152,19 @@ class PropFind:
class PropPatch: class PropPatch:
"""Model a PROPPATCH request."""
def __init__(self, request): def __init__(self, request):
self.request=request self.request=request
data=request.get('BODY', '') data=request.get('BODY', '')
self.values=[] self.values=[]
self.parse(data) self.parse(data)
def parse(self, data): def parse(self, data, dav='DAV:'):
root=XmlParser().parse(data) root=XmlParser().parse(data)
e=root.elements('propertyupdate', ns=dav_id)[0] e=root.elements('propertyupdate', ns=dav)[0]
for ob in e.elements(): for ob in e.elements():
if ob.name()=='set' and ob.namespace()==dav_id: if ob.name()=='set' and ob.namespace()==dav:
prop=ob.elements('prop', ns=dav_id)[0] prop=ob.elements('prop', ns=dav)[0]
for val in prop.elements(): for val in prop.elements():
# We have to ensure that all tag attrs (including # We have to ensure that all tag attrs (including
# an xmlns attr for all xml namespaces used by the # an xmlns attr for all xml namespaces used by the
...@@ -183,25 +176,23 @@ class PropPatch: ...@@ -183,25 +176,23 @@ class PropPatch:
md={'attrs':attrs, 'nsid': val.__nskey__} md={'attrs':attrs, 'nsid': val.__nskey__}
item=(val.name(), val.namespace(), val.strval(), md) item=(val.name(), val.namespace(), val.strval(), md)
self.values.append(item) self.values.append(item)
if ob.name()=='remove' and ob.namespace()==dav_id: if ob.name()=='remove' and ob.namespace()==dav:
prop=ob.elements('prop', ns=dav_id)[0] prop=ob.elements('prop', ns=dav)[0]
for val in prop.elements(): for val in prop.elements():
item=(val.name(), val.namespace()) item=(val.name(), val.namespace())
self.values.append(item) self.values.append(item)
def apply(self, obj): def apply(self, obj):
url=self.request['URL'] url=urlfix(self.request['URL'], 'PROPPATCH')
if url[-10:]=='/PROPPATCH':
url=url[:-10]
if hasattr(aq_base(obj), 'isAnObjectManager') and \ if hasattr(aq_base(obj), 'isAnObjectManager') and \
obj.isAnObjectManager and url[-1] != '/': obj.isAnObjectManager and url[-1] != '/':
url=url+'/' url=url+'/'
result=StringIO() result=StringIO()
errors=[] errors=[]
result.write('<?xml version="1.0" encoding="utf-8"?>\n' \ result.write('<?xml version="1.0" encoding="utf-8"?>\n' \
'<d:multistatus xmlns:d="DAV:" xmlns:z="%s">\n' \ '<d:multistatus xmlns:d="DAV:">\n' \
'<d:response>\n' \ '<d:response>\n' \
'<d:href>%s</d:href>\n' % (zope_id, url)) '<d:href>%s</d:href>\n' % url)
propsets=obj.propertysheets propsets=obj.propertysheets
for value in self.values: for value in self.values:
status='200 OK' status='200 OK'
...@@ -210,7 +201,7 @@ class PropPatch: ...@@ -210,7 +201,7 @@ class PropPatch:
propset=propsets.get(ns, None) propset=propsets.get(ns, None)
if propset is None: if propset is None:
obj.propertysheets.manage_addPropertySheet('', ns) obj.propertysheets.manage_addPropertySheet('', ns)
propsets=obj.propertysheets.items() propsets=obj.propertysheets.values()
propset=propsets.get(ns) propset=propsets.get(ns)
if propset.hasProperty(name): if propset.hasProperty(name):
try: propset._updateProperty(name, val, meta=md) try: propset._updateProperty(name, val, meta=md)
...@@ -234,9 +225,9 @@ class PropPatch: ...@@ -234,9 +225,9 @@ class PropPatch:
errors.append('%s cannot be deleted.' % name) errors.append('%s cannot be deleted.' % name)
status='409 Conflict' status='409 Conflict'
if result != '200 OK': abort=1 if result != '200 OK': abort=1
result.write('<d:propstat xmlns:ps="%s">\n' \ result.write('<d:propstat xmlns:n="%s">\n' \
' <d:prop>\n' \ ' <d:prop>\n' \
' <ps:%s/>\n' \ ' <n:%s/>\n' \
' </d:prop>\n' \ ' </d:prop>\n' \
' <d:status>HTTP/1.1 %s</d:status>\n' \ ' <d:status>HTTP/1.1 %s</d:status>\n' \
'</d:propstat>\n' % (ns, name, status)) '</d:propstat>\n' % (ns, name, status))
...@@ -256,117 +247,21 @@ class PropPatch: ...@@ -256,117 +247,21 @@ class PropPatch:
class Lock: class Lock:
def __init__(self, data): """Model a LOCK request."""
def __init__(self, request):
self.request=request
data=request.get('BODY', '')
self.scope='exclusive' self.scope='exclusive'
self.type='write' self.type='write'
self.owner='' self.owner=''
self.parse(data) self.parse(data)
def parse(self, data): def parse(self, data, dav='DAV:'):
root=XmlParser().parse(data) root=XmlParser().parse(data)
info=root.elements('lockinfo', ns=dav_id)[0] info=root.elements('lockinfo', ns=dav)[0]
ls=info.elements('lockscope', ns=dav_id)[0] ls=info.elements('lockscope', ns=dav)[0]
self.scope=ls.elements()[0].name() self.scope=ls.elements()[0].name()
lt=info.elements('locktype', ns=dav_id)[0] lt=info.elements('locktype', ns=dav)[0]
self.type=lt.elements()[0].name() self.type=lt.elements()[0].name()
lo=info.elements('owner', ns=dav_id) lo=info.elements('owner', ns=dav)
if lo: self.owner=lo[0].toxml() if lo: self.owner=lo[0].toxml()
def absattr(attr):
if callable(attr):
return attr()
return attr
def aq_base(ob):
if hasattr(ob, 'aq_base'):
return ob.aq_base
return ob
propfind_xml="""<?xml version="1.0" encoding="utf-8" ?>
<d:propfind xmlns:d="DAV:">
<d:prop xmlns:z="http://www.zope.org/propsets/default">
<z:title/>
<z:author/>
<z:content_type/>
</d:prop>
</d:propfind>
"""
rem_xml="""<?xml version="1.0" encoding="utf-8"?>
<d:propertyupdate xmlns:d="DAV:"
xmlns:z="http://www.zope.org/propsets/default">
<d:remove>
<d:prop>
<z:author/>
<z:title/>
</d:prop>
</d:remove>
</d:propertyupdate>
"""
proppatch_xml="""<?xml version="1.0" encoding="utf-8" ?>
<d:propertyupdate xmlns:d="DAV:"
xmlns:z="http://www.w3.com/standards/z39.50/">
<d:set>
<d:prop>
<z:authors>
<z:Author>Jim Whitehead</z:Author>
<z:Author>Roy Fielding</z:Author>
</z:authors>
</d:prop>
</d:set>
<d:remove>
<d:prop><z:Copyright-Owner/></d:prop>
</d:remove>
</d:propertyupdate>
"""
lock_xml="""<?xml version="1.0" encoding="utf-8" ?>
<D:lockinfo xmlns:D='DAV:'>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
<D:owner>
<D:href>http://www.ics.uci.edu/~ejw/contact.html</D:href>
</D:owner>
</D:lockinfo>
"""
multistatus_xml="""<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:">
<response xmlns:z="http://www.zope.org/dav/">
<href>http://www.foo.bar/container/</href>
<propstat>
<prop xmlns:R="http://www.foo.bar/boxschema/">
<R:bigbox z:type="int"/>
<R:author/>
<creationdate/>
<displayname/>
<resourcetype/>
<supportedlock/>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
<response>
<href>http://www.foo.bar/container/front.html</href>
<propstat>
<prop xmlns:R="http://www.foo.bar/boxschema/">
<R:bigbox/>
<creationdate/>
<displayname/>
<getcontentlength/>
<getcontenttype/>
<getetag/>
<getlastmodified/>
<resourcetype/>
<supportedlock/>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
</multistatus>
"""
...@@ -84,7 +84,8 @@ ...@@ -84,7 +84,8 @@
############################################################################## ##############################################################################
"""WebDAV XML parsing tools.""" """WebDAV XML parsing tools."""
__version__='$Revision: 1.1 $'[11:-2]
__version__='$Revision: 1.2 $'[11:-2]
import sys, os, string, xmllib import sys, os, string, xmllib
from Acquisition import Implicit from Acquisition import Implicit
......
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