Giter Site home page Giter Site logo

mar10 / wsgidav Goto Github PK

View Code? Open in Web Editor NEW
813.0 22.0 144.0 9.23 MB

A generic and extendable WebDAV server based on WSGI

Home Page: https://wsgidav.readthedocs.io

License: MIT License

Python 98.79% Shell 0.02% JavaScript 0.41% CSS 0.15% HTML 0.50% Dockerfile 0.14%
webdav python wsgi server webserver

wsgidav's Introduction

logo WsgiDAV

Tests Latest Version License Documentation Status codecov Released with: Yabs StackOverflow: WsgiDAV

Edit online in vscode.dev Open in Visual Studio Code (experimental)

A generic and extendable WebDAV server written in Python and based on WSGI.

Main features:

  • WsgiDAV is a stand-alone WebDAV server with SSL support, that can be installed and run as Python command line script on Linux, OSX, and Windows:

    $ pip install wsgidav cheroot
    $ wsgidav --host=0.0.0.0 --port=80 --root=/tmp --auth=anonymous
    Running without configuration file.
    10:54:16.597 - INFO    : WsgiDAV/4.0.0-a1 Python/3.9.1 macOS-12.0.1-x86_64-i386-64bit
    10:54:16.598 - INFO    : Registered DAV providers by route:
    10:54:16.598 - INFO    :   - '/:dir_browser': FilesystemProvider for path '/Users/martin/prj/git/wsgidav/wsgidav/dir_browser/htdocs' (Read-Only) (anonymous)
    10:54:16.599 - INFO    :   - '/': FilesystemProvider for path '/tmp' (Read-Write) (anonymous)
    10:54:16.599 - WARNING : Basic authentication is enabled: It is highly recommended to enable SSL.
    10:54:16.599 - WARNING : Share '/' will allow anonymous write access.
    10:54:16.813 - INFO    : Running WsgiDAV/4.0.0-a1 Cheroot/8.5.2 Python 3.9.1
    10:54:16.813 - INFO    : Serving on http://0.0.0.0:80 ...
    

    Run wsgidav --help for a list of available options.

  • The python-pam library is needed as extra requirement if pam-login authentication is used on Linux or OSX:

    $ pip install wsgidav[pam]
    $ wsgidav --host=0.0.0.0 --port=8080 --root=/tmp --auth=pam-login
    
  • Note: Windows users may prefer the MSI Installer (see Assets section), or use winget:

    > winget install wsgidav
  • WebDAV is a superset of HTTP, so WsgiDAV is also a performant, multi-threaded web server with SSL support.

  • WsgiDAV is also a Python library that implements the WSGI protocol and can be run behind any WSGI compliant web server.

  • WsgiDAV is implemented as a configurable stack of WSGI middleware applications.
    Its open architecture allows to extend the functionality and integrate WebDAV services into your project.
    Typical use cases are:

    • Expose data structures as virtual, editable file systems.
    • Allow online editing of MS Office documents.

Status

Latest Version See the (change log) for details.

Note: Release 4.0 introduces some refactorings and breaking changes.
See the (change log) for details.

More info

Credits

Contributors:

Any kind of feedback is very welcome!
Have fun :-)
Martin

wsgidav's People

Contributors

andkrau avatar caixiangyue avatar dependabot[bot] avatar djmattyg007 avatar dobesv avatar fngx avatar harobed avatar hr0109 avatar icarito avatar joecoder1021 avatar johaven avatar jonasbardino avatar jwodder avatar mar10 avatar milos-u avatar n3storm avatar newbieorange avatar noobieotsute avatar nsymms avatar ph0tonic avatar pokoli avatar pws21 avatar rootraider avatar samuelfekete avatar steffende avatar tobiaspatton-s4 avatar tomviner avatar tworec avatar uwohlfeil avatar yarikoptic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wsgidav's Issues

Windows Mini Redirector Loads and Caches Whole File

Hi,
I've written a custom provider for WsgiDAV which fetches the files from some data storage over network and I'm very happy with it. When I use WsgiDAV with almost any client (e.g. OSX Finder), I can see that only the parts requested by client is retrieved. However, if I mount the WsgiDAV resource with Windows native client, that is WebDAV Mini Redirector, and open for example a video file, the whole file is retrieved from the server and only then is handed over to the program, VLC if you may, which as one may argue is nice as seeking different parts of the video is done immediately (as it seems that Windows has cached the whole file), but has the downside of waiting an unbearable amount of time for large files to be loaded. I was wondering if this problem is indeed caused by MiniRedir, and if there is any other solution for Windows to mount WebDAV resource without this limitation?

Improve logging configuration

