def sendStatusResponse(environ, start_response, e): """Start a WSGI response for a DAVError or status code.""" status = getHttpStatusString(e) headers = [] # if 'keep-alive' in environ.get('HTTP_CONNECTION', '').lower(): # headers += [ # ('Connection', 'keep-alive'), # ] if e in (HTTP_NOT_MODIFIED, HTTP_NO_CONTENT): # See paste.lint: these code don't have content start_response(status, [ ("Content-Length", "0"), ("Date", getRfc1123Time()), ] + headers) return [b""] if e in (HTTP_OK, HTTP_CREATED): e = DAVError(e) assert isinstance(e, DAVError) content_type, body = e.getResponsePage() assert compat.is_bytes(body), body # If not, Content-Length is wrong! start_response(status, [ ("Content-Type", content_type), ("Date", getRfc1123Time()), ("Content-Length", str(len(body))), ] + headers) return [body]
def send_multi_status_response(environ, start_response, multistatusEL): # If logging of the body is desired, then this is the place to do it # pretty: if environ.get("wsgidav.dump_response_body"): xml = "{} XML response body:\n{}".format( environ["REQUEST_METHOD"], compat.to_native(xml_to_bytes(multistatusEL, pretty_print=True)), ) environ["wsgidav.dump_response_body"] = xml # Hotfix for Windows XP # PROPFIND XML response is not recognized, when pretty_print = True! # (Vista and others would accept this). xml_data = xml_to_bytes(multistatusEL, pretty_print=False) # If not, Content-Length is wrong! assert compat.is_bytes(xml_data), xml_data headers = [ ("Content-Type", "application/xml"), ("Date", get_rfc1123_time()), ("Content-Length", str(len(xml_data))), ] # if 'keep-alive' in environ.get('HTTP_CONNECTION', '').lower(): # headers += [ # ('Connection', 'keep-alive'), # ] start_response("207 Multi-Status", headers) return [xml_data]
def get_etag(file_path): """Return a strong Entity Tag for a (file)path. http://www.webdav.org/specs/rfc4918.html#etag Returns the following as entity tags:: Non-file - md5(pathname) Win32 - md5(pathname)-lastmodifiedtime-filesize Others - inode-lastmodifiedtime-filesize """ # (At least on Vista) os.path.exists returns False, if a file name contains # special characters, even if it is correctly UTF-8 encoded. # So we convert to unicode. On the other hand, md5() needs a byte string. if compat.is_bytes(file_path): unicodeFilePath = to_unicode_safe(file_path) else: unicodeFilePath = file_path file_path = file_path.encode("utf8") if not os.path.isfile(unicodeFilePath): return md5(file_path).hexdigest() if sys.platform == "win32": statresults = os.stat(unicodeFilePath) return (md5(file_path).hexdigest() + "-" + str(statresults[stat.ST_MTIME]) + "-" + str(statresults[stat.ST_SIZE])) else: statresults = os.stat(unicodeFilePath) return (str(statresults[stat.ST_INO]) + "-" + str(statresults[stat.ST_MTIME]) + "-" + str(statresults[stat.ST_SIZE]))
def safeReEncode(s, encoding_to, errors="backslashreplace"): """Re-encode str or binary so that is compatible wit a given encoding (replacing unsupported chars).""" if compat.is_bytes(s): s = s.decode(encoding_to, errors=errors).encode(encoding_to) else: s = s.encode(encoding_to, errors=errors).decode(encoding_to) return s
def string_repr(s): """Return a string as hex dump.""" if compat.is_bytes(s): res = "{!r}: ".format(s) for b in s: if type(b) is str: # Py2 b = ord(b) res += "%02x " % b return res return "{}".format(s)
def stringRepr(s): """Return a string as hex dump.""" if compat.is_bytes(s): res = "%r: " % s for b in s: if type(b) is str: # Py2 b = ord(b) res += "%02x " % b return res return "%s" % s
def validate_lock(lock): assert compat.is_native(lock["root"]) assert lock["root"].startswith("/") assert lock["type"] == "write" assert lock["scope"] in ("shared", "exclusive") assert lock["depth"] in ("0", "infinity") assert compat.is_bytes(lock["owner"]), lock # XML bytestring # raises TypeError: timeout = float(lock["timeout"]) assert timeout > 0 or timeout == -1, "timeout must be positive or -1" assert compat.is_native(lock["principal"]) if "token" in lock: assert compat.is_native(lock["token"])
def safe_re_encode(s, encoding_to, errors="backslashreplace"): """Re-encode str or binary so that is compatible with a given encoding (replacing unsupported chars). We use ASCII as default, which gives us some output that contains \x99 and \u9999 for every character > 127, for easier debugging. (e.g. if we don't know the encoding, see #87, #96) """ # prev = s if not encoding_to: encoding_to = "ASCII" if compat.is_bytes(s): s = s.decode(encoding_to, errors=errors).encode(encoding_to) else: s = s.encode(encoding_to, errors=errors).decode(encoding_to) # print("safe_re_encode({}, {}) => {}".format(prev, encoding_to, s)) return s
def get_etag(file_path): """Return a strong Entity Tag for a (file)path. http://www.webdav.org/specs/rfc4918.html#etag Returns the following as entity tags:: Non-file - md5(pathname) Win32 - md5(pathname)-lastmodifiedtime-filesize Others - inode-lastmodifiedtime-filesize """ # (At least on Vista) os.path.exists returns False, if a file name contains # special characters, even if it is correctly UTF-8 encoded. # So we convert to unicode. On the other hand, md5() needs a byte string. if compat.is_bytes(file_path): unicodeFilePath = to_unicode_safe(file_path) else: unicodeFilePath = file_path file_path = file_path.encode("utf8") if not os.path.isfile(unicodeFilePath): return md5(file_path).hexdigest() if sys.platform == "win32": statresults = os.stat(unicodeFilePath) return ( md5(file_path).hexdigest() + "-" + str(statresults[stat.ST_MTIME]) + "-" + str(statresults[stat.ST_SIZE]) ) else: statresults = os.stat(unicodeFilePath) return ( str(statresults[stat.ST_INO]) + "-" + str(statresults[stat.ST_MTIME]) + "-" + str(statresults[stat.ST_SIZE]) )
def send_status_response(environ, start_response, e, add_headers=None, is_head=False): """Start a WSGI response for a DAVError or status code.""" status = get_http_status_string(e) headers = [] if add_headers: headers.extend(add_headers) # if 'keep-alive' in environ.get('HTTP_CONNECTION', '').lower(): # headers += [ # ('Connection', 'keep-alive'), # ] if e in (HTTP_NOT_MODIFIED, HTTP_NO_CONTENT): # See paste.lint: these code don't have content start_response(status, [("Content-Length", "0"), ("Date", get_rfc1123_time())] + headers) return [b""] if e in (HTTP_OK, HTTP_CREATED): e = DAVError(e) assert isinstance(e, DAVError) content_type, body = e.get_response_page() if is_head: body = compat.b_empty assert compat.is_bytes(body), body # If not, Content-Length is wrong! start_response( status, [ ("Content-Type", content_type), ("Date", get_rfc1123_time()), ("Content-Length", str(len(body))), ] + headers, ) return [body]
def send_status_response(environ, start_response, e, add_headers=None, is_head=False): """Start a WSGI response for a DAVError or status code.""" status = get_http_status_string(e) headers = [] if add_headers: headers.extend(add_headers) # if 'keep-alive' in environ.get('HTTP_CONNECTION', '').lower(): # headers += [ # ('Connection', 'keep-alive'), # ] if e in (HTTP_NOT_MODIFIED, HTTP_NO_CONTENT): # See paste.lint: these code don't have content start_response( status, [("Content-Length", "0"), ("Date", get_rfc1123_time())] + headers ) return [b""] if e in (HTTP_OK, HTTP_CREATED): e = DAVError(e) assert isinstance(e, DAVError) content_type, body = e.get_response_page() if is_head: body = compat.b_empty assert compat.is_bytes(body), body # If not, Content-Length is wrong! start_response( status, [ ("Content-Type", content_type), ("Date", get_rfc1123_time()), ("Content-Length", str(len(body))), ] + headers, ) return [body]
def __call__(self, environ, start_response): """""" verbose = self._config.get("verbose", 2) self.last_request_time = "{0}_{1}".format( datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S"), int(round(time.time() * 1000))) method = environ["REQUEST_METHOD"] debugBreak = False dumpRequest = False dumpResponse = False if verbose >= 3 or self._config.get("dump_requests"): dumpRequest = dumpResponse = True # Process URL commands if "dump_storage" in environ.get("QUERY_STRING"): dav = environ.get("wsgidav.provider") if dav.lockManager: dav.lockManager._dump() if dav.propManager: dav.propManager._dump() # Turn on max. debugging for selected litmus tests litmusTag = environ.get("HTTP_X_LITMUS", environ.get("HTTP_X_LITMUS_SECOND")) if litmusTag and verbose >= 2: print("----\nRunning litmus test '%s'..." % litmusTag, file=self.out) for litmusSubstring in self.debug_litmus: if litmusSubstring in litmusTag: verbose = 3 debugBreak = True dumpRequest = True dumpResponse = True break for litmusSubstring in self.break_after_litmus: if litmusSubstring in self.passedLitmus and litmusSubstring not in litmusTag: print(" *** break after litmus %s" % litmusTag, file=self.out) sys.exit(-1) if litmusSubstring in litmusTag: self.passedLitmus[litmusSubstring] = True # Turn on max. debugging for selected request methods if verbose >= 2 and method in self.debug_methods: verbose = 3 debugBreak = True dumpRequest = True dumpResponse = True # Set debug options to environment environ["wsgidav.verbose"] = verbose # environ["wsgidav.debug_methods"] = self.debug_methods environ["wsgidav.debug_break"] = debugBreak environ["wsgidav.dump_request_body"] = dumpRequest environ["wsgidav.dump_response_body"] = dumpResponse # Dump request headers if dumpRequest: print("<%s> --- %s Request ---" % (threading.currentThread().ident, method), file=self.out) for k, v in environ.items(): if k == k.upper(): print("%20s: '%s'" % (k, v), file=self.out) print("\n", file=self.out) self._dump_request(environ, xml=None) # Intercept start_response # sub_app_start_response = util.SubAppStartResponse() nbytes = 0 first_yield = True app_iter = self._application(environ, sub_app_start_response) for v in app_iter: # Start response (the first time) if first_yield: # Success! start_response( sub_app_start_response.status, sub_app_start_response.response_headers, sub_app_start_response.exc_info, ) # Dump response headers if first_yield and dumpResponse: print( "<%s> --- %s Response(%s): ---" % (threading.currentThread().ident, method, sub_app_start_response.status), file=self.out, ) headersdict = dict(sub_app_start_response.response_headers) for envitem in headersdict.keys(): print("%s: %s" % (envitem, repr(headersdict[envitem])), file=self.out) print("", file=self.out) # Check, if response is a binary string, otherwise we probably have # calculated a wrong content-length assert compat.is_bytes(v), v # Dump response body drb = environ.get("wsgidav.dump_response_body") if compat.is_basestring(drb): # Middleware provided a formatted body representation print(drb, file=self.out) elif drb is True: # Else dump what we get, (except for long GET responses) if method == "GET": if first_yield: print(v[:50], "...", file=self.out) elif len(v) > 0: print(v, file=self.out) if dumpResponse: self._dump_response(sub_app_start_response, drb) drb = environ["wsgidav.dump_response_body"] = None nbytes += len(v) first_yield = False yield v if hasattr(app_iter, "close"): app_iter.close() # Start response (if it hasn't been done yet) if first_yield: start_response( # Success! sub_app_start_response.status, sub_app_start_response.response_headers, sub_app_start_response.exc_info, ) if dumpResponse: print( "\n<%s> --- End of %s Response (%i bytes) ---" % (threading.currentThread().ident, method, nbytes), file=self.out, ) return
def __call__(self, environ, start_response): """""" # srvcfg = environ["wsgidav.config"] verbose = self._config.get("verbose", 3) method = environ["REQUEST_METHOD"] debugBreak = False dumpRequest = False dumpResponse = False if verbose >= 5: dumpRequest = dumpResponse = True # Process URL commands if "dump_storage" in environ.get("QUERY_STRING", ""): dav = environ.get("wsgidav.provider") if dav.lockManager: dav.lockManager._dump() if dav.propManager: dav.propManager._dump() # Turn on max. debugging for selected litmus tests litmusTag = environ.get("HTTP_X_LITMUS", environ.get("HTTP_X_LITMUS_SECOND")) if litmusTag and verbose >= 3: _logger.info("----\nRunning litmus test '{}'...".format(litmusTag)) for litmusSubstring in self.debug_litmus: if litmusSubstring in litmusTag: verbose = 5 debugBreak = True dumpRequest = True dumpResponse = True break for litmusSubstring in self.break_after_litmus: if litmusSubstring in self.passedLitmus and litmusSubstring not in litmusTag: _logger.info( " *** break after litmus {}".format(litmusTag)) sys.exit(-1) if litmusSubstring in litmusTag: self.passedLitmus[litmusSubstring] = True # Turn on max. debugging for selected request methods if verbose >= 3 and method in self.debug_methods: verbose = 5 debugBreak = True dumpRequest = True dumpResponse = True # Set debug options to environment environ["wsgidav.verbose"] = verbose # environ["wsgidav.debug_methods"] = self.debug_methods environ["wsgidav.debug_break"] = debugBreak environ["wsgidav.dump_request_body"] = dumpRequest environ["wsgidav.dump_response_body"] = dumpResponse # Dump request headers if dumpRequest: _logger.info("{} Request ---".format(method)) # _logger.info("<{}> --- {} Request ---".format( # threading.currentThread().ident, method)) for k, v in environ.items(): if k == k.upper(): _logger.info("{:<20}: '{}'".format( k, safe_re_encode(v, "utf8"))) _logger.info("\n") # Intercept start_response # sub_app_start_response = util.SubAppStartResponse() nbytes = 0 first_yield = True app_iter = self.next_app(environ, sub_app_start_response) for v in app_iter: # Start response (the first time) if first_yield: # Success! start_response(sub_app_start_response.status, sub_app_start_response.response_headers, sub_app_start_response.exc_info) # Dump response headers if first_yield and dumpResponse: _logger.info("<{}> ---{} Response({}): ---".format( threading.currentThread().ident, method, sub_app_start_response.status)) headersdict = dict(sub_app_start_response.response_headers) for envitem in headersdict.keys(): _logger.info("{}: {}".format(envitem, repr(headersdict[envitem]))) _logger.info("") # Check, if response is a binary string, otherwise we probably have # calculated a wrong content-length assert compat.is_bytes(v), v # Dump response body drb = environ.get("wsgidav.dump_response_body") if compat.is_basestring(drb): # Middleware provided a formatted body representation _logger.info(drb) drb = environ["wsgidav.dump_response_body"] = None elif drb is True: # Else dump what we get, (except for long GET responses) if method == "GET": if first_yield: _logger.info("{}...".format(v[:50])) elif len(v) > 0: _logger.info(v) nbytes += len(v) first_yield = False yield v if hasattr(app_iter, "close"): app_iter.close() # Start response (if it hasn't been done yet) if first_yield: # Success! start_response(sub_app_start_response.status, sub_app_start_response.response_headers, sub_app_start_response.exc_info) if dumpResponse: _logger.info("<{}> --- End of {} Response ({:d} bytes) ---".format( threading.currentThread().ident, method, nbytes)) return
def __call__(self, environ, start_response): """""" # srvcfg = environ["wsgidav.config"] verbose = self._config.get("verbose", 3) method = environ["REQUEST_METHOD"] debugBreak = False dumpRequest = False dumpResponse = False if verbose >= 5: dumpRequest = dumpResponse = True # Process URL commands if "dump_storage" in environ.get("QUERY_STRING", ""): dav = environ.get("wsgidav.provider") if dav.lock_manager: dav.lock_manager._dump() if dav.prop_manager: dav.prop_manager._dump() # Turn on max. debugging for selected litmus tests litmusTag = environ.get("HTTP_X_LITMUS", environ.get("HTTP_X_LITMUS_SECOND")) if litmusTag and verbose >= 3: _logger.info("----\nRunning litmus test '{}'...".format(litmusTag)) for litmusSubstring in self.debug_litmus: if litmusSubstring in litmusTag: verbose = 5 debugBreak = True dumpRequest = True dumpResponse = True break for litmusSubstring in self.break_after_litmus: if ( litmusSubstring in self.passedLitmus and litmusSubstring not in litmusTag ): _logger.info(" *** break after litmus {}".format(litmusTag)) sys.exit(-1) if litmusSubstring in litmusTag: self.passedLitmus[litmusSubstring] = True # Turn on max. debugging for selected request methods if verbose >= 3 and method in self.debug_methods: verbose = 5 debugBreak = True dumpRequest = True dumpResponse = True # Set debug options to environment environ["wsgidav.verbose"] = verbose # environ["wsgidav.debug_methods"] = self.debug_methods environ["wsgidav.debug_break"] = debugBreak environ["wsgidav.dump_request_body"] = dumpRequest environ["wsgidav.dump_response_body"] = dumpResponse # Dump request headers if dumpRequest: _logger.info("{} Request ---".format(method)) # _logger.info("<{}> --- {} Request ---".format( # threading.currentThread().ident, method)) for k, v in environ.items(): if k == k.upper(): _logger.info("{:<20}: '{}'".format(k, safe_re_encode(v, "utf8"))) _logger.info("\n") # Intercept start_response # sub_app_start_response = util.SubAppStartResponse() nbytes = 0 first_yield = True app_iter = self.next_app(environ, sub_app_start_response) for v in app_iter: # Start response (the first time) if first_yield: # Success! start_response( sub_app_start_response.status, sub_app_start_response.response_headers, sub_app_start_response.exc_info, ) # Dump response headers if first_yield and dumpResponse: _logger.info( "<{}> ---{} Response({}): ---".format( threading.currentThread().ident, method, sub_app_start_response.status, ) ) headersdict = dict(sub_app_start_response.response_headers) for envitem in headersdict.keys(): _logger.info("{}: {}".format(envitem, repr(headersdict[envitem]))) _logger.info("") # Check, if response is a binary string, otherwise we probably have # calculated a wrong content-length assert compat.is_bytes(v), v # Dump response body drb = environ.get("wsgidav.dump_response_body") if compat.is_basestring(drb): # Middleware provided a formatted body representation _logger.info(drb) drb = environ["wsgidav.dump_response_body"] = None elif drb is True: # Else dump what we get, (except for long GET responses) if method == "GET": if first_yield: _logger.info("{}...".format(v[:50])) elif len(v) > 0: _logger.info(v) nbytes += len(v) first_yield = False yield v if hasattr(app_iter, "close"): app_iter.close() # Start response (if it hasn't been done yet) if first_yield: # Success! start_response( sub_app_start_response.status, sub_app_start_response.response_headers, sub_app_start_response.exc_info, ) if dumpResponse: _logger.info( "<{}> --- End of {} Response ({:d} bytes) ---".format( threading.currentThread().ident, method, nbytes ) ) return
def __call__(self, environ, start_response): """""" # srvcfg = environ["wsgidav.config"] verbose = self._config.get("verbose", 2) self.last_request_time = '{0}_{1}'.format( datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S'), int(round(time.time() * 1000)), ) method = environ["REQUEST_METHOD"] debugBreak = False dumpRequest = False dumpResponse = False if verbose >= 3 or self._config.get("dump_requests"): dumpRequest = dumpResponse = True # Process URL commands if "dump_storage" in environ.get("QUERY_STRING"): dav = environ.get("wsgidav.provider") if dav.lockManager: dav.lockManager._dump() if dav.propManager: dav.propManager._dump() # Turn on max. debugging for selected litmus tests litmusTag = environ.get("HTTP_X_LITMUS", environ.get("HTTP_X_LITMUS_SECOND")) if litmusTag and verbose >= 2: print("----\nRunning litmus test '%s'..." % litmusTag, file=self.out) for litmusSubstring in self.debug_litmus: if litmusSubstring in litmusTag: verbose = 3 debugBreak = True dumpRequest = True dumpResponse = True break for litmusSubstring in self.break_after_litmus: if litmusSubstring in self.passedLitmus and litmusSubstring not in litmusTag: print(" *** break after litmus %s" % litmusTag, file=self.out) sys.exit(-1) if litmusSubstring in litmusTag: self.passedLitmus[litmusSubstring] = True # Turn on max. debugging for selected request methods if verbose >= 2 and method in self.debug_methods: verbose = 3 debugBreak = True dumpRequest = True dumpResponse = True # Set debug options to environment environ["wsgidav.verbose"] = verbose # environ["wsgidav.debug_methods"] = self.debug_methods environ["wsgidav.debug_break"] = debugBreak environ["wsgidav.dump_request_body"] = dumpRequest environ["wsgidav.dump_response_body"] = dumpResponse # Dump request headers if dumpRequest: print("<%s> --- %s Request ---" % ( threading.currentThread().ident, method), file=self.out) for k, v in environ.items(): if k == k.upper(): print("%20s: '%s'" % (k, v), file=self.out) print("\n", file=self.out) self._dump_request(environ, xml=None) # Intercept start_response # sub_app_start_response = util.SubAppStartResponse() nbytes = 0 first_yield = True app_iter = self._application(environ, sub_app_start_response) for v in app_iter: # Start response (the first time) if first_yield: # Success! start_response(sub_app_start_response.status, sub_app_start_response.response_headers, sub_app_start_response.exc_info) # Dump response headers if first_yield and dumpResponse: print("<%s> --- %s Response(%s): ---" % ( threading.currentThread().ident, method, sub_app_start_response.status), file=self.out) headersdict = dict(sub_app_start_response.response_headers) for envitem in headersdict.keys(): print("%s: %s" % (envitem, repr(headersdict[envitem])), file=self.out) print("", file=self.out) # Check, if response is a binary string, otherwise we probably have # calculated a wrong content-length assert compat.is_bytes(v), v # Dump response body drb = environ.get("wsgidav.dump_response_body") if compat.is_basestring(drb): # Middleware provided a formatted body representation print(drb, file=self.out) elif drb is True: # Else dump what we get, (except for long GET responses) if method == "GET": if first_yield: print(v[:50], "...", file=self.out) elif len(v) > 0: print(v, file=self.out) if dumpResponse: self._dump_response(sub_app_start_response, drb) drb = environ["wsgidav.dump_response_body"] = None nbytes += len(v) first_yield = False yield v if hasattr(app_iter, "close"): app_iter.close() # Start response (if it hasn't been done yet) if first_yield: # Success! start_response(sub_app_start_response.status, sub_app_start_response.response_headers, sub_app_start_response.exc_info) if dumpResponse: print("\n<%s> --- End of %s Response (%i bytes) ---" % ( threading.currentThread().ident, method, nbytes), file=self.out) return