Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A couple of baseurl-related fixes. #19

Merged
merged 4 commits into from
Dec 30, 2022

Conversation

jaysonlarose
Copy link
Contributor

Fixed bytes/str type mismatch when using explicit baseurl.
Added baseurl directive to example config.ini.

Copy link
Owner

@andrewleech andrewleech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect a lot of these changes will break python2 compatibility (which I was originally maintaing with six) but to be honest I'm past caring about py2 these days.

@@ -341,7 +340,7 @@ def do_PROPFIND(self):
return self.send_status(400)

try:
DATA = b'%s\n' % pf.createResponse()
DATA = pf.createResponse()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure the newline isn't still needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

I'd say it's desirable, and I didn't intend to exclude it... but it didn't break the clients I was using to test (cadaver, davfs2, and GLib gio/gvfs).

Having said that, I'd like to move where that newline's getting generated a bit further down into the implementation, as it feels like it's part of a complete well-formed response. I'm thinking at the end of the return doc.toxml(encoding="utf-8") clauses on lines 119 and 183 of propfind.py. Does that sound reasonable?

(Also, I'm pretty sure the DATA = '%s\n' % rp.createResponse() line just below here in the do_REPORT() method has the same fundamental issue, I just didn't notice it before. I'll make a fix for that, as soon as I figure out how to get a WebDAV client to elicit that response from the server.)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what you mean, this doesn't really look like the right place for it. I think http generally ignores extra newlines anyway so doesn't entirely surprise me the clients still work. Consolidating the place that adds them is a good idea though.

_randGen = random.Random(time.time())
return '%s-%s-00105A989226:%.03f' % \
(_randGen.random(),_randGen.random(),time.time())
return str(uuid.uuid4())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice cleanup

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I checked the output from Apache's DAV module and saw that it was returning a UUID, for the lock token, and the example on The top Google hit for "webdav lock token" did the same... since the uuid module is already a standard Python module, I figured it was a win-win.

@@ -59,6 +59,9 @@ mimecheck = 1
# webdav level (1 = webdav level 2)
lockemulation = 1

# dav server base url
baseurl =
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to leave this empty config line here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Without that directive in the config file, davserver blows up with a configparser.NoOptionError:

[09:22] jayson@skully$ davserver -c wut_it_be/config.ini 
2019-12-03 09:22:59,150 INFO Starting up PyWebDAV server (version 0.9.14)
2019-12-03 09:22:59,151 INFO chunked_http_response feature ON
2019-12-03 09:22:59,151 INFO http_request_use_iterator feature OFF
2019-12-03 09:22:59,151 INFO http_response_use_iterator feature ON
2019-12-03 09:22:59,151 INFO Initialized with /home/jayson/wut_it_be http://localhost:8081/
2019-12-03 09:22:59,151 WARNING Authentication disabled!
2019-12-03 09:22:59,151 INFO Serving data from /home/jayson/wut_it_be
Traceback (most recent call last):
  File "/usr/lib/python3.7/configparser.py", line 788, in get
    value = d[option]
  File "/usr/lib/python3.7/collections/__init__.py", line 914, in __getitem__
    return self.__missing__(key)            # support subclasses that define __missing__
  File "/usr/lib/python3.7/collections/__init__.py", line 906, in __missing__
    raise KeyError(key)
KeyError: 'baseurl'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/davserver", line 11, in <module>
    load_entry_point('PyWebDAV3==0.9.14', 'console_scripts', 'davserver')()
  File "/usr/local/lib/python3.7/dist-packages/PyWebDAV3-0.9.14-py3.7.egg/pywebdav/server/server.py", line 376, in run
    handler=handler)
  File "/usr/local/lib/python3.7/dist-packages/PyWebDAV3-0.9.14-py3.7.egg/pywebdav/server/server.py", line 94, in runserver
    if handler._config.DAV.baseurl:
  File "/usr/local/lib/python3.7/dist-packages/PyWebDAV3-0.9.14-py3.7.egg/pywebdav/lib/INI_Parse.py", line 34, in __getattr__
    return self.__parser.get(self.name, name)
  File "/usr/lib/python3.7/configparser.py", line 791, in get
    raise NoOptionError(option, section)
configparser.NoOptionError: No option 'baseurl' in section: 'DAV'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that's why this is PyWebDAV3 :)

To be honest, I didn't even think about python 2 compatibility when I submitted this pull request. I figured the references to six might have been part of the old PyWebDAV codebase (which I promptly set a figurative match to as soon as I saw that this library existed and I didn't have to fix the whole thing).

But seriously, I feel you... I'm looking forward to saying goodbye to all the insanity that comes with maintaining Python2 compatibility (and maintaining Python2 text objects).

According to https://pythonclock.org/, only about 28 more days to go! Which means if Python 2 were a cop in a movie, now's about the time it gets tragically gunned down in the line of duty...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a heads-up: it looks like there's a destructive bug in do_MOVE.

