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 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.""" 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 user = getSecurityManager().getUser() 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 Lockable.wl_isLocked(self): if ifhdr: self.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1) else: raise Locked elif Lockable.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 = davcmds.DeleteCollection() result = cmd.apply(self, token, user, 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.""" 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 user = getSecurityManager().getUser() 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 Lockable.wl_isLocked(self): if ifhdr: self.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1) else: raise 'Locked' elif Lockable.wl_isLocked(parent): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1) else: raise 'Precondition Failed' # 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 = davcmds.DeleteCollection() result = cmd.apply(self, token, user, 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, 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 dav__simpleifhandler(self, request, response, method='PUT', col=0, url=None, refresh=0): ifhdr = request.get_header('If', None) if Lockable.wl_isLocked(self) and (not ifhdr): raise Locked, "Resource is locked." if not ifhdr: return None if not Lockable.wl_isLocked(self): 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] havetag = lambda x, self=self: self.wl_hasLock(x) 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 = filter(havetag, tag_list) 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 = filter(havetag, tag_list) 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, 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 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, valerrors: errmsg = "412 Precondition Failed"
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 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 'Bad Request', 'Invalid request' result.write('</d:response>\n') if depth in ('1', 'infinity') and iscol: for ob in obj.objectValues(): 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=os.path.join(url, absattr(ob.id)) 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 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) propdict=propset._propdict() 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' if result != '200 OK': abort=1 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 :( get_transaction().abort() result=result.replace( '200 OK', '424 Failed Dependency') return result
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()