mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-08-20 14:13:19 +00:00
See ChangeLog
Monotone-Parent: f1c001048ccc36181a09a459327f563dbc26e12f Monotone-Revision: aab1e060a1d26a9624f7f84c30d1414ca1969560 Monotone-Author: ludovic@Sophos.ca Monotone-Date: 2010-01-24T20:40:12 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
@@ -0,0 +1,500 @@
|
||||
import cStringIO
|
||||
import httplib
|
||||
import M2Crypto.httpslib
|
||||
import re
|
||||
import time
|
||||
import xml.sax.saxutils
|
||||
import xml.dom.ext.reader.Sax2
|
||||
import xml.xpath
|
||||
import sys
|
||||
|
||||
xmlns_dav = "DAV:"
|
||||
xmlns_caldav = "urn:ietf:params:xml:ns:caldav"
|
||||
xmlns_inversedav = "urn:inverse:params:xml:ns:inverse-dav"
|
||||
|
||||
url_re = None
|
||||
|
||||
class HTTPUnparsedURL:
|
||||
def __init__(self, url):
|
||||
self._parse(url)
|
||||
|
||||
def _parse(self, url):
|
||||
# ((proto)://((username(:(password)?)@)?hostname(:(port))))(path)?
|
||||
# if url_re is None:
|
||||
url_parts = url.split("?")
|
||||
alpha_match = "[a-zA-Z0-9%\._-]+"
|
||||
num_match = "[0-9]+"
|
||||
pattern = ("((%s)://(((%s)(:(%s))?@)?(%s)(:(%s))?))?(/.*)"
|
||||
% (alpha_match, alpha_match, alpha_match,
|
||||
alpha_match, num_match))
|
||||
url_re = re.compile(pattern)
|
||||
re_match = url_re.match(url_parts[0])
|
||||
if re_match is None:
|
||||
raise Exception, "URL expression could not be parsed: %s" % url
|
||||
|
||||
(trash, self.protocol, trash, trash, self.username, trash,
|
||||
self.password, self.hostname, trash, self.port, self.path) = re_match.groups()
|
||||
|
||||
self.parameters = {}
|
||||
if len(url_parts) > 1:
|
||||
param_elms = url_parts[1].split("&")
|
||||
for param_pair in param_elms:
|
||||
parameter = param_pair.split("=")
|
||||
self.parameters[parameter[0]] = parameter[1]
|
||||
|
||||
class WebDAVClient:
|
||||
user_agent = "Mozilla/5.0"
|
||||
|
||||
def __init__(self, hostname, port, username, password, forcessl = False):
|
||||
if port == "443" or forcessl:
|
||||
self.conn = M2Crypto.httpslib.HTTPSConnection(hostname, int(port),
|
||||
True)
|
||||
else:
|
||||
self.conn = httplib.HTTPConnection(hostname, port, True)
|
||||
|
||||
self.simpleauth_hash = (("%s:%s" % (username, password))
|
||||
.encode('base64')[:-1])
|
||||
|
||||
def prepare_headers(self, query, body):
|
||||
headers = { "User-Agent": self.user_agent,
|
||||
"authorization": "Basic %s" % self.simpleauth_hash }
|
||||
if body is not None:
|
||||
headers["content-length"] = len(body)
|
||||
if query.__dict__.has_key("depth") and query.depth is not None:
|
||||
headers["depth"] = query.depth
|
||||
if query.__dict__.has_key("content_type"):
|
||||
headers["content-type"] = query.content_type
|
||||
if not query.__dict__.has_key("accept-language"):
|
||||
headers["accept-language"] = 'en-us,en;q=0.5'
|
||||
|
||||
query_headers = query.prepare_headers()
|
||||
if query_headers is not None:
|
||||
for key in query_headers.keys():
|
||||
headers[key] = query_headers[key]
|
||||
|
||||
return headers
|
||||
|
||||
def execute(self, query):
|
||||
body = query.render()
|
||||
|
||||
query.start = time.time()
|
||||
self.conn.request(query.method, query.url,
|
||||
body, self.prepare_headers(query, body))
|
||||
query.set_response(self.conn.getresponse());
|
||||
query.duration = time.time() - query.start
|
||||
|
||||
class HTTPSimpleQuery:
|
||||
method = None
|
||||
|
||||
def __init__(self, url):
|
||||
self.url = url
|
||||
self.response = None
|
||||
self.start = -1
|
||||
self.duration = -1
|
||||
|
||||
def prepare_headers(self):
|
||||
return {}
|
||||
|
||||
def render(self):
|
||||
return None
|
||||
|
||||
def set_response(self, http_response):
|
||||
headers = {}
|
||||
for rk, rv in http_response.getheaders():
|
||||
k = rk.lower()
|
||||
headers[k] = rv
|
||||
self.response = { "headers": headers,
|
||||
"status": http_response.status,
|
||||
"version": http_response.version,
|
||||
"body": http_response.read() }
|
||||
|
||||
class HTTPGET(HTTPSimpleQuery):
|
||||
method = "GET"
|
||||
|
||||
class HTTPQuery(HTTPSimpleQuery):
|
||||
def __init__(self, url):
|
||||
HTTPSimpleQuery.__init__(self, url)
|
||||
self.content_type = "application/octet-stream"
|
||||
|
||||
class HTTPPUT(HTTPQuery):
|
||||
method = "PUT"
|
||||
|
||||
def __init__(self, url, content):
|
||||
HTTPQuery.__init__(self, url)
|
||||
self.content = content
|
||||
|
||||
def render(self):
|
||||
return self.content
|
||||
|
||||
class HTTPPOST(HTTPPUT):
|
||||
method = "POST"
|
||||
|
||||
class WebDAVQuery(HTTPQuery):
|
||||
method = None
|
||||
|
||||
def __init__(self, url, depth = None):
|
||||
HTTPQuery.__init__(self, url)
|
||||
self.content_type = "application/xml; charset=\"utf-8\""
|
||||
self.depth = depth
|
||||
self.ns_mgr = _WD_XMLNS_MGR()
|
||||
self.top_node = None
|
||||
self.xml_response = None
|
||||
self.xpath_namespace = { "D": xmlns_dav }
|
||||
|
||||
# helper for PROPFIND and REPORT (only)
|
||||
def _initProperties(self, properties):
|
||||
props = _WD_XMLTreeElement("prop")
|
||||
self.top_node.append(props)
|
||||
for prop in properties:
|
||||
prop_tag = self.render_tag(prop)
|
||||
props.append(_WD_XMLTreeElement(prop_tag))
|
||||
|
||||
def render(self):
|
||||
if self.top_node is not None:
|
||||
text = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n%s"
|
||||
% self.top_node.render(self.ns_mgr.render()))
|
||||
else:
|
||||
text = ""
|
||||
|
||||
return text
|
||||
|
||||
def render_tag(self, tag):
|
||||
cb = tag.find("}")
|
||||
if cb > -1:
|
||||
ns = tag[1:cb]
|
||||
real_tag = tag[cb+1:]
|
||||
new_tag = self.ns_mgr.register(real_tag, ns)
|
||||
else:
|
||||
new_tag = tag
|
||||
|
||||
return new_tag
|
||||
|
||||
def set_response(self, http_response):
|
||||
HTTPQuery.set_response(self, http_response)
|
||||
headers = self.response["headers"]
|
||||
if (headers.has_key("content-type")
|
||||
and headers.has_key("content-length")
|
||||
and (headers["content-type"].startswith("application/xml")
|
||||
or headers["content-type"].startswith("text/xml"))
|
||||
and int(headers["content-length"]) > 0):
|
||||
reader = xml.dom.ext.reader.Sax2.Reader()
|
||||
stream = cStringIO.StringIO(self.response["body"])
|
||||
dom_response = reader.fromStream(stream)
|
||||
self.response["document"] = dom_response.documentElement
|
||||
|
||||
def xpath_evaluate(self, query, top_node = None):
|
||||
if top_node is None:
|
||||
top_node = self.response["document"]
|
||||
xpath_context = xml.xpath.CreateContext(top_node)
|
||||
xpath_context.setNamespaces(self.xpath_namespace)
|
||||
return xml.xpath.Evaluate(query, None, xpath_context)
|
||||
|
||||
class WebDAVMKCOL(WebDAVQuery):
|
||||
method = "MKCOL"
|
||||
|
||||
class WebDAVDELETE(WebDAVQuery):
|
||||
method = "DELETE"
|
||||
|
||||
class WebDAVREPORT(WebDAVQuery):
|
||||
method = "REPORT"
|
||||
|
||||
class WebDAVGET(WebDAVQuery):
|
||||
method = "GET"
|
||||
|
||||
class WebDAVPROPFIND(WebDAVQuery):
|
||||
method = "PROPFIND"
|
||||
|
||||
def __init__(self, url, properties, depth = None):
|
||||
WebDAVQuery.__init__(self, url, depth)
|
||||
self.top_node = _WD_XMLTreeElement("propfind")
|
||||
if properties is not None and len(properties) > 0:
|
||||
self._initProperties(properties)
|
||||
|
||||
class WebDAVPROPPATCH(WebDAVQuery):
|
||||
method = "PROPPATCH"
|
||||
|
||||
# <x0:propertyupdate xmlns:x1="urn:ietf:params:xml:ns:caldav" xmlns:x0="DAV:"><x0:set><x0:prop>
|
||||
|
||||
def __init__(self, url, properties):
|
||||
WebDAVQuery.__init__(self, url, None)
|
||||
self.top_node = _WD_XMLTreeElement("propertyupdate")
|
||||
set_node = _WD_XMLTreeElement("set")
|
||||
self.top_node.append(set_node)
|
||||
prop_node = _WD_XMLTreeElement("prop")
|
||||
set_node.append(prop_node)
|
||||
|
||||
prop_node.appendSubtree(self, properties)
|
||||
|
||||
class WebDAVMOVE(WebDAVQuery):
|
||||
method = "MOVE"
|
||||
destination = None
|
||||
host = None
|
||||
|
||||
def prepare_headers(self):
|
||||
headers = WebDAVQuery.prepare_headers(self)
|
||||
print "DESTINATION", self.destination
|
||||
if self.destination is not None:
|
||||
headers["Destination"] = self.destination
|
||||
if self.host is not None:
|
||||
headers["Host"] = self.host
|
||||
return headers
|
||||
|
||||
class WebDAVPUT(WebDAVQuery):
|
||||
method = "PUT"
|
||||
|
||||
def __init__(self, url, content):
|
||||
WebDAVQuery.__init__(self, url)
|
||||
self.content_type = "text/plain; charset=utf-8"
|
||||
self.content = content
|
||||
|
||||
def prepare_headers(self):
|
||||
return WebDAVQuery.prepare_headers(self)
|
||||
|
||||
def render(self):
|
||||
return self.content
|
||||
|
||||
|
||||
class CalDAVPOST(WebDAVQuery):
|
||||
method = "POST"
|
||||
|
||||
def __init__(self, url, content,
|
||||
originator = None, recipients = None):
|
||||
WebDAVQuery.__init__(self, url)
|
||||
self.content_type = "text/calendar; charset=utf-8"
|
||||
self.originator = originator
|
||||
self.recipients = recipients
|
||||
self.content = content
|
||||
|
||||
def prepare_headers(self):
|
||||
headers = WebDAVQuery.prepare_headers(self)
|
||||
|
||||
if self.originator is not None:
|
||||
headers["originator"] = self.originator
|
||||
|
||||
if self.recipients is not None:
|
||||
headers["recipient"] = ",".join(self.recipients)
|
||||
|
||||
return headers
|
||||
|
||||
def render(self):
|
||||
return self.content
|
||||
|
||||
class CalDAVCalendarMultiget(WebDAVREPORT):
|
||||
def __init__(self, url, properties, hrefs):
|
||||
WebDAVQuery.__init__(self, url)
|
||||
multiget_tag = self.ns_mgr.register("calendar-multiget", xmlns_caldav)
|
||||
self.top_node = _WD_XMLTreeElement(multiget_tag)
|
||||
if properties is not None and len(properties) > 0:
|
||||
self._initProperties(properties)
|
||||
|
||||
for href in hrefs:
|
||||
href_node = _WD_XMLTreeElement("href")
|
||||
self.top_node.append(href_node)
|
||||
href_node.append(_WD_XMLTreeTextNode(href))
|
||||
|
||||
class CalDAVCalendarQuery(WebDAVREPORT):
|
||||
def __init__(self, url, properties, component = None, timerange = None):
|
||||
WebDAVQuery.__init__(self, url)
|
||||
multiget_tag = self.ns_mgr.register("calendar-query", xmlns_caldav)
|
||||
self.top_node = _WD_XMLTreeElement(multiget_tag)
|
||||
if properties is not None and len(properties) > 0:
|
||||
self._initProperties(properties)
|
||||
|
||||
if component is not None:
|
||||
filter_tag = self.ns_mgr.register("filter",
|
||||
xmlns_caldav)
|
||||
compfilter_tag = self.ns_mgr.register("comp-filter",
|
||||
xmlns_caldav)
|
||||
filter_node = _WD_XMLTreeElement(filter_tag)
|
||||
cal_filter_node = _WD_XMLTreeElement(compfilter_tag,
|
||||
{ "name": "VCALENDAR" })
|
||||
comp_node = _WD_XMLTreeElement(compfilter_tag,
|
||||
{ "name": component })
|
||||
## TODO
|
||||
# if timerange is not None:
|
||||
cal_filter_node.append(comp_node)
|
||||
filter_node.append(cal_filter_node)
|
||||
self.top_node.append(filter_node)
|
||||
|
||||
class WebDAVSyncQuery(WebDAVREPORT):
|
||||
def __init__(self, url, token, properties):
|
||||
WebDAVQuery.__init__(self, url)
|
||||
self.top_node = _WD_XMLTreeElement("sync-collection")
|
||||
|
||||
sync_token = _WD_XMLTreeElement("sync-token")
|
||||
self.top_node.append(sync_token)
|
||||
if token is not None:
|
||||
sync_token.append(_WD_XMLTreeTextNode(token))
|
||||
|
||||
if properties is not None and len(properties) > 0:
|
||||
self._initProperties(properties)
|
||||
|
||||
class WebDAVExpandProperty(WebDAVREPORT):
|
||||
def _parseTag(self, tag):
|
||||
result = []
|
||||
|
||||
cb = tag.find("}")
|
||||
if cb > -1:
|
||||
result.append(tag[cb+1:])
|
||||
result.append(tag[1:cb])
|
||||
else:
|
||||
result.append(tag)
|
||||
result.append("DAV:")
|
||||
|
||||
return result;
|
||||
|
||||
def _propElement(self, tag):
|
||||
parsedTag = self._parseTag(tag)
|
||||
parameters = { "name": parsedTag[0] }
|
||||
if len(parsedTag) > 1:
|
||||
parameters["namespace"] = parsedTag[1]
|
||||
|
||||
return _WD_XMLTreeElement("property", parameters)
|
||||
|
||||
def __init__(self, url, query_properties, properties):
|
||||
WebDAVQuery.__init__(self, url)
|
||||
self.top_node = _WD_XMLTreeElement("expand-property")
|
||||
|
||||
for query_tag in query_properties:
|
||||
property_query = self._propElement(query_tag)
|
||||
self.top_node.append(property_query)
|
||||
for tag in properties:
|
||||
property = self._propElement(tag)
|
||||
property_query.append(property)
|
||||
|
||||
class MailDAVMailQuery(WebDAVREPORT):
|
||||
def __init__(self, url, properties, filters = None,
|
||||
sort = None, ascending = True):
|
||||
WebDAVQuery.__init__(self, url)
|
||||
mailquery_tag = self.ns_mgr.register("mail-query",
|
||||
xmlns_inversedav)
|
||||
self.top_node = _WD_XMLTreeElement(mailquery_tag)
|
||||
if properties is not None and len(properties) > 0:
|
||||
self._initProperties(properties)
|
||||
|
||||
if filters is not None and len(filters) > 0:
|
||||
self._initFilters(filters)
|
||||
|
||||
if sort is not None and len(sort) > 0:
|
||||
self._initSort(sort, ascending)
|
||||
|
||||
def _initFilters(self, filters):
|
||||
mailfilter_tag = self.ns_mgr.register("mail-filters",
|
||||
xmlns_inversedav)
|
||||
mailfilter_node = _WD_XMLTreeElement(mailfilter_tag)
|
||||
self.top_node.append(mailfilter_node)
|
||||
for filterk in filters.keys():
|
||||
filter_tag = self.ns_mgr.register(filterk,
|
||||
xmlns_inversedav)
|
||||
filter_node = _WD_XMLTreeElement(filter_tag,
|
||||
filters[filterk])
|
||||
mailfilter_node.append(filter_node)
|
||||
|
||||
def _initSort(self, sort, ascending):
|
||||
sort_tag = self.ns_mgr.register("sort", xmlns_inversedav)
|
||||
if ascending:
|
||||
sort_attrs = { "order": "ascending" }
|
||||
else:
|
||||
sort_attrs = { "order": "descending" }
|
||||
sort_node = _WD_XMLTreeElement(sort_tag, sort_attrs)
|
||||
self.top_node.append(sort_node)
|
||||
|
||||
for item in sort:
|
||||
sort_subnode = _WD_XMLTreeElement(self.render_tag(item))
|
||||
sort_node.append(sort_subnode)
|
||||
|
||||
# private classes to handle XML stuff
|
||||
class _WD_XMLNS_MGR:
|
||||
def __init__(self):
|
||||
self.xmlns = {}
|
||||
self.counter = 0
|
||||
|
||||
def render(self):
|
||||
text = " xmlns=\"DAV:\""
|
||||
for k in self.xmlns:
|
||||
text = text + " xmlns:%s=\"%s\"" % (self.xmlns[k], k)
|
||||
|
||||
return text
|
||||
|
||||
def create_key(self, namespace):
|
||||
new_nssym = "n%d" % self.counter
|
||||
self.counter = self.counter + 1
|
||||
self.xmlns[namespace] = new_nssym
|
||||
|
||||
return new_nssym
|
||||
|
||||
def register(self, tag, namespace):
|
||||
if namespace != xmlns_dav:
|
||||
if self.xmlns.has_key(namespace):
|
||||
key = self.xmlns[namespace]
|
||||
else:
|
||||
key = self.create_key(namespace)
|
||||
else:
|
||||
key = None
|
||||
|
||||
if key is not None:
|
||||
newTag = "%s:%s" % (key, tag)
|
||||
else:
|
||||
newTag = tag
|
||||
|
||||
return newTag
|
||||
|
||||
class _WD_XMLTreeElement:
|
||||
typeNum = type(0)
|
||||
typeStr = type("")
|
||||
typeList = type([])
|
||||
typeDict = type({})
|
||||
|
||||
def __init__(self, tag, attributes = {}):
|
||||
self.tag = tag
|
||||
self.children = []
|
||||
self.attributes = attributes
|
||||
|
||||
def append(self, child):
|
||||
self.children.append(child)
|
||||
|
||||
def appendSubtree(self, query, subtree):
|
||||
if type(subtree) == self.typeNum:
|
||||
strValue = "%d" % subtree
|
||||
textNode = _WD_XMLTreeTextNode(strValue)
|
||||
self.append(textNode)
|
||||
elif type(subtree) == self.typeStr:
|
||||
textNode = _WD_XMLTreeTextNode(subtree)
|
||||
self.append(textNode)
|
||||
elif type(subtree) == self.typeList:
|
||||
for x in subtree:
|
||||
self.appendSubtree(query, x)
|
||||
elif type(subtree) == self.typeDict:
|
||||
for x in subtree.keys():
|
||||
tag = query.render_tag(x)
|
||||
node = _WD_XMLTreeElement(tag)
|
||||
node.appendSubtree(query, subtree[x])
|
||||
self.append(node)
|
||||
else:
|
||||
None
|
||||
|
||||
def render(self, ns_text = None):
|
||||
text = "<" + self.tag
|
||||
|
||||
if ns_text is not None:
|
||||
text = text + ns_text
|
||||
|
||||
for k in self.attributes:
|
||||
text = text + " %s=\"%s\"" % (k, self.attributes[k])
|
||||
|
||||
if len(self.children) > 0:
|
||||
text = text + ">"
|
||||
for child in self.children:
|
||||
text = text + child.render()
|
||||
text = text + "</" + self.tag + ">"
|
||||
else:
|
||||
text = text + "/>"
|
||||
|
||||
return text
|
||||
|
||||
class _WD_XMLTreeTextNode:
|
||||
def __init__(self, text):
|
||||
self.text = xml.sax.saxutils.escape(text)
|
||||
|
||||
def render(self):
|
||||
return self.text
|
||||
Reference in New Issue
Block a user