def dav__simpleifhandler(self, request, response, method='PUT', col=0, url=None, refresh=0): ifhdr = request.get_header('If', None) lockable = wl_isLockable(self) if not lockable: # degenerate case, we shouldnt have even called this method. return None locked = self.wl_isLocked() if locked and (not ifhdr): raise Locked('Resource is locked.') if not ifhdr: return None # Since we're a simple if handler, and since some clients don't # pass in the port information in the resource part of an If # header, we're only going to worry about if the paths compare if url is None: url = urlfix(request['URL'], method) url = urlbase(url) # Gets just the path information # if 'col' is passed in, an operation is happening on a submember # of a collection, while the Lock may be on the parent. Lob off # the final part of the URL (ie '/a/b/foo.html' becomes '/a/b/') if col: url = url[:url.rfind('/')+1] found = 0; resourcetagged = 0 taglist = IfParser(ifhdr) for tag in taglist: if not tag.resource: # There's no resource (url) with this tag tag_list = map(tokenFinder, tag.list) wehave = [t for t in tag_list if self.wl_hasLock(t)] if not wehave: continue if tag.NOTTED: continue if refresh: for token in wehave: self.wl_getLock(token).refresh() resourcetagged = 1 found = 1; break elif urlbase(tag.resource) == url: resourcetagged = 1 tag_list = map(tokenFinder, tag.list) wehave = [t for t in tag_list if self.wl_hasLock(t)] if not wehave: continue if tag.NOTTED: continue if refresh: for token in wehave: self.wl_getLock(token).refresh() found = 1; break if resourcetagged and (not found): raise PreconditionFailed, 'Condition failed.' elif resourcetagged and found: return 1 else: return 0
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 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) 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 HEAD(self, REQUEST, RESPONSE): """Retrieve resource information without a response body.""" self.dav__init(REQUEST, RESPONSE) content_type=None if hasattr(self, 'content_type'): content_type=absattr(self.content_type) if content_type is None: url=urlfix(REQUEST['URL'], 'HEAD') name=unquote(filter(None, url.split( '/')[-1])) content_type, encoding=mimetypes.guess_type(name) if content_type is None: if hasattr(self, 'default_content_type'): content_type=absattr(self.default_content_type) if content_type is None: content_type = 'application/octet-stream' RESPONSE.setHeader('Content-Type', content_type.lower()) if hasattr(aq_base(self), 'get_size'): RESPONSE.setHeader('Content-Length', absattr(self.get_size)) if hasattr(self, '_p_mtime'): mtime=rfc1123_date(self._p_mtime) RESPONSE.setHeader('Last-Modified', mtime) if hasattr(aq_base(self), 'http__etag'): etag = self.http__etag(readonly=1) if etag: RESPONSE.setHeader('Etag', etag) RESPONSE.setStatus(200) return RESPONSE
def HEAD(self, REQUEST, RESPONSE): """Retrieve resource information without a response body.""" self.dav__init(REQUEST, RESPONSE) content_type = None if hasattr(self, 'content_type'): content_type = absattr(self.content_type) if content_type is None: url = urlfix(REQUEST['URL'], 'HEAD') name = unquote([_f for _f in url.split('/') if _f][-1]) content_type, encoding = mimetypes.guess_type(name) if content_type is None: if hasattr(self, 'default_content_type'): content_type = absattr(self.default_content_type) if content_type is None: content_type = 'application/octet-stream' RESPONSE.setHeader('Content-Type', content_type.lower()) if hasattr(aq_base(self), 'get_size'): RESPONSE.setHeader('Content-Length', absattr(self.get_size)) if hasattr(self, '_p_mtime'): mtime = rfc1123_date(self._p_mtime) RESPONSE.setHeader('Last-Modified', mtime) if hasattr(aq_base(self), 'http__etag'): etag = self.http__etag(readonly=1) if etag: RESPONSE.setHeader('Etag', etag) RESPONSE.setStatus(200) 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([_f for _f in url.split('/') if _f][-1]) parent = aq_parent(aq_inner(self)) # Lock checking if 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) 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 Locked('Parent of this resource is locked.') # 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 DELETE(self, REQUEST, RESPONSE): """Delete a collection resource. For collection resources, DELETE may return either 200 (OK) or 204 (No Content) to indicate total success, or may return 207 (Multistatus) to indicate partial success. Note that in Zope a DELETE currently never returns 207.""" from webdav.davcmds import DeleteCollection self.dav__init(REQUEST, RESPONSE) ifhdr = REQUEST.get_header('If', '') url = urlfix(REQUEST['URL'], 'DELETE') name = unquote(filter(None, url.split( '/'))[-1]) parent = self.aq_parent sm = getSecurityManager() token = None # if re.match("/Control_Panel",REQUEST['PATH_INFO']): # RESPONSE.setStatus(403) # RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"') # return RESPONSE # Level 1 of lock checking (is the collection or its parent locked?) if wl_isLocked(self): if ifhdr: self.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1) else: raise Locked elif wl_isLocked(parent): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1) else: raise PreconditionFailed # Second level of lock\conflict checking (are any descendants locked, # or is the user not permitted to delete?). This results in a # multistatus response if ifhdr: tokens = self.wl_lockTokens() for tok in tokens: # We already know that the simple if handler succeeded, # we just want to get the right token out of the header now if ifhdr.find(tok) > -1: token = tok cmd = DeleteCollection() result = cmd.apply(self, token, sm, REQUEST['URL']) if result: # There were conflicts, so we need to report them RESPONSE.setStatus(207) RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"') RESPONSE.setBody(result) else: # There were no conflicts, so we can go ahead and delete # ajung: additional check if we really could delete the collection # (Collector #2196) if parent.manage_delObjects([name],REQUEST=None) is None: RESPONSE.setStatus(204) else: RESPONSE.setStatus(403) return RESPONSE
def DELETE(self, REQUEST, RESPONSE): """Delete a collection resource. For collection resources, DELETE may return either 200 (OK) or 204 (No Content) to indicate total success, or may return 207 (Multistatus) to indicate partial success. Note that in Zope a DELETE currently never returns 207.""" from webdav.davcmds import DeleteCollection self.dav__init(REQUEST, RESPONSE) ifhdr = REQUEST.get_header('If', '') url = urlfix(REQUEST['URL'], 'DELETE') name = unquote(filter(None, url.split('/'))[-1]) parent = self.aq_parent sm = getSecurityManager() token = None # if re.match("/Control_Panel",REQUEST['PATH_INFO']): # RESPONSE.setStatus(403) # RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"') # return RESPONSE # Level 1 of lock checking (is the collection or its parent locked?) if wl_isLocked(self): if ifhdr: self.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1) else: raise Locked elif wl_isLocked(parent): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1) else: raise PreconditionFailed # Second level of lock\conflict checking (are any descendants locked, # or is the user not permitted to delete?). This results in a # multistatus response if ifhdr: tokens = self.wl_lockTokens() for tok in tokens: # We already know that the simple if handler succeeded, # we just want to get the right token out of the header now if ifhdr.find(tok) > -1: token = tok cmd = DeleteCollection() result = cmd.apply(self, token, sm, REQUEST['URL']) if result: # There were conflicts, so we need to report them RESPONSE.setStatus(207) RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"') RESPONSE.setBody(result) else: # There were no conflicts, so we can go ahead and delete # ajung: additional check if we really could delete the collection # (Collector #2196) if parent.manage_delObjects([name], REQUEST=None) is None: RESPONSE.setStatus(204) else: RESPONSE.setStatus(403) return RESPONSE
def apply(self, obj, token, sm, 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) if parent and (not sm.checkPermission(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): if errmsg == "403 Forbidden": raise Forbidden() if errmsg == "423 Locked": raise Locked() 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, sm, 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 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): 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 apply(self, obj, url=None, depth=0, result=None, top=1): if result is None: result=StringIO() depth=self.depth url=urlfix(self.request['URL'], 'PROPFIND') url=urlbase(url) result.write('<?xml version="1.0" encoding="utf-8"?>\n' \ '<d:multistatus xmlns:d="DAV:">\n') iscol=isDavCollection(obj) if iscol and url[-1] != '/': url=url+'/' result.write('<d:response>\n<d:href>%s</d:href>\n' % safe_quote(url)) if hasattr(aq_base(obj), 'propertysheets'): propsets=obj.propertysheets.values() obsheets=obj.propertysheets else: davprops=DAVProps(obj) propsets=(davprops,) obsheets={'DAV:': davprops} if self.allprop: stats=[] for ps in propsets: if hasattr(aq_base(ps), 'dav__allprop'): stats.append(ps.dav__allprop()) stats=''.join(stats) or '<d:status>200 OK</d:status>\n' result.write(stats) elif self.propname: stats=[] for ps in propsets: if hasattr(aq_base(ps), 'dav__propnames'): stats.append(ps.dav__propnames()) stats=''.join(stats) or '<d:status>200 OK</d:status>\n' result.write(stats) elif self.propnames: rdict={} for name, ns in self.propnames: ps=obsheets.get(ns, None) if ps is not None and hasattr(aq_base(ps), 'dav__propstat'): stat=ps.dav__propstat(name, rdict) else: prop='<n:%s xmlns:n="%s"/>' % (name, ns) code='404 Not Found' if not rdict.has_key(code): rdict[code]=[prop] else: rdict[code].append(prop) keys=rdict.keys() keys.sort() for key in keys: result.write('<d:propstat>\n' \ ' <d:prop>\n' \ ) map(result.write, rdict[key]) result.write(' </d:prop>\n' \ ' <d:status>HTTP/1.1 %s</d:status>\n' \ '</d:propstat>\n' % key ) else: raise BadRequest, 'Invalid request' result.write('</d:response>\n') if depth in ('1', 'infinity') and iscol: for ob in obj.listDAVObjects(): if hasattr(ob,"meta_type"): if ob.meta_type=="Broken Because Product is Gone": continue dflag=hasattr(ob, '_p_changed') and (ob._p_changed == None) if hasattr(ob, '__locknull_resource__'): # Do nothing, a null resource shouldn't show up to DAV if dflag: ob._p_deactivate() elif hasattr(ob, '__dav_resource__'): uri = urljoin(url, absattr(ob.getId())) depth = depth=='infinity' and depth or 0 self.apply(ob, uri, depth, result, top=0) if dflag: ob._p_deactivate() if not top: return result result.write('</d:multistatus>') return result.getvalue()
def dav__simpleifhandler(self, request, response, method='PUT', col=0, url=None, refresh=0): ifhdr = request.get_header('If', None) lockable = wl_isLockable(self) if not lockable: # degenerate case, we shouldnt have even called this method. return None locked = self.wl_isLocked() if locked and (not ifhdr): raise Locked('Resource is locked.') if not ifhdr: return None # Since we're a simple if handler, and since some clients don't # pass in the port information in the resource part of an If # header, we're only going to worry about if the paths compare if url is None: url = urlfix(request['URL'], method) url = urlbase(url) # Gets just the path information # if 'col' is passed in, an operation is happening on a submember # of a collection, while the Lock may be on the parent. Lob off # the final part of the URL (ie '/a/b/foo.html' becomes '/a/b/') if col: url = url[:url.rfind('/') + 1] found = 0 resourcetagged = 0 taglist = IfParser(ifhdr) for tag in taglist: if not tag.resource: # There's no resource (url) with this tag tag_list = [tokenFinder(x) for x in tag.list] wehave = [t for t in tag_list if self.wl_hasLock(t)] if not wehave: continue if tag.NOTTED: continue if refresh: for token in wehave: self.wl_getLock(token).refresh() resourcetagged = 1 found = 1 break elif unquote(urlbase(tag.resource)) == unquote(url): resourcetagged = 1 tag_list = [tokenFinder(x) for x in tag.list] wehave = [t for t in tag_list if self.wl_hasLock(t)] if not wehave: continue if tag.NOTTED: continue if refresh: for token in wehave: self.wl_getLock(token).refresh() found = 1 break if resourcetagged and (not found): raise PreconditionFailed('Condition failed.') elif resourcetagged and found: return 1 else: return 0
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) if islockable: if 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" else: errmsg = '400 Bad Request' else: # 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): 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 apply(self, obj): url=urlfix(self.request['URL'], 'PROPPATCH') if isDavCollection(obj): url=url+'/' result=StringIO() errors=[] result.write('<?xml version="1.0" encoding="utf-8"?>\n' \ '<d:multistatus xmlns:d="DAV:">\n' \ '<d:response>\n' \ '<d:href>%s</d:href>\n' % quote(url)) propsets=obj.propertysheets for value in self.values: status='200 OK' if len(value) > 2: name, ns, val, md=value propset=propsets.get(ns, None) if propset is None: propsets.manage_addPropertySheet('', ns) propset=propsets.get(ns) if propset.hasProperty(name): try: propset._updateProperty(name, val, meta=md) except: errors.append(str(sys.exc_info()[1])) status='409 Conflict' else: try: propset._setProperty(name, val, meta=md) except: errors.append(str(sys.exc_info()[1])) status='409 Conflict' else: name, ns=value propset=propsets.get(ns, None) if propset is None or not propset.hasProperty(name): # removing a non-existing property is not an error! # according to RFC 2518 status='200 OK' else: try: propset._delProperty(name) except: errors.append('%s cannot be deleted.' % name) status='409 Conflict' result.write('<d:propstat xmlns:n="%s">\n' \ ' <d:prop>\n' \ ' <n:%s/>\n' \ ' </d:prop>\n' \ ' <d:status>HTTP/1.1 %s</d:status>\n' \ '</d:propstat>\n' % (ns, name, status)) errmsg='\n'.join(errors) or 'The operation succeded.' result.write('<d:responsedescription>\n' \ '%s\n' \ '</d:responsedescription>\n' \ '</d:response>\n' \ '</d:multistatus>' % errmsg) result=result.getvalue() if not errors: return result # This is lame, but I cant find a way to keep ZPublisher # from sticking a traceback into my xml response :( transaction.abort() result=result.replace( '200 OK', '424 Failed Dependency') return result