It should be possible to make use of the extended configuration possibilities that the Python logging module offers.
A solution should consider

  • there are still print statements in the code, but also logging loggers are used
  • integrate with the current debug options (verbose, enable_loggers, debug_methods, debug_litmus, ...) and the WsgiDavDebugFilter middleware
  • make standard configuration options available, for example rotating log files
  • a WSGI application that embeds WsgiDAV should be able to include WsgiDAV output into its own logging setup

Custom responses for PUTs etc.

We need the ability to customise the response status and body for PUT requests (to support the s3 protocol on the client side).

We will probably also need this for GET and POST as mentioned here, so I think it would be good to think of a consistent way of doing this for all methods, and provide plugins with the ability to specify the status and body to return.

Windows file manager and OSX Finder fails on file names with comma

One of our users wanted to upload a bunch of files with several exotic characters in file names. We managed to change our own code base to support most except ones with commas.
Apparently the native Microsoft Win 7 WebDAV client does not URL-encode paths passed in the
HTTP_AUTHORIZATION header and that causes troubles with the headerparser regex in http_authenticator.py.

An example HTTP_AUTHORIZATION value would be

'Digest
username="bardino@...",realm="/",nonce="MTQ...",uri="/test-enc/a,b.txt",cnonce="647...",nc=00000001,algorithm=MD5,response="af...",qop="auth"'

The regexp headerparser thus splits after the comma in /test-enc/a,b.txt causing auth errors like:

<140650507945728> [10:53:59.701] wsgidav.http_authenticator:
computeDigestResponse('/', 'bardino@...', ...):
4177dc6ee07fcaa2455855132cdeb7f8 != af0259a96cfd680c478b875e4b44e4f1

Testing the same with the cadaver client works and debug show that the paths get urlencoded there.

I'll see if I can provide a server side hotfix to address it.

print messages on console not configured through logger

First of all thanks for keep alive this very interesting project.
I configured WsgiDAV behind the Apache Webserver, but I found that even if I set log verbosity to 0 (only errors) I experienced these logs in the Apache log file:

[Fri Mar 07 10:32:36 2014] [error] OT powerpoint
[Fri Mar 07 10:32:36 2014] [error] OT {'ms_sharepoint_urls': False, 'enable': True, 'response_trailer': '', 'davmount': False, 'ms_sharepoint_plugin': True, 'ms_mount': False}
[Fri Mar 07 10:32:36 2014] [error] OT excel
[Fri Mar 07 10:32:36 2014] [error] OT {'ms_sharepoint_urls': False, 'enable': True, 'response_trailer': '', 'davmount': False, 'ms_sharepoint_plugin': True, 'ms_mount': False}
[Fri Mar 07 10:32:36 2014] [error] OT word

I found there are some "print" in dir_browser.py file (lines 271-272):

print "OT", officeType
print "OT", dirConfig

Maybe these lines should be logged in the standard way.

Thanks,

ROb

PATH_INFO should not be unquoted

I was attempting to use wsgidav-1.1.0 and apache-2.2 with mod_dav to access files. I ran into an issue when manipulating file names containing percent characters. I tracked this down to a single statement at wsgidav_app.py:243 which is:

        # We unquote PATH_INFO here, although this should already be done by
        # the server.
        path = urllib.unquote(environ["PATH_INFO"])

According to PEP-333, the PATH_INFO environment is already unquoted and this extra unquote causes errors on paths with actual percent characters in them.

Exception in destructor

I've bumped into this exception:
Exception TypeError: "'NoneType' object is not callable" in ignored
Maybe the problem is that destructor of PropertyManager instance calls global function(currentThread) which has been already destroyed by garbage collector(wsgidav/rw_lock.py line 128)?

SLE-11 GVFS-DAV behavior

There is currently a work-around behavior for PUT requests with some MS Windows clients. I have found that older versions of GVFS used in SUSE Linux 11 has the same behavior, file copies are an empty PUT to create followed by a PUT with actual file content.

WsgiDAVApp does not allow upstream authentication

This is not too big of a deal for me, but I am using WsgiDAVAPP behind Apache HTTP authentication / authorization layer. Currently, there is no way to interrupt the WsgiDAVApp stack to avoid or replace use of HTTPAuthenticator object. This makes it impossible to substitute value for environ['http_authenticator.username'] as HTTPAuthenticator sets it to the empty string if not used.

It would be convenient to have a way to control use of HTTPAuthenticator similar to how the config option "dir_browser" controls use of WsgiDavDirBrowser.

(minor) Don't default to cheroot if it is not installed by default

Steps to reproduce

mkvirtualenv wsgidav-py3 -ppython3 
pip install -e git+ssh://[email protected]:mar10/wsgidav.git#egg=wsgidav
wsgidav --host=0.0.0.0 --port=8088 --root=/some/folder -v

Expected Result

Server should serve the content of /some/folder

Actual Result

