Index: tools/telemetry/third_party/davclient/davclient.py |
=================================================================== |
--- tools/telemetry/third_party/davclient/davclient.py (revision 0) |
+++ tools/telemetry/third_party/davclient/davclient.py (revision 0) |
@@ -0,0 +1,312 @@ |
+# Copyright (c) 2006-2007 Open Source Applications Foundation |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+ |
+import urlparse, httplib, copy, base64, StringIO |
+import urllib |
+ |
+try: |
+ from xml.etree import ElementTree |
+except: |
+ from elementtree import ElementTree |
+ |
+__all__ = ['DAVClient'] |
+ |
+def object_to_etree(parent, obj, namespace=''): |
+ """This function takes in a python object, traverses it, and adds it to an existing etree object""" |
+ |
+ if type(obj) is int or type(obj) is float or type(obj) is str: |
+ # If object is a string, int, or float just add it |
+ obj = str(obj) |
+ if obj.startswith('{') is False: |
+ ElementTree.SubElement(parent, '{%s}%s' % (namespace, obj)) |
+ else: |
+ ElementTree.SubElement(parent, obj) |
+ |
+ elif type(obj) is dict: |
+ # If the object is a dictionary we'll need to parse it and send it back recusively |
+ for key, value in obj.items(): |
+ if key.startswith('{') is False: |
+ key_etree = ElementTree.SubElement(parent, '{%s}%s' % (namespace, key)) |
+ object_to_etree(key_etree, value, namespace=namespace) |
+ else: |
+ key_etree = ElementTree.SubElement(parent, key) |
+ object_to_etree(key_etree, value, namespace=namespace) |
+ |
+ elif type(obj) is list: |
+ # If the object is a list parse it and send it back recursively |
+ for item in obj: |
+ object_to_etree(parent, item, namespace=namespace) |
+ |
+ else: |
+ # If it's none of previous types then raise |
+ raise TypeError, '%s is an unsupported type' % type(obj) |
+ |
+ |
+class DAVClient(object): |
+ |
+ def __init__(self, url='http://localhost:8080'): |
+ """Initialization""" |
+ |
+ self._url = urlparse.urlparse(url) |
+ |
+ self.headers = {'Host':self._url[1], |
+ 'User-Agent': 'python.davclient.DAVClient/0.1'} |
+ |
+ |
+ def _request(self, method, path='', body=None, headers=None): |
+ """Internal request method""" |
+ self.response = None |
+ |
+ if headers is None: |
+ headers = copy.copy(self.headers) |
+ else: |
+ new_headers = copy.copy(self.headers) |
+ new_headers.update(headers) |
+ headers = new_headers |
+ |
+ if self._url.scheme == 'http': |
+ self._connection = httplib.HTTPConnection(self._url[1]) |
+ elif self._url.scheme == 'https': |
+ self._connection = httplib.HTTPSConnection(self._url[1]) |
+ else: |
+ raise Exception, 'Unsupported scheme' |
+ |
+ self._connection.request(method, path, body, headers) |
+ |
+ self.response = self._connection.getresponse() |
+ |
+ self.response.body = self.response.read() |
+ |
+ # Try to parse and get an etree |
+ try: |
+ self._get_response_tree() |
+ except: |
+ pass |
+ |
+ |
+ def _get_response_tree(self): |
+ """Parse the response body into an elementree object""" |
+ self.response.tree = ElementTree.fromstring(self.response.body) |
+ return self.response.tree |
+ |
+ def set_basic_auth(self, username, password): |
+ """Set basic authentication""" |
+ auth = 'Basic %s' % base64.encodestring('%s:%s' % (username, password)).strip() |
+ self._username = username |
+ self._password = password |
+ self.headers['Authorization'] = auth |
+ |
+ ## HTTP DAV methods ## |
+ |
+ def get(self, path, headers=None): |
+ """Simple get request""" |
+ self._request('GET', path, headers=headers) |
+ return self.response.body |
+ |
+ def head(self, path, headers=None): |
+ """Basic HEAD request""" |
+ self._request('HEAD', path, headers=headers) |
+ |
+ def put(self, path, body=None, f=None, headers=None): |
+ """Put resource with body""" |
+ if f is not None: |
+ body = f.read() |
+ |
+ self._request('PUT', path, body=body, headers=headers) |
+ |
+ def post(self, path, body=None, headers=None): |
+ """POST resource with body""" |
+ |
+ self._request('POST', path, body=body, headers=headers) |
+ |
+ def mkcol(self, path, headers=None): |
+ """Make DAV collection""" |
+ self._request('MKCOL', path=path, headers=headers) |
+ |
+ make_collection = mkcol |
+ |
+ def delete(self, path, headers=None): |
+ """Delete DAV resource""" |
+ self._request('DELETE', path=path, headers=headers) |
+ |
+ def copy(self, source, destination, body=None, depth='infinity', overwrite=True, headers=None): |
+ """Copy DAV resource""" |
+ # Set all proper headers |
+ if headers is None: |
+ headers = {'Destination':destination} |
+ else: |
+ headers['Destination'] = self._url.geturl() + destination |
+ if overwrite is False: |
+ headers['Overwrite'] = 'F' |
+ headers['Depth'] = depth |
+ |
+ self._request('COPY', source, body=body, headers=headers) |
+ |
+ |
+ def copy_collection(self, source, destination, depth='infinity', overwrite=True, headers=None): |
+ """Copy DAV collection""" |
+ body = '<?xml version="1.0" encoding="utf-8" ?><d:propertybehavior xmlns:d="DAV:"><d:keepalive>*</d:keepalive></d:propertybehavior>' |
+ |
+ # Add proper headers |
+ if headers is None: |
+ headers = {} |
+ headers['Content-Type'] = 'text/xml; charset="utf-8"' |
+ |
+ self.copy(source, destination, body=unicode(body, 'utf-8'), depth=depth, overwrite=overwrite, headers=headers) |
+ |
+ |
+ def move(self, source, destination, body=None, depth='infinity', overwrite=True, headers=None): |
+ """Move DAV resource""" |
+ # Set all proper headers |
+ if headers is None: |
+ headers = {'Destination':destination} |
+ else: |
+ headers['Destination'] = self._url.geturl() + destination |
+ if overwrite is False: |
+ headers['Overwrite'] = 'F' |
+ headers['Depth'] = depth |
+ |
+ self._request('MOVE', source, body=body, headers=headers) |
+ |
+ |
+ def move_collection(self, source, destination, depth='infinity', overwrite=True, headers=None): |
+ """Move DAV collection and copy all properties""" |
+ body = '<?xml version="1.0" encoding="utf-8" ?><d:propertybehavior xmlns:d="DAV:"><d:keepalive>*</d:keepalive></d:propertybehavior>' |
+ |
+ # Add proper headers |
+ if headers is None: |
+ headers = {} |
+ headers['Content-Type'] = 'text/xml; charset="utf-8"' |
+ |
+ self.move(source, destination, unicode(body, 'utf-8'), depth=depth, overwrite=overwrite, headers=headers) |
+ |
+ |
+ def propfind(self, path, properties='allprop', namespace='DAV:', depth=None, headers=None): |
+ """Property find. If properties arg is unspecified it defaults to 'allprop'""" |
+ # Build propfind xml |
+ root = ElementTree.Element('{DAV:}propfind') |
+ if type(properties) is str: |
+ ElementTree.SubElement(root, '{DAV:}%s' % properties) |
+ else: |
+ props = ElementTree.SubElement(root, '{DAV:}prop') |
+ object_to_etree(props, properties, namespace=namespace) |
+ tree = ElementTree.ElementTree(root) |
+ |
+ # Etree won't just return a normal string, so we have to do this |
+ body = StringIO.StringIO() |
+ tree.write(body) |
+ body = body.getvalue() |
+ |
+ # Add proper headers |
+ if headers is None: |
+ headers = {} |
+ if depth is not None: |
+ headers['Depth'] = depth |
+ headers['Content-Type'] = 'text/xml; charset="utf-8"' |
+ |
+ # Body encoding must be utf-8, 207 is proper response |
+ self._request('PROPFIND', path, body=unicode('<?xml version="1.0" encoding="utf-8" ?>\n'+body, 'utf-8'), headers=headers) |
+ |
+ if self.response is not None and hasattr(self.response, 'tree') is True: |
+ property_responses = {} |
+ for response in self.response.tree._children: |
+ property_href = response.find('{DAV:}href') |
+ property_stat = response.find('{DAV:}propstat') |
+ |
+ def parse_props(props): |
+ property_dict = {} |
+ for prop in props: |
+ if prop.tag.find('{DAV:}') is not -1: |
+ name = prop.tag.split('}')[-1] |
+ else: |
+ name = prop.tag |
+ if len(prop._children) is not 0: |
+ property_dict[name] = parse_props(prop._children) |
+ else: |
+ property_dict[name] = prop.text |
+ return property_dict |
+ |
+ if property_href is not None and property_stat is not None: |
+ property_dict = parse_props(property_stat.find('{DAV:}prop')._children) |
+ property_responses[property_href.text] = property_dict |
+ return property_responses |
+ |
+ def proppatch(self, path, set_props=None, remove_props=None, namespace='DAV:', headers=None): |
+ """Patch properties on a DAV resource. If namespace is not specified the DAV namespace is used for all properties""" |
+ root = ElementTree.Element('{DAV:}propertyupdate') |
+ |
+ if set_props is not None: |
+ prop_set = ElementTree.SubElement(root, '{DAV:}set') |
+ object_to_etree(prop_set, set_props, namespace=namespace) |
+ if remove_props is not None: |
+ prop_remove = ElementTree.SubElement(root, '{DAV:}remove') |
+ object_to_etree(prop_remove, remove_props, namespace=namespace) |
+ |
+ tree = ElementTree.ElementTree(root) |
+ |
+ # Add proper headers |
+ if headers is None: |
+ headers = {} |
+ headers['Content-Type'] = 'text/xml; charset="utf-8"' |
+ |
+ self._request('PROPPATCH', path, body=unicode('<?xml version="1.0" encoding="utf-8" ?>\n'+body, 'utf-8'), headers=headers) |
+ |
+ |
+ def set_lock(self, path, owner, locktype='exclusive', lockscope='write', depth=None, headers=None): |
+ """Set a lock on a dav resource""" |
+ root = ElementTree.Element('{DAV:}lockinfo') |
+ object_to_etree(root, {'locktype':locktype, 'lockscope':lockscope, 'owner':{'href':owner}}, namespace='DAV:') |
+ tree = ElementTree.ElementTree(root) |
+ |
+ # Add proper headers |
+ if headers is None: |
+ headers = {} |
+ if depth is not None: |
+ headers['Depth'] = depth |
+ headers['Content-Type'] = 'text/xml; charset="utf-8"' |
+ headers['Timeout'] = 'Infinite, Second-4100000000' |
+ |
+ self._request('LOCK', path, body=unicode('<?xml version="1.0" encoding="utf-8" ?>\n'+body, 'utf-8'), headers=headers) |
+ |
+ locks = self.response.etree.finall('.//{DAV:}locktoken') |
+ lock_list = [] |
+ for lock in locks: |
+ lock_list.append(lock.getchildren()[0].text.strip().strip('\n')) |
+ return lock_list |
+ |
+ |
+ def refresh_lock(self, path, token, headers=None): |
+ """Refresh lock with token""" |
+ |
+ if headers is None: |
+ headers = {} |
+ headers['If'] = '(<%s>)' % token |
+ headers['Timeout'] = 'Infinite, Second-4100000000' |
+ |
+ self._request('LOCK', path, body=None, headers=headers) |
+ |
+ |
+ def unlock(self, path, token, headers=None): |
+ """Unlock DAV resource with token""" |
+ if headers is None: |
+ headers = {} |
+ headers['Lock-Tocken'] = '<%s>' % token |
+ |
+ self._request('UNLOCK', path, body=None, headers=headers) |
+ |
+ |
+ |
+ |
+ |
+ |