def get_response_page(self): """Return a tuple (content-type, response page).""" # If it has pre- or post-condition: return as XML response if self.err_condition: return ("application/xml", compat.to_bytes(self.err_condition.as_string())) # Else return as HTML status = get_http_status_string(self) html = [] html.append( "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' " "'http://www.w3.org/TR/html4/strict.dtd'>" ) html.append("<html><head>") html.append( " <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>" ) html.append(" <title>{}</title>".format(status)) html.append("</head><body>") html.append(" <h1>{}</h1>".format(status)) html.append(" <p>{}</p>".format(compat.html_escape(self.get_user_info()))) html.append("<hr/>") html.append( "<a href='https://github.com/mar10/wsgidav/'>WsgiDAV/{}</a> - {}".format( __version__, compat.html_escape(str(datetime.datetime.now()), "utf-8") ) ) html.append("</body></html>") html = "\n".join(html) return ("text/html", compat.to_bytes(html))
def getResponsePage(self): """Return an tuple (content-type, response page).""" # If it has pre- or post-condition: return as XML response if self.errcondition: return ("application/xml", compat.to_bytes(self.errcondition.as_string())) # Else return as HTML status = getHttpStatusString(self) html = [] html.append("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>"); html.append("<html><head>") html.append(" <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>") html.append(" <title>%s</title>" % status) html.append("</head><body>") html.append(" <h1>%s</h1>" % status) html.append(" <p>%s</p>" % compat.html_escape(self.getUserInfo())) # html.append(" <hr>") # html.append(" <p>%s</p>" % cgi.escape(str(datetime.datetime.now()))) # if self._server_descriptor: # respbody.append(self._server_descriptor + "<hr>") html.append("<hr/>") html.append("<a href='https://github.com/mar10/wsgidav/'>WsgiDAV/%s</a> - %s" % (__version__, compat.html_escape(str(datetime.datetime.now()), "utf-8"))) html.append("</body></html>") html = "\n".join(html) return ("text/html", compat.to_bytes(html))
def get_response_page(self): """Return a tuple (content-type, response page).""" # If it has pre- or post-condition: return as XML response if self.err_condition: return ("application/xml", compat.to_bytes(self.err_condition.as_string())) # Else return as HTML status = get_http_status_string(self) html = [] html.append("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' " "'http://www.w3.org/TR/html4/strict.dtd'>") html.append("<html><head>") html.append( " <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>" ) html.append(" <title>{}</title>".format(status)) html.append("</head><body>") html.append(" <h1>{}</h1>".format(status)) html.append(" <p>{}</p>".format( compat.html_escape(self.get_user_info()))) html.append("<hr/>") html.append( "<a href='https://github.com/mar10/wsgidav/'>WsgiDAV/{}</a> - {}". format(__version__, compat.html_escape(str(datetime.datetime.now()), "utf-8"))) html.append("</body></html>") html = "\n".join(html) return ("text/html", compat.to_bytes(html))
def __call__(self, environ, start_response): path = environ["PATH_INFO"] dav_res = None if environ["wsgidav.provider"]: dav_res = environ["wsgidav.provider"].get_resource_inst(path, environ) if ( environ["REQUEST_METHOD"] in ("GET", "HEAD") and dav_res and dav_res.is_collection ): if util.get_content_length(environ) != 0: self._fail( HTTP_MEDIATYPE_NOT_SUPPORTED, "The server does not handle any body content.", ) if environ["REQUEST_METHOD"] == "HEAD": return util.send_status_response( environ, start_response, HTTP_OK, is_head=True ) # Support DAV mount (http://www.ietf.org/rfc/rfc4709.txt) if self.dir_config.get("davmount") and "davmount" in environ.get( "QUERY_STRING", "" ): collectionUrl = util.make_complete_url(environ) collectionUrl = collectionUrl.split("?", 1)[0] res = compat.to_bytes(DAVMOUNT_TEMPLATE.format(collectionUrl)) # TODO: support <dm:open>%s</dm:open> start_response( "200 OK", [ ("Content-Type", "application/davmount+xml"), ("Content-Length", str(len(res))), ("Cache-Control", "private"), ("Date", util.get_rfc1123_time()), ], ) return [res] context = self._get_context(environ, dav_res) res = self.template.render(**context) res = compat.to_bytes(res) start_response( "200 OK", [ ("Content-Type", "text/html"), ("Content-Length", str(len(res))), ("Cache-Control", "private"), ("Date", util.get_rfc1123_time()), ], ) return [res] return self.next_app(environ, start_response)
def __call__(self, environ, start_response): path = environ["PATH_INFO"] dav_res = None if environ["wsgidav.provider"]: dav_res = environ["wsgidav.provider"].get_resource_inst(path, environ) if ( environ["REQUEST_METHOD"] in ("GET", "HEAD") and dav_res and dav_res.is_collection ): if util.get_content_length(environ) != 0: self._fail( HTTP_MEDIATYPE_NOT_SUPPORTED, "The server does not handle any body content.", ) if environ["REQUEST_METHOD"] == "HEAD": return util.send_status_response( environ, start_response, HTTP_OK, is_head=True ) # Support DAV mount (http://www.ietf.org/rfc/rfc4709.txt) if self.dir_config.get("davmount") and "davmount" in environ.get( "QUERY_STRING", "" ): collectionUrl = util.make_complete_url(environ) collectionUrl = collectionUrl.split("?", 1)[0] res = compat.to_bytes(DAVMOUNT_TEMPLATE.format(collectionUrl)) # TODO: support <dm:open>%s</dm:open> start_response( "200 OK", [ ("Content-Type", "application/davmount+xml"), ("Content-Length", str(len(res))), ("Cache-Control", "private"), ("Date", util.get_rfc1123_time()), ], ) return [res] context = self._get_context(environ, dav_res) res = self.template.render(**context) res = compat.to_bytes(res) start_response( "200 OK", [ ("Content-Type", "text/html"), ("Content-Length", str(len(res))), ("Cache-Control", "private"), ("Date", util.get_rfc1123_time()), ], ) return [res] return self.next_app(environ, start_response)
def send_digest_auth_response(self, environ, start_response): realm_name = self.domain_controller.get_domain_realm( environ["PATH_INFO"], environ) random.seed() serverkey = hex(random.getrandbits(32))[2:] etagkey = calc_hexdigest(environ["PATH_INFO"]) timekey = str(time.time()) nonce_source = timekey + calc_hexdigest(timekey + ":" + etagkey + ":" + serverkey) nonce = calc_base64(nonce_source) wwwauthheaders = 'Digest realm="{}", nonce="{}", algorithm=MD5, qop="auth"'.format( realm_name, nonce) _logger.debug("401 Not Authorized for realm '{}' (digest): {}".format( realm_name, wwwauthheaders)) body = compat.to_bytes(self.get_error_message()) start_response( "401 Not Authorized", [ ("WWW-Authenticate", wwwauthheaders), ("Content-Type", "text/html"), ("Content-Length", str(len(body))), ("Date", util.get_rfc1123_time()), ], ) return [body]
def send_digest_auth_response(self, environ, start_response): realm = self.domain_controller.get_domain_realm(environ["PATH_INFO"], environ) random.seed() serverkey = hex(random.getrandbits(32))[2:] etagkey = calc_hexdigest(environ["PATH_INFO"]) timekey = str(time.time()) nonce_source = timekey + calc_hexdigest( timekey + ":" + etagkey + ":" + serverkey ) nonce = calc_base64(nonce_source) wwwauthheaders = 'Digest realm="{}", nonce="{}", algorithm=MD5, qop="auth"'.format( realm, nonce ) _logger.debug( "401 Not Authorized for realm '{}' (digest): {}".format( realm, wwwauthheaders ) ) body = compat.to_bytes(self.error_message_401) start_response( "401 Not Authorized", [ ("WWW-Authenticate", wwwauthheaders), ("Content-Type", "text/html"), ("Content-Length", str(len(body))), ("Date", util.get_rfc1123_time()), ], ) return [body]
def wsgiWriteData(self, data): if not self.wsgiSentHeaders: status, headers = self.wsgiHeaders # Need to send header prior to data statusCode = status[:status.find(" ")] statusMsg = status[status.find(" ") + 1:] _logger.debug("wsgiWriteData: send headers '{!r}', {!r}".format( status, headers)) self.send_response(int(statusCode), statusMsg) for header, value in headers: self.send_header(header, value) self.end_headers() self.wsgiSentHeaders = 1 # Send the data # assert type(data) is str # If not, Content-Length is propably wrong! _logger.debug("wsgiWriteData: write {} bytes: '{!r}'...".format( len(data), compat.to_native(data[:50]))) if compat.is_unicode( data): # If not, Content-Length is propably wrong! _logger.info( "ext_wsgiutils_server: Got unicode data: {!r}".format(data)) # data = compat.wsgi_to_bytes(data) data = compat.to_bytes(data) try: self.wfile.write(data) except socket.error as e: # Suppress stack trace when client aborts connection disgracefully: # 10053: Software caused connection abort # 10054: Connection reset by peer if e.args[0] in (10053, 10054): _logger.info("*** Caught socket.error: ", e, file=sys.stderr) else: raise
def sendDigestAuthResponse(self, environ, start_response): realmname = self._domaincontroller.getDomainRealm( environ["PATH_INFO"], environ) random.seed() serverkey = hex(random.getrandbits(32))[2:] etagkey = calc_hexdigest(environ["PATH_INFO"]) timekey = str(time.time()) nonce_source = timekey + \ calc_hexdigest(timekey + ":" + etagkey + ":" + serverkey) # nonce = to_native(base64.b64encode(compat.to_bytes(nonce_source))) nonce = calc_base64(nonce_source) wwwauthheaders = ('Digest realm="%s", nonce="%s", algorithm=MD5, qop="auth"' % (realmname, nonce)) _logger.debug("401 Not Authorized for realm '%s' (digest): %s" % (realmname, wwwauthheaders)) body = compat.to_bytes(self.getErrorMessage()) # start_response("403 Forbidden", [("WWW-Authenticate", wwwauthheaders), start_response("401 Not Authorized", [("WWW-Authenticate", wwwauthheaders), ("Content-Type", "text/html"), ("Content-Length", str(len(body))), ("Date", util.getRfc1123Time()), ]) return [body]
def testGetPut(self): """Read and write file contents.""" app = self.app # Prepare file content data1 = b"this is a file\nwith two lines" data2 = b"this is another file\nwith three lines\nsee?" # Big file with 10 MB lines = [] line = "." * (1000 - 6 - len("\n")) for i in compat.xrange(10 * 1000): lines.append("%04i: %s\n" % (i, line)) data3 = "".join(lines) data3 = compat.to_bytes(data3) # Remove old test files app.delete("/file1.txt", expect_errors=True) app.delete("/file2.txt", expect_errors=True) app.delete("/file3.txt", expect_errors=True) # Access unmapped resource (expect '404 Not Found') app.delete("/file1.txt", status=404) app.get("/file1.txt", status=404) # PUT a small file (expect '201 Created') app.put("/file1.txt", params=data1, status=201) res = app.get("/file1.txt", status=200) assert res.body == data1, "GET file content different from PUT" # PUT overwrites a small file (expect '204 No Content') app.put("/file1.txt", params=data2, status=204) res = app.get("/file1.txt", status=200) assert res.body == data2, "GET file content different from PUT" # PUT writes a big file (expect '201 Created') app.put("/file2.txt", params=data3, status=201) res = app.get("/file2.txt", status=200) assert res.body == data3, "GET file content different from PUT" # Request must not contain a body (expect '415 Media Type Not # Supported') app.request( "/file1.txt", method="GET", headers={"Content-Length": compat.to_native(len(data1))}, body=data1, status=415, ) # Delete existing resource (expect '204 No Content') app.delete("/file1.txt", status=204) # Get deleted resource (expect '404 Not Found') app.get("/file1.txt", status=404) # PUT a small file (expect '201 Created') app.put("/file1.txt", params=data1, status=201)
def testGetPut(self): """Read and write file contents.""" app = self.app # Prepare file content data1 = b"this is a file\nwith two lines" data2 = b"this is another file\nwith three lines\nsee?" # Big file with 10 MB lines = [] line = "." * (1000 - 6 - len("\n")) for i in compat.xrange(10 * 1000): lines.append("%04i: %s\n" % (i, line)) data3 = "".join(lines) data3 = compat.to_bytes(data3) # Remove old test files app.delete("/file1.txt", expect_errors=True) app.delete("/file2.txt", expect_errors=True) app.delete("/file3.txt", expect_errors=True) # Access unmapped resource (expect '404 Not Found') app.delete("/file1.txt", status=404) app.get("/file1.txt", status=404) # PUT a small file (expect '201 Created') app.put("/file1.txt", params=data1, status=201) res = app.get("/file1.txt", status=200) assert res.body == data1, "GET file content different from PUT" # PUT overwrites a small file (expect '204 No Content') app.put("/file1.txt", params=data2, status=204) res = app.get("/file1.txt", status=200) assert res.body == data2, "GET file content different from PUT" # PUT writes a big file (expect '201 Created') app.put("/file2.txt", params=data3, status=201) res = app.get("/file2.txt", status=200) assert res.body == data3, "GET file content different from PUT" # Request must not contain a body (expect '415 Media Type Not # Supported') app.request( "/file1.txt", method="GET", headers={"Content-Length": compat.to_native(len(data1))}, body=data1, status=415, ) # Delete existing resource (expect '204 No Content') app.delete("/file1.txt", status=204) # Get deleted resource (expect '404 Not Found') app.get("/file1.txt", status=404) # PUT a small file (expect '201 Created') app.put("/file1.txt", params=data1, status=201)
def get_content(self): fileLinks = [ "<a href='%s'>%s</a>\n" % (os.path.basename(f), f) for f in self.data["resPathList"] ] dict = self.data.copy() dict["fileLinks"] = ", ".join(fileLinks) if self.name == ".Info.html": html = ( """\ <html><head> <title>%(title)s</title> </head><body> <h1>%(title)s</h1> <table> <tr> <td>Description</td> <td>%(description)s</td> </tr><tr> <td>Status</td> <td>%(status)s</td> </tr><tr> <td>Tags</td> <td>%(tags)s</td> </tr><tr> <td>Orga unit</td> <td>%(orga)s</td> </tr><tr> <td>Files</td> <td>%(fileLinks)s</td> </tr><tr> <td>Key</td> <td>%(key)s</td> </tr> </table> <p>This is a virtual WsgiDAV resource called '%(title)s'.</p> </body></html>""" % dict ) elif self.name == ".Info.txt": lines = [ self.data["title"], "=" * len(self.data["title"]), self.data["description"], "", "Status: %s" % self.data["status"], "Orga: %8s" % self.data["orga"], "Tags: '%s'" % "', '".join(self.data["tags"]), "Key: %s" % self.data["key"], ] html = "\n".join(lines) elif self.name == ".Description.txt": html = self.data["description"] else: raise DAVError(HTTP_INTERNAL_ERROR, "Invalid artifact '%s'" % self.name) return compat.BytesIO(compat.to_bytes(html))
def _consumer(): # print("_consumer: {}".format(self.target_path)) with open(self.target_path, "wb") as f: s = 0 # print("_consumer: read()...") data = queue.read() while data: s += len(data) # print("_consumer: read(): write") f.write(compat.to_bytes(data)) data = queue.read()
def get_content(self): fileLinks = [ "<a href='%s'>%s</a>\n" % (os.path.basename(f), f) for f in self.data["resPathList"] ] dict = self.data.copy() dict["fileLinks"] = ", ".join(fileLinks) if self.name == ".Info.html": html = ("""\ <html><head> <title>%(title)s</title> </head><body> <h1>%(title)s</h1> <table> <tr> <td>Description</td> <td>%(description)s</td> </tr><tr> <td>Status</td> <td>%(status)s</td> </tr><tr> <td>Tags</td> <td>%(tags)s</td> </tr><tr> <td>Orga unit</td> <td>%(orga)s</td> </tr><tr> <td>Files</td> <td>%(fileLinks)s</td> </tr><tr> <td>Key</td> <td>%(key)s</td> </tr> </table> <p>This is a virtual WsgiDAV resource called '%(title)s'.</p> </body></html>""" % dict) elif self.name == ".Info.txt": lines = [ self.data["title"], "=" * len(self.data["title"]), self.data["description"], "", "Status: %s" % self.data["status"], "Orga: %8s" % self.data["orga"], "Tags: '%s'" % "', '".join(self.data["tags"]), "Key: %s" % self.data["key"], ] html = "\n".join(lines) elif self.name == ".Description.txt": html = self.data["description"] else: raise DAVError(HTTP_INTERNAL_ERROR, "Invalid artifact '%s'" % self.name) return compat.BytesIO(compat.to_bytes(html))
def _consumer(): # print("_consumer: {}".format(self.target_path)) with open(self.target_path, "wb") as f: s = 0 # print("_consumer: read()...") data = queue.read() while data: s += len(data) # print("_consumer: read(): write") f.write(compat.to_bytes(data)) data = queue.read()
def sendNotAuthorizedResponse(self, body, environ, start_response): _logger.debug('401 Not Authorized (token)') wwwauthheaders = 'Token' body = compat.to_bytes(body) start_response('401 Not Authorized', [ ('WWW-Authenticate', wwwauthheaders), ('Content-Type', 'text/html'), ('Content-Length', str(len(body))), ('Date', util.getRfc1123Time()), ]) return [body]
def sendBasicAuthResponse(self, environ, start_response): realmname = self._domaincontroller.getDomainRealm( environ["PATH_INFO"], environ) _logger.debug("401 Not Authorized for realm '{}' (basic)".format(realmname)) wwwauthheaders = "Basic realm=\"" + realmname + "\"" body = compat.to_bytes(self.getErrorMessage()) start_response("401 Not Authorized", [("WWW-Authenticate", wwwauthheaders), ("Content-Type", "text/html"), ("Content-Length", str(len(body))), ("Date", util.getRfc1123Time()), ]) return [body]
def __call__(self, environ, start_response): path = environ["PATH_INFO"] davres = None if environ["wsgidav.provider"]: davres = environ["wsgidav.provider"].getResourceInst(path, environ) if environ["REQUEST_METHOD"] in ( "GET", "HEAD") and davres and davres.isCollection: if util.getContentLength(environ) != 0: self._fail(HTTP_MEDIATYPE_NOT_SUPPORTED, "The server does not handle any body content.") if environ["REQUEST_METHOD"] == "HEAD": return util.sendStatusResponse(environ, start_response, HTTP_OK) # Support DAV mount (http://www.ietf.org/rfc/rfc4709.txt) dirConfig = environ["wsgidav.config"].get("dir_browser", {}) if dirConfig.get("davmount") and "davmount" in environ.get( "QUERY_STRING", ""): collectionUrl = util.makeCompleteUrl(environ) collectionUrl = collectionUrl.split("?", 1)[0] res = """ <dm:mount xmlns:dm="http://purl.org/NET/webdav/mount"> <dm:url>{}</dm:url> </dm:mount>""".format(collectionUrl) # TODO: support <dm:open>%s</dm:open> start_response("200 OK", [ ("Content-Type", "application/davmount+xml"), ("Content-Length", str(len(res))), ("Cache-Control", "private"), ("Date", util.getRfc1123Time()), ]) return [res] context = self._get_context(environ, davres) res = self.template.render(**context) res = compat.to_bytes(res) start_response("200 OK", [ ("Content-Type", "text/html"), ("Content-Length", str(len(res))), ("Cache-Control", "private"), ("Date", util.getRfc1123Time()), ]) return [res] return self.next_app(environ, start_response)
def send_basic_auth_response(self, environ, start_response): realm = self.domain_controller.get_domain_realm(environ["PATH_INFO"], environ) _logger.debug("401 Not Authorized for realm '{}' (basic)".format(realm)) wwwauthheaders = 'Basic realm="' + realm + '"' body = compat.to_bytes(self.error_message_401) start_response( "401 Not Authorized", [ ("WWW-Authenticate", wwwauthheaders), ("Content-Type", "text/html"), ("Content-Length", str(len(body))), ("Date", util.get_rfc1123_time()), ], ) return [body]
def send_basic_auth_response(self, environ, start_response): realm = self.domain_controller.get_domain_realm(environ["PATH_INFO"], environ) _logger.debug("401 Not Authorized for realm '{}' (basic)".format(realm)) wwwauthheaders = 'Basic realm="' + realm + '"' body = compat.to_bytes(self.error_message_401) start_response( "401 Not Authorized", [ ("WWW-Authenticate", wwwauthheaders), ("Content-Type", "text/html"), ("Content-Length", str(len(body))), ("Date", util.get_rfc1123_time()), ], ) return [body]
def authBasicAuthRequest(self, environ, start_response): realmname = self._domaincontroller.getDomainRealm( environ["PATH_INFO"], environ) authheader = environ["HTTP_AUTHORIZATION"] authvalue = "" try: authvalue = authheader[len("Basic "):].strip() except Exception: authvalue = "" # authvalue = authvalue.strip().decode("base64") authvalue = compat.base64_decodebytes(compat.to_bytes(authvalue)) authvalue = compat.to_native(authvalue) username, password = authvalue.split(":", 1) if self._domaincontroller.authDomainUser(realmname, username, password, environ): environ["http_authenticator.realm"] = realmname environ["http_authenticator.username"] = username return self._application(environ, start_response) return self.sendBasicAuthResponse(environ, start_response)
def handle_basic_auth_request(self, environ, start_response): realm = self.domain_controller.get_domain_realm(environ["PATH_INFO"], environ) auth_header = environ["HTTP_AUTHORIZATION"] auth_value = "" try: auth_value = auth_header[len("Basic ") :].strip() except Exception: auth_value = "" auth_value = compat.base64_decodebytes(compat.to_bytes(auth_value)) auth_value = compat.to_native(auth_value) user_name, password = auth_value.split(":", 1) if self.domain_controller.basic_auth_user(realm, user_name, password, environ): environ["wsgidav.auth.realm"] = realm environ["wsgidav.auth.user_name"] = user_name return self.next_app(environ, start_response) _logger.warning( "Authentication (basic) failed for user '{}', realm '{}'.".format( user_name, realm ) ) return self.send_basic_auth_response(environ, start_response)
def handle_basic_auth_request(self, environ, start_response): realm = self.domain_controller.get_domain_realm(environ["PATH_INFO"], environ) auth_header = environ["HTTP_AUTHORIZATION"] auth_value = "" try: auth_value = auth_header[len("Basic ") :].strip() except Exception: auth_value = "" auth_value = compat.base64_decodebytes(compat.to_bytes(auth_value)) auth_value = compat.to_native(auth_value) user_name, password = auth_value.split(":", 1) if self.domain_controller.basic_auth_user(realm, user_name, password, environ): environ["wsgidav.auth.realm"] = realm environ["wsgidav.auth.user_name"] = user_name return self.next_app(environ, start_response) _logger.warning( "Authentication (basic) failed for user '{}', realm '{}'.".format( user_name, realm ) ) return self.send_basic_auth_response(environ, start_response)
def testGetPut(self): """Read and write file contents.""" client = self.client # Prepare file content data1 = b"this is a file\nwith two lines" data2 = b"this is another file\nwith three lines\nsee?" # Big file with 10 MB lines = [] line = "." * (1000 - 6 - len("\n")) for i in compat.xrange(10 * 1000): lines.append("%04i: %s\n" % (i, line)) data3 = "".join(lines) data3 = compat.to_bytes(data3) # Cleanup client.delete("/test/") client.mkcol("/test/") client.checkResponse(201) # PUT files client.put("/test/file1.txt", data1) client.checkResponse(201) client.put("/test/file2.txt", data2) client.checkResponse(201) client.put("/test/bigfile.txt", data3) client.checkResponse(201) body = client.get("/test/file1.txt") client.checkResponse(200) assert body == data1, "Put/Get produced different bytes" # PUT with overwrite must return 204 No Content, instead of 201 Created client.put("/test/file2.txt", data2) client.checkResponse(204) client.mkcol("/test/folder") client.checkResponse(201) locks = client.set_lock("/test/lock-0", owner="test-bench", locktype="write", lockscope="exclusive", depth="infinity") client.checkResponse(201) # created assert len(locks) == 1, "LOCK failed" token = locks[0] client.refresh_lock("/test/lock-0", token) client.checkResponse(200) # ok client.unlock("/test/lock-0", token) client.checkResponse(204) # no content client.unlock("/test/lock-0", token) # 409 Conflict, because resource was not locked # (http://www.webdav.org/specs/rfc4918.html#METHOD_UNLOCK) client.checkResponse(409) client.proppatch("/test/file1.txt", set_props=[("{testns:}testname", "testval"), ], remove_props=None) client.checkResponse() client.copy("/test/file1.txt", "/test/file2.txt", depth='infinity', overwrite=True) client.checkResponse() client.move("/test/file2.txt", "/test/file2_moved.txt", depth='infinity', overwrite=True) client.checkResponse() client.propfind("/", properties="allprop", namespace='DAV:', depth=None, headers=None) client.checkResponse()
def testGetPut(self): """Read and write file contents.""" client = self.client # Prepare file content data1 = b"this is a file\nwith two lines" data2 = b"this is another file\nwith three lines\nsee?" # Big file with 10 MB lines = [] line = "." * (1000 - 6 - len("\n")) for i in compat.xrange(10 * 1000): lines.append("%04i: %s\n" % (i, line)) data3 = "".join(lines) data3 = compat.to_bytes(data3) # Cleanup client.delete("/test/") client.mkcol("/test/") client.check_response(201) # PUT files client.put("/test/file1.txt", data1) client.check_response(201) client.put("/test/file2.txt", data2) client.check_response(201) client.put("/test/bigfile.txt", data3) client.check_response(201) body = client.get("/test/file1.txt") client.check_response(200) assert body == data1, "Put/Get produced different bytes" # PUT with overwrite must return 204 No Content, instead of 201 Created client.put("/test/file2.txt", data2) client.check_response(204) client.mkcol("/test/folder") client.check_response(201) # if a LOCK request is sent to an unmapped URL, we must create a # lock-null resource and return '201 Created', instead of '404 Not found' locks = client.set_lock( "/test/lock-0", owner="test-bench", lock_type="write", lock_scope="exclusive", depth="infinity", ) client.check_response(201) # created assert len(locks) == 1, "LOCK failed" token = locks[0] client.refresh_lock("/test/lock-0", token) client.check_response(200) # ok client.unlock("/test/lock-0", token) client.check_response(204) # no content client.unlock("/test/lock-0", token) # 409 Conflict, because resource was not locked # (http://www.webdav.org/specs/rfc4918.html#METHOD_UNLOCK) client.check_response(409) # issue #71: unlock non existing resource client.unlock("/test/lock-not-existing", token) client.check_response(404) client.proppatch( "/test/file1.txt", set_props=[("{testns:}testname", "testval")], remove_props=None, ) client.check_response() client.copy( "/test/file1.txt", "/test/file2.txt", depth="infinity", overwrite=True ) client.check_response() client.move( "/test/file2.txt", "/test/file2_moved.txt", depth="infinity", overwrite=True ) client.check_response() client.propfind( "/", properties="allprop", namespace="DAV:", depth=None, headers=None ) client.check_response()
def write_test_file(name, size): path = os.path.join(gettempdir(), name) with open(path, "wb") as f: f.write(compat.to_bytes("*") * size) return path
def write_test_file(name, size): path = os.path.join(gettempdir(), name) with open(path, "wb") as f: f.write(compat.to_bytes("*") * size) return path
def _compute_http_digest_a1(self, realm, user_name, password): """Internal helper for derived classes to compute a digest hash (A1 part).""" data = user_name + ":" + realm + ":" + password A1 = md5(compat.to_bytes(data)).hexdigest() return A1
def calc_base64(s): """Return base64 encoded binarystring.""" s = compat.to_bytes(s) s = base64.encodestring(s).strip() # return bytestring return compat.to_native(s)
def md5h(data): return md5(compat.to_bytes(data)).hexdigest()
def calc_base64(s): """Return base64 encoded binarystring.""" s = compat.to_bytes(s) s = compat.base64_encodebytes(s).strip() # return bytestring return compat.to_native(s)
def _listDirectory(self, davres, environ, start_response): """ @see: http://www.webdav.org/specs/rfc4918.html#rfc.section.9.4 """ assert davres.isCollection dirConfig = environ["wsgidav.config"].get("dir_browser", {}) displaypath = compat.unquote(davres.getHref()) isReadOnly = environ["wsgidav.provider"].isReadOnly() trailer = dirConfig.get("response_trailer") if trailer: trailer = trailer.replace("${version}", "<a href='https://github.com/mar10/wsgidav/'>WsgiDAV/%s</a>" % __version__) trailer = trailer.replace("${time}", util.getRfc1123Time()) else: trailer = ("<a href='https://github.com/mar10/wsgidav/'>WsgiDAV/%s</a> - %s" % (__version__, util.getRfc1123Time())) html = [] html.append( "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' " "'http://www.w3.org/TR/html4/strict.dtd'>") html.append("<html>") html.append("<head>") html.append( "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>") html.append("<meta name='generator' content='WsgiDAV %s'>" % __version__) html.append("<title>WsgiDAV - Index of %s </title>" % displaypath) html.append("<script type='text/javascript'>%s</script>" % PAGE_SCRIPT) html.append("<style type='text/css'>%s</style>" % PAGE_CSS) # Special CSS to enable MS Internet Explorer behaviour if dirConfig.get("ms_mount"): html.append( "<style type='text/css'> A {behavior: url(#default#AnchorClick);} </style>") if dirConfig.get("ms_sharepoint_plugin"): html.append( "<object id='winFirefoxPlugin' type='application/x-sharepoint' width='0' " "height='0' style=''visibility: hidden;'></object>") html.append("</head>") html.append("<body onload='onLoad()'>") # Title html.append("<h1>Index of %s</h1>" % displaypath) # Add DAV-Mount link and Web-Folder link links = [] if dirConfig.get("davmount"): links.append("<a title='Open this folder in a WebDAV client.' " "href='%s?davmount'>Mount</a>" % util.makeCompleteUrl(environ)) if dirConfig.get("ms_mount"): links.append("<a title='Open as Web Folder (requires Microsoft Internet Explorer)' " "href='' FOLDER='%s'>Open as Web Folder</a>" % util.makeCompleteUrl(environ)) # html.append("<a href='' FOLDER='%ssetup.py'>Open setup.py as WebDAV</a>" % util.makeCompleteUrl(environ)) if links: html.append("<p>%s</p>" % " – ".join(links)) html.append("<hr>") # Listing html.append("<table onclick='return onClickTable(event)'>") html.append("<thead>") html.append( "<tr><th>Name</th> <th>Type</th> <th class='right'>Size</th> " "<th class='right'>Last modified</th> </tr>") html.append("</thead>") html.append("<tbody>") if davres.path in ("", "/"): html.append( "<tr><td>Top level share</td> <td></td> <td></td> <td></td> </tr>") else: parentUrl = util.getUriParent(davres.getHref()) html.append("<tr><td><a href='" + parentUrl + "'>Parent Directory</a></td> <td></td> <td></td> <td></td> </tr>") # Ask collection for member info list dirInfoList = davres.getDirectoryInfo() if dirInfoList is None: # No pre-build info: traverse members dirInfoList = [] childList = davres.getDescendants(depth="1", addSelf=False) for res in childList: di = res.getDisplayInfo() href = res.getHref() infoDict = {"href": href, "class": "", "displayName": res.getDisplayName(), "lastModified": res.getLastModified(), "isCollection": res.isCollection, "contentLength": res.getContentLength(), "displayType": di.get("type"), "displayTypeComment": di.get("typeComment"), } if not isReadOnly and not res.isCollection: ext = os.path.splitext(href)[1].lstrip(".").lower() officeType = msOfficeExtToTypeMap.get(ext) if officeType: # print "OT", officeType # print "OT", dirConfig if dirConfig.get("ms_sharepoint_plugin"): infoDict["class"] = "msoffice" elif dirConfig.get("ms_sharepoint_urls"): infoDict[ "href"] = "ms-%s:ofe|u|%s" % (officeType, href) dirInfoList.append(infoDict) # for infoDict in dirInfoList: lastModified = infoDict.get("lastModified") if lastModified is None: infoDict["strModified"] = "" else: infoDict["strModified"] = util.getRfc1123Time(lastModified) infoDict["strSize"] = "-" if not infoDict.get("isCollection"): contentLength = infoDict.get("contentLength") if contentLength is not None: infoDict["strSize"] = util.byteNumberString(contentLength) html.append("""\ <tr><td><a href="%(href)s" class="%(class)s">%(displayName)s</a></td> <td>%(displayType)s</td> <td class='right'>%(strSize)s</td> <td class='right'>%(strModified)s</td></tr>""" % infoDict) html.append("</tbody>") html.append("</table>") html.append("<hr>") if "http_authenticator.username" in environ: if environ.get("http_authenticator.username"): html.append("<p>Authenticated user: '******', realm: '%s'.</p>" % (environ.get("http_authenticator.username"), environ.get("http_authenticator.realm"))) # else: # html.append("<p>Anonymous</p>") if trailer: html.append("<p class='trailer'>%s</p>" % trailer) html.append("</body></html>") body = "\n".join(html) body = compat.to_bytes(body) start_response("200 OK", [("Content-Type", "text/html"), ("Content-Length", str(len(body))), ("Date", util.getRfc1123Time()), ]) return [body]
def calc_hexdigest(s): """Return md5 digest for a string.""" s = compat.to_bytes(s) return md5(s).hexdigest() # return native string
def calc_hexdigest(s): """Return md5 digest for a string.""" s = compat.to_bytes(s) return md5(s).hexdigest() # return native string
def _bench_script(opts): # print("Scriptes benchmarks") # print("_bench_script(), {}...".format(opts)) from tests import davclient server_url = opts.get("external_server") or "http://localhost:8080/" client = davclient.DAVClient(server_url) client.set_basic_auth("tester", "secret") # Prepare file content data_1k = b"." * 1000 # Prepare big file with 10 MB lines = [] line = "." * (1000 - 6 - len("\n")) for i in compat.xrange(10 * 1000): lines.append("%04i: %s\n" % (i, line)) data_10m = "".join(lines) data_10m = compat.to_bytes(data_10m) with Timing("Setup fixture"): _setup_fixture(opts, client) # PUT files with Timing("1000 x PUT 1 kB", 1000, "{:>6.1f} req/sec", 1, "{:>7,.3f} MB/sec"): for _ in compat.xrange(1000): client.put("/test/file1.txt", data_1k) client.check_response() with Timing("10 x PUT 10 MB", 10, "{:>6.1f} req/sec", 100, "{:>7,.3f} MB/sec"): for _ in compat.xrange(10): client.put("/test/bigfile.txt", data_10m) client.check_response() with Timing("1000 x GET 1 kB", 1000, "{:>6.1f} req/sec", 1, "{:>7,.3f} MB/sec"): for _ in compat.xrange(1000): body = client.get("/test/file1.txt") client.check_response() with Timing("10 x GET 10 MB", 10, "{:>6.1f} req/sec", 100, "{:>7,.3f} MB/sec"): for _ in compat.xrange(10): body = client.get("/test/bigfile.txt") # noqa F841 client.check_response() with Timing("10 x COPY 10 MB", 10, "{:>6.1f} req/sec", 100, "{:>7,.3f} MB/sec"): for _ in compat.xrange(10): client.copy( "/test/bigfile.txt", "/test/bigfile-copy.txt", depth="infinity", overwrite=True, ) client.check_response() with Timing("100 x MOVE 10 MB", 100, "{:>6.1f} req/sec"): name_from = "/test/bigfile-copy.txt" for i in compat.xrange(100): name_to = "/test/bigfile-copy-{}.txt".format(i) client.move(name_from, name_to, depth="infinity", overwrite=True) name_from = name_to client.check_response() with Timing("100 x LOCK/UNLOCK", 200, "{:>6.1f} req/sec"): for _ in compat.xrange(100): locks = client.set_lock( "/test/lock-0", owner="test-bench", locktype="write", lockscope="exclusive", depth="infinity", ) token = locks[0] client.unlock("/test/lock-0", token) client.check_response() with Timing("1000 x PROPPATCH", 1000, "{:>6.1f} req/sec"): for _ in compat.xrange(1000): client.proppatch( "/test/file1.txt", set_props=[("{testns:}testname", "testval")], remove_props=None, ) client.check_response() with Timing("500 x PROPFIND", 500, "{:>6.1f} req/sec"): for _ in compat.xrange(500): client.propfind("/", properties="allprop", namespace="DAV:", depth=None, headers=None) client.check_response()
def md5h(self, data): return md5(compat.to_bytes(data)).hexdigest()
def _compute_http_digest_a1(self, realm, user_name, password): """Internal helper for derived classes to compute a digest hash (A1 part).""" data = user_name + ":" + realm + ":" + password A1 = md5(compat.to_bytes(data)).hexdigest() return A1