| Index: tools/perf/third_party/davclient/davclient.py
 | 
| diff --git a/tools/perf/third_party/davclient/davclient.py b/tools/perf/third_party/davclient/davclient.py
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..2d9107aa9fb2f6a40a50c983bbcdebb1576c3957
 | 
| --- /dev/null
 | 
| +++ b/tools/perf/third_party/davclient/davclient.py
 | 
| @@ -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)
 | 
| +        
 | 
| +
 | 
| +
 | 
| +
 | 
| +
 | 
| +
 | 
| 
 |