diff --git a/easywebdav/client.py b/easywebdav/client.py index 4003198..0e82a2e 100644 --- a/easywebdav/client.py +++ b/easywebdav/client.py @@ -1,20 +1,29 @@ +# coding: utf-8 import requests import platform from numbers import Number import xml.etree.cElementTree as xml from collections import namedtuple +from six import string_types py_majversion, py_minversion, py_revversion = platform.python_version_tuple() if py_majversion == '2': from httplib import responses as HTTP_CODES from urlparse import urlparse + from urlparse import urlsplit else: from http.client import responses as HTTP_CODES from urllib.parse import urlparse - + from urllib.parse import urlsplit + DOWNLOAD_CHUNK_SIZE_BYTES = 1 * 1024 * 1024 +AUTH_MODE_BASIC = 'basic' +AUTH_MODE_DIGEST = 'digest' + +CONTENT_TYPE_DIRECTORY = 'httpd/unix-directory' + class WebdavException(Exception): pass @@ -26,7 +35,7 @@ def codestr(code): return HTTP_CODES.get(code, 'UNKNOWN') -File = namedtuple('File', ['name', 'size', 'mtime', 'ctime', 'contenttype']) +File = namedtuple('File', ['name', 'size', 'mtime', 'ctime', 'contenttype','contentlength', 'is_dir']) def prop(elem, name, default=None): @@ -35,12 +44,29 @@ def prop(elem, name, default=None): def elem2file(elem): + + href = prop(elem, 'href') + url_parts = urlsplit(href) + path = url_parts.path + + # remove trailing slashes + if path[-1] == '/': + path = path[0:len(path)-1] + + content_type = prop(elem, 'getcontenttype', '') + content_length = prop(elem, 'getcontentlength') + + # Try to detect directories... + is_dir = (content_type == CONTENT_TYPE_DIRECTORY) or content_length == None + return File( - prop(elem, 'href'), + path, int(prop(elem, 'getcontentlength', 0)), prop(elem, 'getlastmodified', ''), prop(elem, 'creationdate', ''), - prop(elem, 'getcontenttype', ''), + content_type, + int(content_length) if content_length != None else None, + is_dir ) @@ -73,12 +99,13 @@ def __init__(self, method, path, expected_code, actual_code): class Client(object): def __init__(self, host, port=0, auth=None, username=None, password=None, - protocol='http', verify_ssl=True, path=None, cert=None): + protocol='http', verify_ssl=True, path=None, cert=None, auth_mode=AUTH_MODE_BASIC): if not port: port = 443 if protocol == 'https' else 80 self.baseurl = '{0}://{1}:{2}'.format(protocol, host, port) if path: self.baseurl = '{0}/{1}'.format(self.baseurl, path) + self.cwd = '/' self.session = requests.session() self.session.verify = verify_ssl @@ -90,7 +117,10 @@ def __init__(self, host, port=0, auth=None, username=None, password=None, if auth: self.session.auth = auth elif username and password: - self.session.auth = (username, password) + if auth_mode == AUTH_MODE_DIGEST: + self.session.auth = requests.auth.HTTPDigestAuth(username, password) + else: + self.session.auth = (username, password) def _send(self, method, path, expected_code, **kwargs): url = self._get_url(path) @@ -150,7 +180,7 @@ def delete(self, path): self._send('DELETE', path, 204) def upload(self, local_path_or_fileobj, remote_path): - if isinstance(local_path_or_fileobj, basestring): + if isinstance(local_path_or_fileobj, string_types): with open(local_path_or_fileobj, 'rb') as f: self._upload(f, remote_path) else: @@ -161,7 +191,7 @@ def _upload(self, fileobj, remote_path): def download(self, remote_path, local_path_or_fileobj): response = self._send('GET', remote_path, 200, stream=True) - if isinstance(local_path_or_fileobj, basestring): + if isinstance(local_path_or_fileobj, string_types): with open(local_path_or_fileobj, 'wb') as f: self._download(f, response) else: @@ -186,3 +216,4 @@ def ls(self, remote_path='.'): def exists(self, remote_path): response = self._send('HEAD', remote_path, (200, 301, 404)) return True if response.status_code != 404 else False +