I tried to move a populated directory into another directory, and wound up with an empty source directory inside the destination.

Call me crazy, but I think it's related to that blurb in davcmd.movetree() that goes "PROBLEM: if something did not copy then we have a problem when deleting as the original might get deleted!" I'm looking into it.

@andrewleech
Copy link
Owner

When I first made this port I was stuck with py2 on my work project, so cared about back compatibility. Those days are long gone however so I'm not fussed.

Thanks for the fix up work, there's always been lingering str/bytes issues here. I'm not actually using this project myself so haven't found the time to systematically clean it up myself. I actually only ported this server in the first place because it was used in the test suite of a webdav client I was porting to py3, so I did this too just to run the tests!

Irrespective of that though I'm happy to take contributions and make releases, they're pretty much automated anyway.

@jaysonlarose
Copy link
Contributor Author

Yeah, I hadn't touched any WebDAV stuff myself in a couple of years, and then I had to get some files off my phone. And, for whatever reason, WebDAV somehow became the lowest common denominator as the network-filesystem-like-thing that iOS apps use.

@jaysonlarose
Copy link
Contributor Author

I've fixed the "move command fails destructively" issue, finished the XML newline cleanup, and made a few other minor tweaks. I think everything's in a state that it's OK to merge in, if you want to take another look.

--Jays

pywebdav/lib/utils.py Outdated Show resolved Hide resolved
@jaysonlarose
Copy link
Contributor Author

I haven't done any great amount of testing, but trying to move a collection to itself should no longer be destructive.

@andrewleech
Copy link
Owner

Great, @mik2k2 would you be able to test this? If you're both happy is be happy to merge this into a new release.

@@ -211,7 +209,7 @@ def asXML(self, namespace='d', discover=False):
owner_str = ''
if isinstance(self.owner, str):
owner_str = self.owner
elif isinstance(self.owner, xml.dom.minicompat.NodeList):
elif isinstance(self.owner, xml.dom.minicompat.NodeList) and len(self.owner) > 0:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be equivalent to just and self.owner, since NodeList inherits from list

return 1
else:
return None
""" returns 1 if uri1 is a prefix of uri2 """
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True

@mikitsu
Copy link

mikitsu commented Jul 13, 2020

In lib/WebDAVServer.py, do_PUT, line 588, headers['Location'] = uri needs to re-quote the URI in case it contains non-ASCII characters.

Added baseurl directive to example config.ini.
Changed "Ok" to "OK" in do_UNLOCK send_body response.
Changed erroneous string HTTP response codes to integer.
Changed the... strange thing... that generateToken outputs to a uuid.uuid4.
Added a length check to avoid an error in asXML.

fshandler.py:
Removed `.encode()`/`.decode()` stuff in relation to URIs; URIs should be of type `str`.

propfind.py:
Reverted the hacky `.encode` I added in my previous fix, removed the `.decode()` further down; URIs should be of type `str`.

WebDAVServer.py:
Removed a lot of conversions of URI to bytes; URIs should be of type `str`.
Removed a clause in `DAVRequestHandler.send_body()` that was converting headers to `bytes` before sending them. This was causing many headers to be sent across the line looking like `Connection: b'Keep-Alive'`.
Added a clause to `DAVRequestHandler.send_body()` to encode DATA to `bytes`; all XML generated is kept as `str` until just before it is to be sent over the line.

AuthServer.py:
Changed a bunch of `bytes` objects over to regular strings.
Added a bit at the end of `AuthRequestHandler.send_autherror()` to encode its HTML response to `bytes` just before it gets sent over the line.
  * reimplemented `copytree()` URI derivation logic using `urllib.parse`
    and `os.path`.
* WebDAVServer.py, propfind.py, davcopy.py, utils:
  * Moved the responsibility of adding newlines to the end of response
    XML out of WebDAVServer.py and into the XML generation routines in
    propfind.py, davcopy.py, and utils.py.
* utils.py:
  * Altered `is_prefix()` to use `os.path.commonpath()` instead of
    a substring comparison.
…2k2!)

* Fixed a bug where attempting to move a collection to itself would result in homicide.
@andrewleech
Copy link
Owner

Thanks, I'm going to merge as-is and fix comments separately!

@andrewleech andrewleech merged commit a810f9e into andrewleech:master Dec 30, 2022
@andrewleech
Copy link
Owner

Included in https://pypi.org/project/PyWebDAV3/0.10.0/

@andrewleech
Copy link
Owner

In lib/WebDAVServer.py, do_PUT, line 588, headers['Location'] = uri needs to re-quote the URI in case it contains non-ASCII characters.

I addressed this and a couple of your other comments here in follow up commits included in https://pypi.org/project/PyWebDAV3/0.10.0/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants