def apply(self, obj, token, url=None, result=None, top=1): if result is None: result = StringIO() url = urlfix(url, 'UNLOCK') url = urlbase(url) iscol = isDavCollection(obj) if iscol and url[-1] != '/': url = url + '/' errmsg = None islockable = IWriteLock.providedBy(obj) or \ WriteLockInterface.isImplementedBy(obj) if islockable and obj.wl_hasLock(token): method = getattr(obj, 'wl_delLock') vld = getSecurityManager().validate(None,obj,'wl_delLock',method) if vld: obj.wl_delLock(token) else: errmsg = "403 Forbidden" elif not islockable: # Only set an error message if the command is being applied # to a top level object. Otherwise, we're descending a tree # which may contain many objects that don't implement locking, # so we just want to avoid them if top: errmsg = "405 Method Not Allowed" if errmsg: if top and (not iscol): # We don't need to raise multistatus errors if errmsg[:3] == '403': raise Forbidden else: raise PreconditionFailed elif not result.getvalue(): # We haven't had any errors yet, so our result is empty # and we need to set up the XML header result.write('<?xml version="1.0" encoding="utf-8" ?>\n' \ '<d:multistatus xmlns:d="DAV:">\n') result.write('<d:response>\n <d:href>%s</d:href>\n' % url) result.write(' <d:status>HTTP/1.1 %s</d:status>\n' % errmsg) result.write('</d:response>\n') if iscol: for ob in obj.objectValues(): if hasattr(ob, '__dav_resource__') and \ (IWriteLock.providedBy(ob) or WriteLockInterface.isImplementedBy(ob)): uri = urljoin(url, absattr(ob.getId())) self.apply(ob, token, uri, result, top=0) if not top: return result if result.getvalue(): # One or more subitems probably failed, so close the multistatus # element and clear out all succesful unlocks result.write('</d:multistatus>') transaction.abort() return result.getvalue()
def MKCOL(self, REQUEST, RESPONSE): """Create a new collection resource.""" self.dav__init(REQUEST, RESPONSE) if REQUEST.get('BODY', ''): raise UnsupportedMediaType, 'Unknown request body.' name=self.__name__ parent = self.__parent__ if hasattr(aq_base(parent), name): raise MethodNotAllowed, 'The name %s is in use.' % name if not isDavCollection(parent): raise Forbidden, 'Cannot create collection at this location.' ifhdr = REQUEST.get_header('If', '') if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, col=1) else: raise Locked elif ifhdr: # There was an If header, but the parent is not locked raise PreconditionFailed # Add hook for webdav/FTP MKCOL (Collector #2254) (needed for CMF) # parent.manage_addFolder(name) mkcol_handler = getattr(parent,'MKCOL_handler' ,parent.manage_addFolder) mkcol_handler(name) RESPONSE.setStatus(201) RESPONSE.setBody('') return RESPONSE
def MKCOL(self, REQUEST, RESPONSE): """ Create a new Collection (folder) resource. Since this is being done on a LockNull resource, this also involves removing the LockNull object and transferring its locks to the newly created Folder """ self.dav__init(REQUEST, RESPONSE) if REQUEST.get('BODY', ''): raise UnsupportedMediaType, 'Unknown request body.' name = self.__name__ parent = self.aq_parent parenturl = parent.absolute_url() ifhdr = REQUEST.get_header('If', '') if not ifhdr: raise PreconditionFailed, 'No If-header' # If the parent object is locked, that information should be in the # if-header if the user owns a lock on the parent if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): itrue = parent.dav__simpleifhandler(REQUEST, RESPONSE, 'MKCOL', col=1, url=parenturl, refresh=1) if not itrue: raise PreconditionFailed, ( 'Condition failed against resources parent') # Now we need to check the If header against our own lock state itrue = self.dav__simpleifhandler(REQUEST, RESPONSE, 'MKCOL', refresh=1) if not itrue: raise PreconditionFailed, ( 'Condition failed against locknull resource') # All of the If header tests succeeded, now we need to remove ourselves # from our parent. We need to transfer lock state to the new folder. locks = self.wl_lockItems() parent._delObject(name) parent.manage_addFolder(name) folder = parent._getOb(name) for token, lock in locks: folder.wl_setLock(token, lock) RESPONSE.setStatus(201) RESPONSE.setBody('') return RESPONSE
def LOCK(self, REQUEST, RESPONSE): """ LOCK on a Null Resource makes a LockNullResource instance """ self.dav__init(REQUEST, RESPONSE) security = getSecurityManager() creator = security.getUser() body = REQUEST.get('BODY', '') ifhdr = REQUEST.get_header('If', '') depth = REQUEST.get_header('Depth', 'infinity') name = self.__name__ parent = self.__parent__ if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, col=1) else: raise Locked elif ifhdr: # There was an If header, but the parent is not locked. raise PreconditionFailed # The logic involved in locking a null resource is simpler than # a regular resource, since we know we're not already locked, # and the lock isn't being refreshed. if not body: raise BadRequest, 'No body was in the request' locknull = LockNullResource(name) parent._setObject(name, locknull) locknull = parent._getOb(name) cmd = davcmds.Lock(REQUEST) token, result = cmd.apply(locknull, creator, depth=depth) if result: # Return the multistatus result (there were multiple errors) # This *shouldn't* happen for locking a NullResource, but it's # inexpensive to handle and is good coverage for any future # changes in davcmds.Lock RESPONSE.setStatus(207) RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"') RESPONSE.setBody(result) else: # The command was succesful lock = locknull.wl_getLock(token) RESPONSE.setStatus(200) RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"') RESPONSE.setHeader('Lock-Token', 'opaquelocktoken:' + token) RESPONSE.setBody(lock.asXML())
def apply(self, obj, token, user, url=None, result=None, top=1): if result is None: result = StringIO() url = urlfix(url, 'DELETE') url = urlbase(url) iscol = isDavCollection(obj) errmsg = None parent = aq_parent(obj) islockable = IWriteLock.providedBy(obj) or \ WriteLockInterface.isImplementedBy(obj) if parent and (not user.has_permission('Delete objects', parent)): # User doesn't have permission to delete this object errmsg = "403 Forbidden" elif islockable and obj.wl_isLocked(): if token and obj.wl_hasLock(token): # Object is locked, and the token matches (no error) errmsg = "" else: errmsg = "423 Locked" if errmsg: if top and (not iscol): err = errmsg[4:] raise err elif not result.getvalue(): # We haven't had any errors yet, so our result is empty # and we need to set up the XML header result.write('<?xml version="1.0" encoding="utf-8" ?>\n' \ '<d:multistatus xmlns:d="DAV:">\n') result.write('<d:response>\n <d:href>%s</d:href>\n' % url) result.write(' <d:status>HTTP/1.1 %s</d:status>\n' % errmsg) result.write('</d:response>\n') if iscol: for ob in obj.objectValues(): dflag = hasattr(ob,'_p_changed') and (ob._p_changed == None) if hasattr(ob, '__dav_resource__'): uri = urljoin(url, absattr(ob.getId())) self.apply(ob, token, user, uri, result, top=0) if dflag: ob._p_deactivate() if not top: return result if result.getvalue(): # One or more subitems can't be delted, so close the multistatus # element result.write('</d:multistatus>\n') return result.getvalue()
def MKCOL(self, REQUEST, RESPONSE): """ Create a new Collection (folder) resource. Since this is being done on a LockNull resource, this also involves removing the LockNull object and transferring its locks to the newly created Folder """ self.dav__init(REQUEST, RESPONSE) if REQUEST.get('BODY', ''): raise UnsupportedMediaType, 'Unknown request body.' name = self.__name__ parent = self.aq_parent parenturl = parent.absolute_url() ifhdr = REQUEST.get_header('If', '') if not ifhdr: raise PreconditionFailed, 'No If-header' # If the parent object is locked, that information should be in the # if-header if the user owns a lock on the parent if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): itrue = parent.dav__simpleifhandler(REQUEST, RESPONSE, 'MKCOL', col=1, url=parenturl, refresh=1) if not itrue: raise PreconditionFailed, ( 'Condition failed against resources parent') # Now we need to check the If header against our own lock state itrue = self.dav__simpleifhandler(REQUEST,RESPONSE,'MKCOL',refresh=1) if not itrue: raise PreconditionFailed, ( 'Condition failed against locknull resource') # All of the If header tests succeeded, now we need to remove ourselves # from our parent. We need to transfer lock state to the new folder. locks = self.wl_lockItems() parent._delObject(name) parent.manage_addFolder(name) folder = parent._getOb(name) for token, lock in locks: folder.wl_setLock(token, lock) RESPONSE.setStatus(201) RESPONSE.setBody('') return RESPONSE
def wl_clearLocks(self): # Called by lock management machinery to quickly and effectively # destroy all locks. try: locks = self.wl_lockmapping() locks.clear() except: # The locks may be totally messed up, so we'll just delete # and replace. if hasattr(self, "_dav_writelocks"): del self._dav_writelocks if IWriteLock.providedBy(self) or WriteLockInterface.isImplementedBy(self): self._dav_writelocks = PersistentMapping() # Call into a special hook used by LockNullResources to delete # themselves. Could be used by other objects who want to deal # with the state of empty locks. if hasattr(Acquisition.aq_base(self), "__no_valid_write_locks__"): self.__no_valid_write_locks__()
def wl_clearLocks(self): # Called by lock management machinery to quickly and effectively # destroy all locks. try: locks = self.wl_lockmapping() locks.clear() except: # The locks may be totally messed up, so we'll just delete # and replace. if hasattr(self, '_dav_writelocks'): del self._dav_writelocks if IWriteLock.providedBy(self) or \ WriteLockInterface.isImplementedBy(self): self._dav_writelocks = PersistentMapping() # Call into a special hook used by LockNullResources to delete # themselves. Could be used by other objects who want to deal # with the state of empty locks. if hasattr(Acquisition.aq_base(self), '__no_valid_write_locks__'): self.__no_valid_write_locks__()
def MKCOL(self, REQUEST, RESPONSE): """Create a new collection resource.""" self.dav__init(REQUEST, RESPONSE) if REQUEST.get('BODY', ''): raise UnsupportedMediaType, 'Unknown request body.' name = self.__name__ parent = self.__parent__ if hasattr(aq_base(parent), name): raise MethodNotAllowed, 'The name %s is in use.' % name if not isDavCollection(parent): raise Forbidden, 'Cannot create collection at this location.' ifhdr = REQUEST.get_header('If', '') if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, col=1) else: raise Locked elif ifhdr: # There was an If header, but the parent is not locked raise PreconditionFailed # Add hook for webdav/FTP MKCOL (Collector #2254) (needed for CMF) # parent.manage_addFolder(name) mkcol_handler = getattr(parent, 'MKCOL_handler', parent.manage_addFolder) mkcol_handler(name) RESPONSE.setStatus(201) RESPONSE.setBody('') return RESPONSE
def DELETE(self, REQUEST, RESPONSE): """Delete a resource. For non-collection resources, DELETE may return either 200 or 204 (No Content) to indicate success.""" self.dav__init(REQUEST, RESPONSE) ifhdr = REQUEST.get_header('If', '') url = urlfix(REQUEST['URL'], 'DELETE') name = unquote(filter(None, url.split( '/')[-1])) parent = aq_parent(aq_inner(self)) # Lock checking if Lockable.wl_isLocked(self): if ifhdr: self.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE') else: # We're locked, and no if header was passed in, so # the client doesn't own a lock. raise Locked, 'Resource is locked.' elif (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1) else: # Our parent is locked, and no If header was passed in. # When a parent is locked, members cannot be removed raise PreconditionFailed, 'Resource is locked, and no '\ 'condition was passed in.' # Either we're not locked, or a succesful lock token was submitted # so we can delete the lock now. # ajung: Fix for Collector # 2196 if parent.manage_delObjects([name],REQUEST=None) is None: RESPONSE.setStatus(204) else: RESPONSE.setStatus(403) return RESPONSE
def wl_isLocked(ob): """ Returns true if the object is locked, returns 0 if the object is not locked or does not implement the WriteLockInterface """ return (IWriteLock.providedBy(ob) or WriteLockInterface.isImplementedBy(ob)) and ob.wl_isLocked()
def MOVE(self, REQUEST, RESPONSE): """Move a resource to a new location. Though we may later try to make a move appear seamless across namespaces (e.g. from Zope to Apache), MOVE is currently only supported within the Zope namespace.""" self.dav__init(REQUEST, RESPONSE) self.dav__validate(self, 'DELETE', REQUEST) if not hasattr(aq_base(self), 'cb_isMoveable') or \ not self.cb_isMoveable(): raise MethodNotAllowed, 'This object may not be moved.' dest=REQUEST.get_header('Destination', '') try: path = REQUEST.physicalPathFromURL(dest) except ValueError: raise BadRequest, 'No destination given' flag=REQUEST.get_header('Overwrite', 'F') flag=flag.upper() name = path.pop() parent_path = '/'.join(path) try: parent = self.restrictedTraverse(path) except ValueError: raise Conflict, 'Attempt to move to an unknown namespace.' except 'Not Found': raise Conflict, 'The resource %s must exist.' % parent_path except: t, v, tb=sys.exc_info() raise t, v if hasattr(parent, '__null_resource__'): raise Conflict, 'The resource %s must exist.' % parent_path existing=hasattr(aq_base(parent), name) if existing and flag=='F': raise PreconditionFailed, 'Resource %s exists.' % dest try: parent._checkId(name, allow_dup=1) except: raise Forbidden, sys.exc_info()[1] try: parent._verifyObjectPaste(self) except Unauthorized: raise except: raise Forbidden, sys.exc_info()[1] # Now check locks. Since we're affecting the resource that we're # moving as well as the destination, we have to check both. ifhdr = REQUEST.get_header('If', '') if existing: # The destination itself exists, so we need to check its locks destob = aq_base(parent)._getOb(name) if (IWriteLock.providedBy(destob) or WriteLockInterface.isImplementedBy(destob)) and \ destob.wl_isLocked(): if ifhdr: itrue = destob.dav__simpleifhandler( REQUEST, RESPONSE, 'MOVE', url=dest, refresh=1) if not itrue: raise PreconditionFailed else: raise Locked, 'Destination is locked.' elif (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): # There's no existing object in the destination folder, so # we need to check the folders locks since we're changing its # member list if ifhdr: itrue = parent.dav__simpleifhandler(REQUEST, RESPONSE, 'MOVE', col=1, url=dest, refresh=1) if not itrue: raise PreconditionFailed, 'Condition failed.' else: raise Locked, 'Destination is locked.' if Lockable.wl_isLocked(self): # Lastly, we check ourselves if ifhdr: itrue = self.dav__simpleifhandler(REQUEST, RESPONSE, 'MOVE', refresh=1) if not itrue: raise PreconditionFailed, 'Condition failed.' else: raise PreconditionFailed, 'Source is locked and no '\ 'condition was passed in.' orig_container = aq_parent(aq_inner(self)) orig_id = self.getId() self._notifyOfCopyTo(parent, op=1) notify(ObjectWillBeMovedEvent(self, orig_container, orig_id, parent, name)) # try to make ownership explicit so that it gets carried # along to the new location if needed. self.manage_changeOwnershipType(explicit=1) ob = self._getCopy(parent) ob._setId(name) try: orig_container._delObject(orig_id, suppress_events=True) except TypeError: # BBB: removed in Zope 2.11 orig_container._delObject(orig_id) warnings.warn( "%s._delObject without suppress_events is deprecated " "and will be removed in Zope 2.11." % orig_container.__class__.__name__, DeprecationWarning) if existing: object=getattr(parent, name) self.dav__validate(object, 'DELETE', REQUEST) parent._delObject(name) try: parent._setObject(name, ob, set_owner=0, suppress_events=True) except TypeError: # BBB: removed in Zope 2.11 parent._setObject(name, ob, set_owner=0) warnings.warn( "%s._setObject without suppress_events is deprecated " "and will be removed in Zope 2.11." % parent.__class__.__name__, DeprecationWarning) ob = parent._getOb(name) notify(ObjectMovedEvent(ob, orig_container, orig_id, parent, name)) notifyContainerModified(orig_container) if aq_base(orig_container) is not aq_base(parent): notifyContainerModified(parent) ob._postCopy(parent, op=1) # try to make ownership implicit if possible ob.manage_changeOwnershipType(explicit=0) RESPONSE.setStatus(existing and 204 or 201) if not existing: RESPONSE.setHeader('Location', dest) RESPONSE.setBody('') return RESPONSE
def PUT(self, REQUEST, RESPONSE): """Create a new non-collection resource. """ from ZServer import LARGE_FILE_THRESHOLD self.dav__init(REQUEST, RESPONSE) name = self.__name__ parent = self.__parent__ ifhdr = REQUEST.get_header('If', '') if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, col=1) else: # There was no If header at all, and our parent is locked, # so we fail here raise Locked elif ifhdr: # There was an If header, but the parent is not locked raise PreconditionFailed # SDS: Only use BODY if the file size is smaller than # LARGE_FILE_THRESHOLD, otherwise read LARGE_FILE_THRESHOLD # bytes from the file which should be enough to trigger # content_type detection, and possibly enough for CMF's # content_type_registry too. # # Note that body here is really just used for detecting the # content type and figuring out the correct factory. The correct # file content will be uploaded on ob.PUT(REQUEST, RESPONSE) after # the object has been created. # # A problem I could see is content_type_registry predicates # that do depend on the whole file being passed here as an # argument. There's none by default that does this though. If # they really do want to look at the file, they should use # REQUEST['BODYFILE'] directly and try as much as possible not # to read the whole file into memory. if int(REQUEST.get('CONTENT_LENGTH') or 0) > LARGE_FILE_THRESHOLD: file = REQUEST['BODYFILE'] body = file.read(LARGE_FILE_THRESHOLD) file.seek(0) else: body = REQUEST.get('BODY', '') typ=REQUEST.get_header('content-type', None) if typ is None: typ, enc = guess_content_type(name, body) factory = getattr(parent, 'PUT_factory', self._default_PUT_factory ) ob = factory(name, typ, body) if ob is None: ob = self._default_PUT_factory(name, typ, body) # We call _verifyObjectPaste with verify_src=0, to see if the # user can create this type of object (and we don't need to # check the clipboard. try: parent._verifyObjectPaste(ob.__of__(parent), 0) except CopyError: raise Unauthorized, sys.exc_info()[1] # Delegate actual PUT handling to the new object, # SDS: But just *after* it has been stored. self.__parent__._setObject(name, ob) ob = self.__parent__._getOb(name) ob.PUT(REQUEST, RESPONSE) RESPONSE.setStatus(201) RESPONSE.setBody('') return RESPONSE
def PUT(self, REQUEST, RESPONSE): """ Create a new non-collection resource, deleting the LockNull object from the container before putting the new object in. """ self.dav__init(REQUEST, RESPONSE) name = self.__name__ parent = self.aq_parent parenturl = parent.absolute_url() ifhdr = REQUEST.get_header('If', '') # Since a Lock null resource is always locked by definition, all # operations done by an owner of the lock that affect the resource # MUST have the If header in the request if not ifhdr: raise PreconditionFailed, 'No If-header' # First we need to see if the parent of the locknull is locked, and # if the user owns that lock (checked by handling the information in # the If header). if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): itrue = parent.dav__simpleifhandler(REQUEST, RESPONSE, 'PUT', col=1, url=parenturl, refresh=1) if not itrue: raise PreconditionFailed, ( 'Condition failed against resources parent') # Now we need to check the If header against our own lock state itrue = self.dav__simpleifhandler(REQUEST, RESPONSE, 'PUT', refresh=1) if not itrue: raise PreconditionFailed, ( 'Condition failed against locknull resource') # All of the If header tests succeeded, now we need to remove ourselves # from our parent. We need to transfer lock state to the new object. locks = self.wl_lockItems() parent._delObject(name) # Now we need to go through the regular operations of PUT body = REQUEST.get('BODY', '') typ = REQUEST.get_header('content-type', None) if typ is None: typ, enc = guess_content_type(name, body) factory = getattr(parent, 'PUT_factory', self._default_PUT_factory) ob = (factory(name, typ, body) or self._default_PUT_factory(name, typ, body)) # Verify that the user can create this type of object try: parent._verifyObjectPaste(ob.__of__(parent), 0) except Unauthorized: raise except: raise Forbidden, sys.exc_info()[1] # Put the locks on the new object if not (IWriteLock.providedBy(ob) or WriteLockInterface.isImplementedBy(ob)): raise MethodNotAllowed, ( 'The target object type cannot be locked') for token, lock in locks: ob.wl_setLock(token, lock) # Delegate actual PUT handling to the new object. ob.PUT(REQUEST, RESPONSE) parent._setObject(name, ob) RESPONSE.setStatus(201) RESPONSE.setBody('') return RESPONSE
def apply(self, obj, creator=None, depth='infinity', token=None, result=None, url=None, top=1): """ Apply, built for recursion (so that we may lock subitems of a collection if requested """ if result is None: result = StringIO() url = urlfix(self.request['URL'], 'LOCK') url = urlbase(url) iscol = isDavCollection(obj) if iscol and url[-1] != '/': url = url + '/' errmsg = None lock = None try: lock = LockItem(creator, self.owner, depth, self.timeout, self.type, self.scope, token) if token is None: token = lock.getLockToken() except ValueError: errmsg = "412 Precondition Failed" except: errmsg = "403 Forbidden" try: if not (IWriteLock.providedBy(obj) or WriteLockInterface.isImplementedBy(obj)): if top: # This is the top level object in the apply, so we # do want an error errmsg = "405 Method Not Allowed" else: # We're in an infinity request and a subobject does # not support locking, so we'll just pass pass elif obj.wl_isLocked(): errmsg = "423 Locked" else: method = getattr(obj, 'wl_setLock') vld = getSecurityManager().validate(None, obj, 'wl_setLock', method) if vld and token and (lock is not None): obj.wl_setLock(token, lock) else: errmsg = "403 Forbidden" except: errmsg = "403 Forbidden" if errmsg: if top and ((depth in (0, '0')) or (not iscol)): # We don't need to raise multistatus errors raise errmsg[4:] elif not result.getvalue(): # We haven't had any errors yet, so our result is empty # and we need to set up the XML header result.write('<?xml version="1.0" encoding="utf-8" ?>\n' \ '<d:multistatus xmlns:d="DAV:">\n') result.write('<d:response>\n <d:href>%s</d:href>\n' % url) result.write(' <d:status>HTTP/1.1 %s</d:status>\n' % errmsg) result.write('</d:response>\n') if depth == 'infinity' and iscol: for ob in obj.objectValues(): if hasattr(obj, '__dav_resource__'): uri = urljoin(url, absattr(ob.getId())) self.apply(ob, creator, depth, token, result, uri, top=0) if not top: return token, result if result.getvalue(): # One or more subitems probably failed, so close the multistatus # element and clear out all succesful locks result.write('</d:multistatus>') transaction.abort() # This *SHOULD* clear all succesful locks return token, result.getvalue()
def PUT(self, REQUEST, RESPONSE): """ Create a new non-collection resource, deleting the LockNull object from the container before putting the new object in. """ self.dav__init(REQUEST, RESPONSE) name = self.__name__ parent = self.aq_parent parenturl = parent.absolute_url() ifhdr = REQUEST.get_header('If', '') # Since a Lock null resource is always locked by definition, all # operations done by an owner of the lock that affect the resource # MUST have the If header in the request if not ifhdr: raise PreconditionFailed, 'No If-header' # First we need to see if the parent of the locknull is locked, and # if the user owns that lock (checked by handling the information in # the If header). if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): itrue = parent.dav__simpleifhandler(REQUEST, RESPONSE, 'PUT', col=1, url=parenturl, refresh=1) if not itrue: raise PreconditionFailed, ( 'Condition failed against resources parent') # Now we need to check the If header against our own lock state itrue = self.dav__simpleifhandler(REQUEST, RESPONSE, 'PUT', refresh=1) if not itrue: raise PreconditionFailed, ( 'Condition failed against locknull resource') # All of the If header tests succeeded, now we need to remove ourselves # from our parent. We need to transfer lock state to the new object. locks = self.wl_lockItems() parent._delObject(name) # Now we need to go through the regular operations of PUT body = REQUEST.get('BODY', '') typ = REQUEST.get_header('content-type', None) if typ is None: typ, enc = guess_content_type(name, body) factory = getattr(parent, 'PUT_factory', self._default_PUT_factory) ob = (factory(name, typ, body) or self._default_PUT_factory(name, typ, body)) # Verify that the user can create this type of object try: parent._verifyObjectPaste(ob.__of__(parent), 0) except Unauthorized: raise except: raise Forbidden, sys.exc_info()[1] # Put the locks on the new object if not (IWriteLock.providedBy(ob) or WriteLockInterface.isImplementedBy(ob)): raise MethodNotAllowed, ('The target object type cannot be locked') for token, lock in locks: ob.wl_setLock(token, lock) # Delegate actual PUT handling to the new object. ob.PUT(REQUEST, RESPONSE) parent._setObject(name, ob) RESPONSE.setStatus(201) RESPONSE.setBody('') return RESPONSE
def COPY(self, REQUEST, RESPONSE): """Create a duplicate of the source resource whose state and behavior match that of the source resource as closely as possible. Though we may later try to make a copy appear seamless across namespaces (e.g. from Zope to Apache), COPY is currently only supported within the Zope namespace.""" self.dav__init(REQUEST, RESPONSE) if not hasattr(aq_base(self), 'cb_isCopyable') or \ not self.cb_isCopyable(): raise MethodNotAllowed, 'This object may not be copied.' depth=REQUEST.get_header('Depth', 'infinity') if not depth in ('0', 'infinity'): raise BadRequest, 'Invalid Depth header.' dest=REQUEST.get_header('Destination', '') while dest and dest[-1]=='/': dest=dest[:-1] if not dest: raise BadRequest, 'Invalid Destination header.' try: path = REQUEST.physicalPathFromURL(dest) except ValueError: raise BadRequest, 'Invalid Destination header' name = path.pop() oflag=REQUEST.get_header('Overwrite', 'F').upper() if not oflag in ('T', 'F'): raise BadRequest, 'Invalid Overwrite header.' try: parent=self.restrictedTraverse(path) except ValueError: raise Conflict, 'Attempt to copy to an unknown namespace.' except NotFound: raise Conflict, 'Object ancestors must already exist.' except: t, v, tb=sys.exc_info() raise t, v if hasattr(parent, '__null_resource__'): raise Conflict, 'Object ancestors must already exist.' existing=hasattr(aq_base(parent), name) if existing and oflag=='F': raise PreconditionFailed, 'Destination resource exists.' try: parent._checkId(name, allow_dup=1) except: raise Forbidden, sys.exc_info()[1] try: parent._verifyObjectPaste(self) except Unauthorized: raise except: raise Forbidden, sys.exc_info()[1] # Now check locks. The If header on a copy only cares about the # lock on the destination, so we need to check out the destinations # lock status. ifhdr = REQUEST.get_header('If', '') if existing: # The destination itself exists, so we need to check its locks destob = aq_base(parent)._getOb(name) if (IWriteLock.providedBy(destob) or WriteLockInterface.isImplementedBy(destob)) and \ destob.wl_isLocked(): if ifhdr: itrue = destob.dav__simpleifhandler( REQUEST, RESPONSE, 'COPY', refresh=1) if not itrue: raise PreconditonFailed else: raise Locked, 'Destination is locked.' elif (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, 'COPY', refresh=1) else: raise Locked, 'Destination is locked.' self._notifyOfCopyTo(parent, op=0) ob = self._getCopy(parent) ob._setId(name) if depth=='0' and isDavCollection(ob): for id in ob.objectIds(): ob._delObject(id) notify(ObjectCopiedEvent(ob, self)) if existing: object=getattr(parent, name) self.dav__validate(object, 'DELETE', REQUEST) parent._delObject(name) parent._setObject(name, ob) ob = parent._getOb(name) ob._postCopy(parent, op=0) OFS.subscribers.compatibilityCall('manage_afterClone', ob, ob) notify(ObjectClonedEvent(ob)) # We remove any locks from the copied object because webdav clients # don't track the lock status and the lock token for copied resources ob.wl_clearLocks() RESPONSE.setStatus(existing and 204 or 201) if not existing: RESPONSE.setHeader('Location', dest) RESPONSE.setBody('') return RESPONSE
def PUT(self, REQUEST, RESPONSE): """Create a new non-collection resource. """ from ZServer import LARGE_FILE_THRESHOLD self.dav__init(REQUEST, RESPONSE) name = self.__name__ parent = self.__parent__ ifhdr = REQUEST.get_header('If', '') if (IWriteLock.providedBy(parent) or WriteLockInterface.isImplementedBy(parent)) and \ parent.wl_isLocked(): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, col=1) else: # There was no If header at all, and our parent is locked, # so we fail here raise Locked elif ifhdr: # There was an If header, but the parent is not locked raise PreconditionFailed # SDS: Only use BODY if the file size is smaller than # LARGE_FILE_THRESHOLD, otherwise read LARGE_FILE_THRESHOLD # bytes from the file which should be enough to trigger # content_type detection, and possibly enough for CMF's # content_type_registry too. # # Note that body here is really just used for detecting the # content type and figuring out the correct factory. The correct # file content will be uploaded on ob.PUT(REQUEST, RESPONSE) after # the object has been created. # # A problem I could see is content_type_registry predicates # that do depend on the whole file being passed here as an # argument. There's none by default that does this though. If # they really do want to look at the file, they should use # REQUEST['BODYFILE'] directly and try as much as possible not # to read the whole file into memory. if int(REQUEST.get('CONTENT_LENGTH') or 0) > LARGE_FILE_THRESHOLD: file = REQUEST['BODYFILE'] body = file.read(LARGE_FILE_THRESHOLD) file.seek(0) else: body = REQUEST.get('BODY', '') typ = REQUEST.get_header('content-type', None) if typ is None: typ, enc = guess_content_type(name, body) factory = getattr(parent, 'PUT_factory', self._default_PUT_factory) ob = factory(name, typ, body) if ob is None: ob = self._default_PUT_factory(name, typ, body) # We call _verifyObjectPaste with verify_src=0, to see if the # user can create this type of object (and we don't need to # check the clipboard. try: parent._verifyObjectPaste(ob.__of__(parent), 0) except CopyError: raise Unauthorized, sys.exc_info()[1] # Delegate actual PUT handling to the new object, # SDS: But just *after* it has been stored. self.__parent__._setObject(name, ob) ob = self.__parent__._getOb(name) ob.PUT(REQUEST, RESPONSE) RESPONSE.setStatus(201) RESPONSE.setBody('') return RESPONSE