Running without configuration file.
<140526985504512> [10:00:26.17] wsgidav:  Default encoding: ascii (file system: UTF-8)
Middleware <class 'wsgidav.dir_browser.WsgiDavDirBrowser'> is suitable
Middleware <class 'wsgidav.http_authenticator.HTTPAuthenticator'> is suitable
Middleware <class 'wsgidav.error_printer.ErrorPrinter'> is suitable
Middleware <class 'wsgidav.debug_filter.WsgiDavDebugFilter'> is suitable
Using lock manager: LockManager(LockStorageDict)
Using property manager: None
Using domain controller: WsgiDAVDomainController
Registered DAV providers:
  Share '/': FilesystemProvider for path '/some/folder' (Read-Write) (anonymous)
WARNING: share '/' will allow anonymous access.
******************************************************************************
ERROR: Could not import Cheroot.
Try `pip install cheroot` or specify another server using the --server option.
******************************************************************************
Traceback (most recent call last):
  File "[...]/.local/bin/wsgidav", line 9, in <module>
    load_entry_point('WsgiDAV', 'console_scripts', 'wsgidav')()
  File "/tmp/src/wsgidav/wsgidav/server/run_server.py", line 591, in run
    handler(app, config, server)
  File "/tmp/src/wsgidav/wsgidav/server/run_server.py", line 435, in _runCheroot
    from cheroot import server, wsgi
ImportError: No module named cheroot

While the error message is helpful and adding --server=ext-wsgiutils to the command line fixes the problem, I still think wsgidav should either fall back to the internal server when there are no other options available. Alternatively, the README could be fixed to include the necessary --server setting.

HTTP Range outputs wrong responses

Current design does not allow returning resource content starting from a particular Range offset.

This makes it impossible to use it as a layer over HTTP resources (think Dropbox, Drive, any other cloud storage), because the library wants a "file-like" object to be returned from the getContent() method.
A "file-like" object unfortunately is not seekable when it comes to a network stream, and this little detail is not documented anywhere.

Unfortunately it then uses that to call "seek()" on it when it needs a particular range requested from the client, which results in two possible scenarios, both bad:

  1. If we return "True" for "supportRanges", server crashes because seeking is not possible in a network stream.
  2. If we return "False" (default), then the client always receives data starting at the beginning of the file!!! This results in later beautiful exceptions like "The remote client closed the connection" and things like that, because the client is not receiving correct data.
    Actually, the server enters a never-ending loop of exceptions right after a client closes a connection unexpectedly for the first time.

Is there a way to make this work? e.g. add range start/end parameter to the getContent method? so the resource will return a fresh, correct, raw stream handle that will read data from the specified position?

Windows Explorer MOVE with non a-z characters

In Windows Explorer, moving a directory from "New Folder" to "test&" fails.

Working:

  1. Right click in WebDAV share. New -> Folder
  2. Name the folder "abcdefg".
  3. Right click on folder "abcdefg" then rename the folder to "test&" (performs WebDAV MOVE)
  4. Success

Not Working:

  1. Right click in WebDAV share. New -> Folder
  2. Name the folder "abc def". (performs WebDAV MOVE)
  3. Failure

Not Working:

  1. Right click in WebDAV share. New -> Folder
  2. Name the folder "abcdef".
  3. Rename "abcdef" to "abc def"
  4. Rename "abc def" to "hello world" (performs WebDAV MOVE)
  5. Failure

We patched this issue in smartfile@17df81a but we aren't sure if there is a better solution. The patch applies to https://github.com/mar10/wsgidav/blob/master/wsgidav/request_server.py#L833

CalDAV-support

This issue is currently deferred

Due to low priority and current lack of resources, this issue was tentatively put aside.
See also info about deferred issues.

as mentioned on the old google-code-ticket, CalDav-Support would be great (even though it's probably something that won't really happen)

**What new or enhanced feature are you proposing?**
Extending wsgidav with caldav

**What goal would this enhancement help you achieve?**
Allowing enhanced calendar support for a collaborative site i'm working on

Digest authentication rejected due to invalid header

The header returned by the server when the Digest protocol is used is not formatted correctly.
VLC rejects the authentication header due to invalid algorithm "MD5", when it should be MD5.
Please see RFC2617

Note that I first thought this is a VLC issue, until they proved me wrong in reading the spec. The algorithm name should not be quoted. Actual quotes are escaped in the spec as <">.

Current format of authentication header:
Digest realm="/", nonce="some_nonce", algorithm="MD5", qop="auth"

Should be:
Digest realm="/", nonce="some_nonce", algorithm=MD5, qop="auth"

Drop support for Python 3.3

Build breaks on travis (Python 3.3, see #87 )
(End of live September 29, 2017)

Pytest doesn't support this either anymore

Expected behavior on lost connections

Hi, I made some tests with wsgidav and I found that when a client uploads a file and closes the connection in the middle of the transmission wsgidav still returns 201 or 204 status code and it actually writes the transmitted contents leaving the file in an unconsistent state:

<140221162948416> [11:48:16.192] wsgidav:  input.read(1012) returned 0 bytes
<140221162948416> [11:48:16.192] wsgidav:  Input stream not completely consumed: closing connection
<140221162948416> [11:48:16.192] wsgidav:  Adding 'Connection: close' header
<140221162948416>  - john - [2014-02-28 10:48:16] "PUT /1234/hello_world.txt" length=1024, elap=0.038sec -> 201 Created

Is this the expected behavior in this case? Why not create a temporary file and check if the Content-length equals to the uploaded file size before create or modify any resource?

Thanks in advance!

wsgidav and CherryPy installed from PyPI: ImportError: cannot import name wsgiserver

Using Python 2.7.13, Void Linux x86_64. Installed all of wsgidav, lxml and cherrypy using:

$ pip install --user wsgidav
$ pip install --user lxml
$ pip install --user cherrypy

A snippet from the output of pip freeze:

CherryPy==10.1.0
WsgiDAV==2.1.0
lxml==3.7.2

When I run wsgidav, I get the following error:

$ wsgidav --host=0.0.0.0 --port=80 --root=/tmp
WARNING: share '/' will allow anonymous access.
WARNING: Could not import lxml: using xml instead (slower).
         Consider installing lxml https://pypi.python.org/pypi/lxml.
******************************************************************************
ERROR: Could not import CherryPy.
Try `pip install cherrypy` or specify another server using the --server option.
******************************************************************************
Traceback (most recent call last):
  File "/home/miki/.local/bin/wsgidav", line 11, in <module>
    load_entry_point('WsgiDAV==2.1.0', 'console_scripts', 'wsgidav')()
  File "/home/miki/.local/lib/python2.7/site-packages/wsgidav/server/run_server.py", line 499, in run
    handler(app, config, server)
  File "/home/miki/.local/lib/python2.7/site-packages/wsgidav/server/run_server.py", line 352, in _runCherryPy
    from cherrypy import wsgiserver
ImportError: cannot import name wsgiserver

(Note the warning about lxml, which is installed locally, as are its system library dependencies. I can import it manually without problems.)

There is no cherrypy.wsgiserver package in my installation of cherrypy. From CherryPy's CHANGES.rst, I gather:

v9.0.0

  • #1481: Move functionality from cherrypy.wsgiserver to the cheroot 5.0 project.

Could you please support this new cheroot project, or provide an upper bound for the version of cherrypy still supported by wsgidav? Thanks in advance.

PEP8 coding style

Hi,

It's a good practise to follow PEP8 to follow a common coding style for python projects. Currently I see that there is a lot of issues with this project (I suppose due to legacy code):

(python3)sergi@portatil ~/wsgidav » flake8 | wc -l
2318

As it will be a huge patch to fix them all, I want to discuss it first. We have to define the following things:

  • Would you like to follow this convention?
  • Do you think we need to add some exceptions?
  • How we manage to fix all the pyflakes issues? A development branch, or PR directly to master?
  • Once fixed, do you want to add a tox test to ensure the style is followed by all of the contributors?

If we reach an agreement, it must be explained on the CONTRIBUTING file, so new comers can follow this guides also.

Thanks in advance,

Set last-modified time support?

Hi Martin

First of all sorry if this should not go under issues, as it may be more a question or feature request than a bug/issue.

One of our users tried out rsync -a localdir mntdir against an OSX Finder-mounted wsgidav volume and it actually mostly works, except the file time stamps are not transferred. Thus repeating the rsync call does not skip the already transferred files as intended. I repeated the test with fusedav from Linux and got similar results. Calling a simple touch on a file in the mount also gets rejected with "Operation not supported".
As far as I can tell from my tracing this is due to wsgidav refusing PROPPATCH calls with the {DAV:}getlastmodified argument. The relevant doc string in setPropertyValue of dav_provider correctly states that any {DAV:} property modification requests result in a HTTP_FORBIDDEN reply and that resource providers may override the method to "update supported custom live properties".

I did a quick test and added such set {DAV:}getlastmodified support directly in (fs_)dav_handler and then touch, rsync -t and rsync -a all work as expected. However, I'm not really sure if it belongs there or if it is even compliant with the WebDAV spec:
http://tools.ietf.org/html/rfc4918#section-15.7

It would be quite cool with rsync support for our purpose, but maybe I'm overlooking some problems it might introduce? Would it make sense to properly add such general set modification time support and maybe only enable it with a conf setting? I think I can give it a shot and send a pull request if so.
Or is that something you consider in the custom live properties category and better left for a derived fs resource provider?

Cheers, Jonas

Fail to run WebDAV server

Fatal Python error: Py_Initialize: unable to load the file system codec
ImportError: No module named 'encodings'
Current thread 0x00002e68 (most recent call first):

ETags for collections?

I have implemented get_Etag() for a collection, but it never gets called. It appears that the directory browser ignores Etags. Is it possible to have Etags for collections?

Inversion of control for a streaming upload provider

I need a way to proxy streaming uploads, that is to forward bytes from the client as soon as they are received, without temp files. To do that, I need to have a file-like object given to the provider for reading - as opposed to the provider giving the request server a file-like object for writing.

I am thinking something along the lines of checking for a flag in doPUT, and if the provider prefers, just give it the reference to environ["wsgi.input"] for reading.

Any thoughts?
@mar10 @tomviner

OSX Finder hang on copy and server DoS

We're working on replacing pywebdav with wsgidav as the provider of our WebDAV(S) interface to user files on the Minimum intrusion Grid (www.migrid.org). It looks very promising so far, with much better performance, protocol compliance and native Windows 7+ support.
One draw-back, however, is an issue with native Mac OSX support. The OSX file manager, Finder, connects and operates alright, but when copying a file from one DAV directory to a sub-directory, it hangs and leaves the server spinning at 100% CPU - effectively slowing it to a near DoS state.

We run our own instance behind SSL with some customization, but we also tested with the wsgidav server in a very basic configuration and the problem remains:
wsgidav --host=0.0.0.0 --port=8021 --root=./davshare --debug > debug-copy-hang.log 2>&1
Apparently I can't attach the resulting log here but it's available at:
http://www.migrid.org/debug-copy-hang.log
Similar log for the same (working) operation from windows 8 file manager and Linux with Thunar/gvfs are available at:
http://www.migrid.org/debug-copy-win8-okay.log
http://www.migrid.org/debug-copy-gvfs-okay.log

I'm not a DAV expert, but it looks strange to me that Finder locks both the qwe.txt and ._qwe.txt and then only unlocks the latter (near the end of the log). It might be a Finder bug, but it should probably not leave the server spinning then?
Windows locks and unlocks only once and gvfs does not lock at all, so it makes sense that it works without problems there.

I don't have a Mac myself, but I can ask a colleague to help with further testing, if you can suggest additional things to try in order to track down and solve the problem?

can not open excel file

I am not sure if this is a wsgidav issue or an MS Excel issue:
wsgidav runs on ubuntu 14 - password protected share
connecting with windows 7 - all works fine -xxxxxx is the user name
creating and opening word, powerpoint files on the webdav share all works fine
trying to open an excel file doesn't work as the filename seems to get corrupted

in the log bot excel and word are newly created files, but also with existing files (length>0) the effect is the same
opening word - this works - even though i am curios about the /foo part - looks more like linux and not something ms would use

xxxxxxx- [] "PROPFIND /WORD DOCUMENT.DOC" length=0, depth=0, elap=0.001sec -> 404 Not Found
(anon) - [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.003sec -> 207 Multi-Status
(anon) - [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "PROPFIND /" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "PROPFIND /foo" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /foo" length=0, depth=0, elap=0.002sec -> 404 Not Found
(anon) - [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "LOCK /Word Document.doc" length=195, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "LOCK /Word Document.doc" length=195, depth=infinity, elap=0.002sec -> 200 OK
(anon) - [] "GET /Word Document.doc" elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "GET /Word Document.doc" depth=0, elap=0.001sec -> 304 Not Modified
(anon) - [] "PROPFIND /~$w Microsoft Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /~$w Microsoft Word Document.doc" length=0, depth=0, elap=0.002sec -> 404 Not Found
(anon) - [] "PROPFIND /~$w Microsoft Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized

this is excel - the .xls in the last line is truncated

xxxxxxx- [] "PROPFIND /WORD DOCUMENT.DOC" length=0, depth=0, elap=0.001sec -> 404 Not Found
(anon) - [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Word Document.doc" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "PROPFIND /Excel Worksheet.xls" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Excel Worksheet.xls" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "GET /Excel Worksheet.xls" elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "GET /Excel Worksheet.xls" depth=0, elap=0.001sec -> 304 Not Modified
(anon) - [] "PROPFIND /Excel Worksheet.xls" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Excel Worksheet.xls" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "PROPFIND /Excel Worksheet.xls" length=0, depth=0, elap=0.000sec -> 401 Not Authorized
xxxxxxx- [] "PROPFIND /Excel Worksheet.xls" length=0, depth=0, elap=0.002sec -> 207 Multi-Status
(anon) - [] "PROPFIND /Excel Worksheet.xl" length=0, depth=0, elap=0.000sec -> 401 Not Authorized

also is it normal that there are always those anonymous connects even though a password is set?

Response headers issue in virtual fs provider

Hi,
I'm trying to setup a WebDAV provider for a custom 'Virtual Filesystem' which serves files indexed in an SQlite database. It's working quite well except for the following: when requesting a file's content, the server returns some HTTP headers in the beginning of the file instead of returning them as proper headers.

If I request the file doing curl -v http://localhost/hello.jpg > hello.jpg I get

*   Trying 0.0.0.0...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to 0.0.0.0 (127.0.0.1) port 8080 (#0)
> GET /hello.jpg HTTP/1.1
> Host: 0.0.0.0:8080
> User-Agent: curl/7.43.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: WsgiDAV/1.2.0 BaseHTTP/0.3 Python/2.7.10
< Date: Tue, 14 Jul 2015 03:58:58 GMT
< Content-Length: 91845
< Content-Type: image/jpeg
< 
{ [8231 bytes data]
100 91845  100 91845    0     0  3949k      0 --:--:-- --:--:-- --:--:-- 4076k
* Connection #0 to host 0.0.0.0 left intact

and then the retrieved 'hello.jpg' file contains

Date: Tue, 14 Jul 2015 04:13:09 GMT
Server: WsgiDAV/1.2.0 CherryPy/3.2.4 Python/2.7.10

[JPG content...]

My provider implementation of a resource is very basic:

class VFSResource(DAVNonCollection):
    def __init__(self, path, environ):
        DAVNonCollection.__init__(self, path, environ)
        self.vfs = self.provider.vfs
        self.node, ign = self.vfs.read(path) # this returns a 'node object'
    def getContent(self):
        return StringIO(self.node.data) # node.data is a buffer with file contents
    def getContentLength(self):
        return len(self.node.data)
    def getContentType(self):
        return self.vfs.magic(self.path) # this returns a mime type string
    def getDisplayName(self):
        return self.vfs._basename(self.path)
    def getDisplayInfo(self):
        return {"type": "Resource"}

class VFSCollection(DAVCollection):
    def __init__(self, path, environ):
        DAVCollection.__init__(self, path, environ)
        self.vfs = self.provider.vfs
        self.lsinfo = self.vfs.list(path)
    def getMemberNames(self):
        return self.lsinfo.keys()
    def getMember(self, name):
        node,ign = self.vfs.read(joinUri(self.path, name))
        if node.type == VFS.NODE_FILE:
            return VFSResource(joinUri(self.path, name), self.environ)
        return VFSCollection(joinUri(self.path, name), self.environ)
    def getDisplayInfo(self):
        return {"type": "Collection"}

class VFSProvider(DAVProvider):
    def __init__(self, options):
        super(VFSProvider, self).__init__()
        self.vfs = VFS(os.path.join(os.getcwd(),'fs'))
        self.environ = {}
        self.environ["wsgidav.provider"]=self
    def getResourceInst(self, path, environ):
        self._count_getResourceInst += 1
        try:
            node, i = self.vfs.read(path)
            if node.type == VFS.NODE_FILE:
                return VFSResource(path,self.environ)
            return VFSCollection(path,self.environ)
        except PathNotFoundError:
            pass
        return None
    def getDisplayInfo(self):
        return {"type": "Provider"}

And the config I'm using looks like

provider_mapping = {}
user_mapping = {}
def addShare(shareName, davProvider):
    provider_mapping[shareName] = davProvider
def addUser(realmName, user, password, description, roles=[]):
    realmName = "/" + realmName.strip(r"\/")
    userDict = user_mapping.setdefault(realmName, {}).setdefault(user, {})
    userDict["password"] = password
    userDict["description"] = description
    userDict["roles"] = roles
host  = "0.0.0.0"
port = 8080
debug_methods = []
debug_litmus = []
dir_browser = {
    "enable": True,          # Render HTML listing for GET requests on collections
    "response_trailer": "",  # Raw HTML code, appended as footer
    "davmount": True,        # Send <dm:mount> response if request URL contains '?davmount'
    "ms_mount": True,        # Add an 'open as webfolder' link (requires Windows)
    "ms_sharepoint_plugin": True, # Invoke MS Offce documents for editing using WebDAV
    "ms_sharepoint_urls": False,  # Prepend 'ms-word:ofe|u|' to URL for MS Offce documents
}
propsmanager = True

import vfs_dav_provider
addShare("", vfs_dav_provider.VFSProvider(None))

acceptbasic = True    # Allow basic authentication, True or False
acceptdigest = True   # Allow digest authentication, True or False
defaultdigest = True  # True (default digest) or False (default basic)

I'd very much appreciate any help on what I'm doing wrong...

Thanks,
Mauro

Specifying mount_path doesn't seem to work

Hi,

I've been trying to make wsgidav available under /dav in a Pyramid application. I have specified "/dav" as "mount_path" in the config and "/" as "provider_mapping", since this is a root share. I used your Pylons example as a starting point for my Pyramid config.

This configuration doesn't work, and WsgiDAV throws a 404. I've been trying to debug this, and it seems like the mount_path part of the request isn't removed when the resource is looked up. This causes wsgidav to think that all resources I'm trying to access reside inside a folder in the root named "dav".

Oddly, if I change my configuration so that mount_path is "" and provider_mapping is "/dav", it seems to work fine.

In _sendResource inside request_server.py, I see the following line:
path = environ["PATH_INFO"]

When I access a resource at http:///dav/resourcename, the path is set to "/dav/resourcename". Of course, since I don't have a folder named "dav" in my root share, the 404 error is thrown.

Is this a configuration issue on my part, or hasn't the mount_path been implemented fully yet?

pip install wsgidav error

CentOS release 6.7 x86_64
python2.7
WsgiDAV-2.0.1 install error:

sh: /usr/bin/ldd: /usr/bin/bash: bad interpreter: 没有那个文件或目录
copying doc/logo.ico -> build/exe.linux-x86_64-2.7/logo.ico
error: [Errno 2] No such file or directory: 'doc/logo.ico'

please fix it,thanks

lock_manager.py - returns timeout negative seconds

I noticed this while troubleshooting LOCK issues with wsgidav.
apparently once the LOCK is requested, in the returned xml, Seconds has apparently negative value visible in the real response below (-1488454203)

request:
<?xml version="1.0" encoding="utf-8" ?>.<D:lockinfo xmlns:D="DAV:">.<D:lockscope><D:exclusive/></D:lockscope>.<D:locktype><D:write/></D:locktype>.<D:owner>.<D:href>mailto:[email protected]</D:href>.</D:owner>.</D:lockinfo>

response:
<?xml version='1.0' encoding='UTF-8'?>.<ns0:prop xmlns:ns0="DAV:"><ns0:lockdiscovery><ns0:activelock><ns0:locktype><ns0:write /></ns0:locktype><ns0:lockscope><ns0:exclusive /></ns0:lockscope><ns0:depth>infinity</ns0:depth><ns0:owner>.<ns0:href>mailto:[email protected]</ns0:href>.</ns0:owner><ns0:timeout>Second--1488454203</ns0:timeout><ns0:locktoken><ns0:href>opaquelocktoken:0xxxxxxxxxxxxxxxxxxxxxxxxxx</ns0:href></ns0:locktoken><ns0:lockroot><ns0:href>/path/file</ns0:href></ns0:lockroot></ns0:activelock></ns0:lockdiscovery></ns0:prop>

the values should be either positive or -1 which then prevent the client from successfully completing the lock

further more, this number is not corresponding to the max timeout defined value either, which for a lock should be 1week:

LOCK_TIME_OUT_DEFAULT = 604800 # 1 week, in seconds

Custom response headers

Is it possible to set custom response headers? In particular I would like to set the Access-Control-Allow-Origin header. It looks like it should be something easily configurable, but I can't find how to do it.

Reverse-proxy authentication

Hello !
Thanks for this grate project. And I have one request for you.
I need to authenticate my users against LDAP. As wsgidav does not have this middleware, I use my nginx configuration to do it before proxying to wsgidav. In short :
user ---> nginx (with ldap authentication) ---> uwsgi running wsgidav
Nginx filters well the users, but wsgidav does not know about them, even if the REMOTE_USER variable is passed.
As I would like to restrict certain folders to a few users, wsgidav needs to know them, at least their username, which is passed with REMOTE_USER. And I do not want to either have a second authentication inside wsgidav nor can i remove the ldap authentication from nginx and enter manually each user/password in the wsgidav configuration.
I think #11 was talking about it, but I seek in the configuration and it seems that scenario is not possible yet.
Adding this possibility really would be great.

Add environ arg to _locToFilePath for per-user chrooting?

Hi Martin

We run a setup with a custom FilesystemProvider where we want to limit users to their individual home sub-directory inside rootFolderPath. I thought we'd already managed that with the existing wsgidav code, but it turns out we ran into a nasty race when trying to override the lookup function dynamically.

Would it be acceptable for you to change the _locToFilePath method to take not only the path but additionally an environ argument (in symmetry with getResourceInst)?
In that way we only need to override that method once and for all to use the username auth header for the chroot-mapping.

It requires a few call changes throughout fs_dav_provider.py but I can provide a pull request if that's any help?

Cheers, Jonas

Status codes and apache mod_wsgi

Currently, the wsgidav module does not define full status messages for all status codes. Unfortunately, the Apache-2 mod_wsgi module fails request processing if the WSGI application does not return both a status code and a status text. A simple patch is to always include something for status text to satisfy mod_wsgi.

Question: how to have per user folders

similar to FTP, each user gets own private folder

when user tester logins, I can share a folder "/davshare/tester" with the user for esample

is there a way to configure wsgidav to do this?

Failure to LOCK with cadaver

Hi,
I ran into an issue with wsgidav and trying to lock files and collections from cadaver 0.23.3

$ cadaver --version
cadaver 0.23.3
neon 0.30.2: Library build, IPv6, Expat 2.1.0, zlib 1.2.8, OpenSSL 1.0.2j  26 Sep 2016.
readline 7.0

This is the backtrace:

<140735281930240> 127.0.0.1 - (anonymous) - [2016-12-31 21:12:54] "PROPFIND /test-lock" length=288, depth=0, elap=0.002sec -> 207 Multi-Status
127.0.0.1 - - [31/Dec/2016 13:12:54] "PROPFIND /test-lock HTTP/1.1" 207 586
Error parsing XML string. If lxml is not available, and unicode is involved, then installing lxml _may_ solve this issue.
XML source: 
<140735281930240> [13:12:54.387] wsgidav:  ErrorPrinter: caught Exception
Traceback (most recent call last):
  File "/private/tmp/venv-wsgidav/lib/python2.7/site-packages/wsgidav/error_printer.py", line 55, in __call__
    for v in app_iter:
  File "/private/tmp/venv-wsgidav/lib/python2.7/site-packages/wsgidav/request_resolver.py", line 206, in __call__
    for v in app_iter:
  File "/private/tmp/venv-wsgidav/lib/python2.7/site-packages/wsgidav/request_server.py", line 127, in __call__
    app_iter = provider.customRequestHandler(environ, start_response, method)
  File "/private/tmp/venv-wsgidav/lib/python2.7/site-packages/wsgidav/dav_provider.py", line 1471, in customRequestHandler
    return defaultHandler(environ, start_response)
  File "/private/tmp/venv-wsgidav/lib/python2.7/site-packages/wsgidav/request_server.py", line 1230, in doLOCK
    lockdiscoveryEL = res.getPropertyValue("{DAV:}lockdiscovery")
  File "/private/tmp/venv-wsgidav/lib/python2.7/site-packages/wsgidav/dav_provider.py", line 617, in getPropertyValue
    ownerEL = xml_tools.stringToXML(lock["owner"])
  File "/private/tmp/venv-wsgidav/lib/python2.7/site-packages/wsgidav/xml_tools.py", line 51, in stringToXML
    return etree.XML(text)
  File "/private/tmp/venv-wsgidav/lib/python2.7/site-packages/defusedxml/common.py", line 160, in fromstring
    return parser.close()
  File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py", line 1665, in close
    self._raiseerror(v)
  File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py", line 1517, in _raiseerror
    raise err
ParseError: no element found: line 1, column 0

How to reproduce:

tmp $ virtualenv --python python2.7 venv-wsgidav
tmp $ venv-wsgidav/bin/pip install wsgidav
tmp $ venv-wsgidav/bin/wsgidav -r /tmp/ --server wsgiref
venv-wsgidav $ cadaver http://localhost:8080
dav:/> mkdir test-lock
Creating `test-lock': succeeded.
dav:/> lock test-lock
Locking collection `test-lock': failed:
Could not read status line: connection was closed by server
dav:/> 

Locking a file yields a similar backtrace. AFAICT the problem is the empty string at lock["owner"] which is set in doLOCK but getPropertyValue expects owner to be an XML string instead.

Backport seafdav patches

Incorrect response when request has header HTTP_AUTHORIZATION: Bearer

When opening a file MS-Word, begins by requesting OPTIONS with HTTP_AUTHORIZATION: Bearer header. The http_authenticator middleware returns 400 Bad Request, which is not correctly handled by word.

If the value of the HTTP_AUTHORIZATION header in the request is not supported by the WebDAV server, the correct response is 401 Not Authorized.

removeProperty called twice with dryRun=False

In wsgidav/dav_provider.py line 761:
https://github.com/mar10/wsgidav/blob/master/wsgidav/dav_provider.py?utf8=%E2%9C%93#L761

removeProperty is not being passed dryRun argument which means that because setPropertyValue is called twice (once with dryRun=True and once with dryRun=False) it will be called twice with effectively dryRun=False which means a property will be deleted even if some other property update instruction fails so atomicity of PROPPATCH is broken.

The fix would be to just pass dryRun:

return pm.removeProperty(refUrl, propname, dryRun)

Pull request: #77

Failed on processing non-iso-8859-1 characters on Python 3

Hi,
I pulled the code directly from the repo, but when I tried to add addShare("测试", r"~/") into the config file, I encountered several problems.

  • On Python 2:
    run_server.py will immediately complain that
SyntaxError: Non-ASCII character '\xe6' in file /testdir/wsgidav.conf on line 107, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

This can be solved by adding

#encoding=utf8

at the very beginning of wsgidav.conf. After that, everything works fine.

  • On Python 3:
    The server will start normally, however, when typing
http://127.0.0.1:8080/%E6%B5%8B%E8%AF%95/

in the browser, the console will give

"GET /��/" elap=0.000sec -> 404 Not Found

and 404, which I cannot solve though :(

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.