Monotone-Parent: 7364cc475f410932b1e40c6ff6e9d7d8210c29b3

Monotone-Revision: 0e81c91aff9e32e69e1dda8e416902bf2f902245

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2009-10-02T14:00:58
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Wolfgang Sourdeau
2009-10-02 14:00:58 +00:00
parent 08a840ca7d
commit a60ab92dc4
3 changed files with 447 additions and 73 deletions
+13
View File
@@ -1,3 +1,16 @@
2009-10-02 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* Tests/webdavlib.py (MailDAVMailQuery): new class that implements
the "mail-query" DAV REPORT.
(WebDAVQuery._initProperties): new helper methods invoked by
PROPFIND and REPORT classes at initialization to format the "prop"
part of their query.
(HTTPUnparsedURL._parse): accept more valid URL characters.
* Tests/test-maildav.py
(DAVMailCollectionTest.testREPORTMailQuery): new method with
initial tests for "mail-query" report.
2009-10-01 Cyril Robert <crobert@inverse.ca>
* SoObjects/Mailer/SOGoMailObject.m: Added methods required for PROPFIND /
+372 -46
View File
@@ -25,35 +25,149 @@ def fetchUserEmail(login):
message1 = """Return-Path: <cyril@cyril.dev>
Received: from cyril.dev (localhost [127.0.0.1])
by cyril.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA;
Tue, 29 Sep 2009 07:42:16 -0400
Tue, 17 Dec 2009 07:42:16 -0400
Received: from aloha.dev (localhost [127.0.0.1])
by aloha.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA;
Tue, 29 Sep 2009 07:42:16 -0400
X-Virus-Scanned: Debian amavisd-new at inverse.ca
Message-ID: <4AC1F296.5060801@cyril.dev>
References: <4AC3BF1B.3010806@inverse.ca>
Message-ID: <4AC1F29sept6.5060801@cyril.dev>
Date: Tue, 29 Sep 2009 07:42:14 -0400
From: Cyril <cyril@cyril.dev>
Organization: Inverse inc.
From: Cyril <message1from@cyril.dev>
User-Agent: Thunderbird 2.0.0.22 (Macintosh/20090605)
MIME-Version: 1.0
To: jacques@cyril.dev
CC: support@inverse.ca, user10@cyril.dev
Subject: Hallo
Content-Type: text/plain; charset=UTF-8; format=flowed
To: message1to@cyril.dev
CC: message1cc@cyril.dev, user10@cyril.dev
Subject: message1subject
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Reply-To: support@inverse.ca,Cyril <cyril@cyril.dev>
Reply-To: support@inverse.ca
Hello Jacques,
Can you read me?
--
Cyril <cyril@cyril.dev>
"""
message2 = """Return-Path: <cyril@cyril.dev>
Received: from cyril.dev (localhost [127.0.0.1])
by cyril.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA;
Tue, 09 Dec 2009 07:42:16 -0400
Message-ID: <410sepAC1F296.5060801a@cyril.dev>
Date: Tue, 10 Sep 2009 07:42:14 -0400
User-Agent: Thunderbird 2.0.0.22 (Macintosh/20090605)
MIME-Version: 1.0
From: Cyril <message2from@cyril.dev>
To: message2to@cyril.dev
CC: message2cc@cyril.dev
Subject: message2subject
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Reply-To: support@inverse.ca
Hello Jacques,
Can you read me?
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff
--
Cyril <cyril@cyril.dev>
"""
message3 = """Return-Path: <cyril@cyril.dev>
Received: from cyril.dev (localhost [127.0.0.1])
by cyril.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA;
Tue, 15 Dec 2009 07:42:16 -0400
Message-ID: <4AC1aF2dec96.5060801a@cyril.dev>
Date: Tue, 10 Dec 2009 07:42:14 -0400
User-Agent: Thunderbird 2.0.0.22 (Macintosh/20090605)
MIME-Version: 1.0
From: Cyril <message3from@cyril.dev>
To: message3to@cyril.dev
CC: message3cc@cyril.dev
Subject: Hallo
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Reply-To: support@inverse.ca
Hello Jacques,
Can you read me?
This message is just a big larger than message1 but smaller than message2
--
Cyril <cyril@cyril.dev>
"""
message1_received = """Received: from cyril.dev (localhost [127.0.0.1])
by cyril.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA;
Tue, 29 Sep 2009 07:42:16 -0400"""
Tue, 17 Dec 2009 07:42:16 -0400"""
class DAVMailCollectionTest(unittest.TestCase):
resource = '/SOGo/dav/%s/Mail/' % username
@@ -64,16 +178,16 @@ class DAVMailCollectionTest(unittest.TestCase):
username, password)
if self.user_email is None:
self.user_email = fetchUserEmail(username)
if self.user_email.startswith ("mailto:"):
if self.user_email.startswith("mailto:"):
self.user_email = self.user_email[7:]
self.resource = '/SOGo/dav/%s/Mail/%s_A_%s/' \
% (username,
username.replace("@", "_A_").replace (".", "_D_"),
mailserver.replace (".", "_D_"))
username.replace("@", "_A_").replace(".", "_D_"),
mailserver.replace(".", "_D_"))
## helper methods
def _makeCollection (self, name, status = 201):
def _makeCollection(self, name, status = 201):
url = "%s%s" % (self.resource, name)
mkcol = webdavlib.WebDAVMKCOL(url)
self.client.execute(mkcol)
@@ -81,7 +195,7 @@ class DAVMailCollectionTest(unittest.TestCase):
"failure creating collection"
"(code = %d)" % mkcol.response["status"])
def _deleteCollection (self, name, status = 204):
def _deleteCollection(self, name, status = 204):
url = "%sfolder%s" % (self.resource, name)
delete = webdavlib.WebDAVDELETE(url)
self.client.execute(delete)
@@ -89,16 +203,18 @@ class DAVMailCollectionTest(unittest.TestCase):
"failure deleting collection"
"(code = %d)" % delete.response["status"])
def _putMessage(self, client, filename,
def _putMessage(self, client, folder, message,
exp_status = 201):
url = "%s%s" % (self.resource, filename)
url = "%sfolder%s" % (self.resource, folder)
put = webdavlib.HTTPPUT(url, message)
put.content_type = "message/rfc822"
client.execute(put)
self.assertEquals(put.response["status"], exp_status,
"%s: event creation/modification:"
" expected status code '%d' (received '%d')"
% (filename, exp_status, put.response["status"]))
if (exp_status is not None):
self.assertEquals(put.response["status"], exp_status,
"message creation/modification:"
" expected status code '%d' (received '%d')"
% (exp_status, put.response["status"]))
return put.response["headers"]["location"]
def _testProperty (self, url, property, expected, isDate = 0):
propfind = webdavlib.WebDAVPROPFIND(url, (property, ), 0)
@@ -123,13 +239,13 @@ class DAVMailCollectionTest(unittest.TestCase):
def testMKCOL(self):
"""Folder creation"""
self._makeCollection ("test-dav-mail-%40-abc")
self._deleteCollection ("test-dav-mail-%40-abc")
self._makeCollection ("test-dav-mail-@-def")
self._deleteCollection ("test-dav-mail-@-def")
self._makeCollection ("test-dav-mail-%20-ghi")
self._deleteCollection ("test-dav-mail-%20-ghi")
self._makeCollection ("test-dav-mail-%25-jkl", 500)
self._makeCollection("test-dav-mail-%40-abc")
self._deleteCollection("test-dav-mail-%40-abc")
self._makeCollection("test-dav-mail-@-def")
self._deleteCollection("test-dav-mail-@-def")
self._makeCollection("test-dav-mail-%20-ghi")
self._deleteCollection("test-dav-mail-%20-ghi")
self._makeCollection("test-dav-mail-%25-jkl", 500)
# Test MOVE
# self._makeCollection ("test-dav-mail-movable")
@@ -144,42 +260,252 @@ class DAVMailCollectionTest(unittest.TestCase):
def testPUT(self):
"""Message creation"""
self._deleteCollection ("test-dav-mail")
self._makeCollection ("test-dav-mail")
self._deleteCollection("test-dav-mail")
self._makeCollection("test-dav-mail")
# message creation on collection url
url = "%s%s" % (self.resource, "foldertest-dav-mail/")
put = webdavlib.HTTPPUT (url, message1)
put = webdavlib.HTTPPUT(url, message1)
put.content_type = "message/rfc822"
self.client.execute (put)
self.client.execute(put)
self.assertEquals(put.response["status"], 201,
"failure putting message"
"(code = %d)" % put.response["status"])
itemLocation = put.response["headers"]["location"]
get = webdavlib.WebDAVGET (itemLocation)
self.client.execute (get)
get = webdavlib.WebDAVGET(itemLocation)
self.client.execute(get)
self.assertEquals(get.response["status"], 200,
"failure getting item"
"(code = %d)" % get.response["status"])
# message creation with explicit filename
url = "%s%s" % (self.resource, "foldertest-dav-mail/blabla.eml")
put = webdavlib.HTTPPUT (url, message1)
url = "%s%s" %(self.resource, "foldertest-dav-mail/blabla.eml")
put = webdavlib.HTTPPUT(url, message1)
put.content_type = "message/rfc822"
self.client.execute (put)
self.client.execute(put)
self.assertEquals(put.response["status"], 201,
"failure putting message"
"(code = %d)" % put.response["status"])
itemLocation = put.response["headers"]["location"]
get = webdavlib.WebDAVGET (itemLocation)
self.client.execute (get)
get = webdavlib.WebDAVGET(itemLocation)
self.client.execute(get)
self.assertEquals(get.response["status"], 200,
"failure getting item"
"(code = %d)" % get.response["status"])
self._deleteCollection ("test-dav-mail")
self._deleteCollection("test-dav-mail")
def _testFilters(self, filters):
for filter in filters:
self._testFilter(filter)
def _testFilter(self, filter):
expected_hrefs = {}
expected_count = len(filter[1])
for href in filter[1]:
expected_hrefs[href] = True
received_count = 0
query = webdavlib.MailDAVMailQuery(self.resource, ["displayname"],
filter[0])
self.client.execute(query)
self.assertEquals(query.response["status"], 207,
"filter %s:\n\tunexpected status: %d"
% (filter[0], query.response["status"]))
query.xpath_namespace = { "D": "DAV:",
"I": "urn:inverse:params:xml:ns:inverse-dav" }
response_nodes = query.xpath_evaluate("/D:multistatus/D:response")
for response_node in response_nodes:
href_node = query.xpath_evaluate("D:href", response_node)[0]
href = href_node.childNodes[0].nodeValue
received_count = received_count + 1
self.assertTrue(expected_hrefs.has_key(href),
"filter %s:\n\tunexpected href: %s" % (filter[0], href))
self.assertEquals(len(filter[1]), received_count,
"filter %s:\n\tunexpected amount of refs: %d"
% (filter[0], received_count))
def testREPORTMailQuery(self):
"""mail-query"""
self._deleteCollection("test-dav-mail")
self._makeCollection("test-dav-mail")
msg1Loc = self._putMessage(self.client, "test-dav-mail", message1)
parsed = webdavlib.HTTPUnparsedURL(msg1Loc)
msg1Path = parsed.path
msg2Loc = self._putMessage(self.client, "test-dav-mail", message2)
parsed = webdavlib.HTTPUnparsedURL(msg2Loc)
msg2Path = parsed.path
msg3Loc = self._putMessage(self.client, "test-dav-mail", message3)
parsed = webdavlib.HTTPUnparsedURL(msg3Loc)
msg3Path = parsed.path
properties = ["{DAV:}displayname"]
## 1. test filter: receive-date
# SINCE, BEFORE, ON
# q = MailDAVMailQuery(self.resource, properties, filters = None)
filters = (({ "receive-date": { "from": "20091201T000000Z",
"to": "20091208T000000Z" } },
[]),
({ "receive-date": { "from": "20091208T000000Z",
"to": "20091213T134300Z" } },
[ msg2Loc ]),
({ "receive-date": { "from": "20091208T000000Z",
"to": "20091216T134300Z" } },
[ msg2Loc, msg3Loc ]),
({ "receive-date": { "from": "20091216T000000Z",
"to": "20091220T134300Z" } },
[ msg1Loc ]),
({ "receive-date": { "from": "20091220T000000Z",
"to": "20091229T134300Z" } },
[]))
self._testFilters(filters)
## 1. test filter: date
# SENTSINCE, SENTBEFORE, SENTON
filters = (({ "date": { "from": "20090101T000000Z",
"to": "20090201T000000Z" } },
[]),
({ "date": { "from": "20090912T000000Z",
"to": "20090929T134300Z" } },
[ msg1Loc ]),
({ "date": { "from": "20090929T134300Z",
"to": "20091209T000000Z" } },
[]),
({ "date": { "from": "20090901T134300Z",
"to": "20091209T000000Z" } },
[ msg1Loc, msg2Loc ]),
({ "date": { "from": "20091201T000000Z",
"to": "20091211T000000Z" } },
[ msg3Loc ]),
({ "date": { "from": "20091211T000000Z",
"to": "20101211T000000Z" } },
[]),
({ "date": { "from": "20090101T000000Z",
"to": "20100101T000000Z" } },
[ msg1Loc, msg2Loc, msg3Loc ]))
self._testFilters(filters)
## 1. test filter: sequence
# x:y
filters = (({ "sequence": { "from": "1" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "sequence": { "from": "5" }},
[]),
({ "sequence": { "to": "5" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "sequence": { "from": "1",
"to": "2" }},
[ msg1Loc, msg2Loc ]))
self._testFilters(filters)
## 1. test filter: uid
# UID
filters = (({ "uid": { "from": "1" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "uid": { "from": "5" }},
[]),
({ "uid": { "to": "5" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "uid": { "from": "1",
"to": "2" }},
[ msg1Loc, msg2Loc ]))
self._testFilters(filters)
## 1. test filter: from
# FROM
filters = (({ "from": { "match": "message" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "from": { "match": "Cyril" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "from": { "match": "cyril.dev" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "from": { "match": "message1from" }},
[ msg1Loc ]),
({ "from": { "match": "message2from" }},
[ msg2Loc ]),
({ "from": { "match": "message3from" }},
[ msg3Loc ]))
self._testFilters(filters)
## 1. test filter: to
# TO
filters = (({ "to": { "match": "message" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "to": { "match": "Cyril" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "to": { "match": "cyril.dev" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "to": { "match": "message1to" }},
[ msg1Loc ]),
({ "to": { "match": "message2to" }},
[ msg2Loc ]),
({ "to": { "match": "message3to" }},
[ msg3Loc ]))
self._testFilters(filters)
## 1. test filter: cc
# CC
filters = (({ "cc": { "match": "message" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "cc": { "match": "Cyril" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "cc": { "match": "cyril.dev" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "cc": { "match": "message1cc" }},
[ msg1Loc ]),
({ "cc": { "match": "message2cc" }},
[ msg2Loc ]),
({ "cc": { "match": "message3cc" }},
[ msg3Loc ]))
self._testFilters(filters)
## 1. test filter: bcc
# BCC
## TODO
## 1. test filter: body
# BODY
filters = (({ "body": { "match": "Hello" }},
[ msg1Loc, msg2Loc, msg3Loc ]),
({ "body": { "match": "Stuff" }},
[ msg2Loc ]),
({ "body": { "match": "DOESNOT MATCH" }},
[]))
self._testFilters(filters)
## 1. test filter: size
# LARGER, SMALLER
## 1. test filter: answered
# ANSWERED, UNANSWERED
## 1. test filter: draft
# DRAFT
## 1. test filter: flagged
# FLAGGED
## 1. test filter: recent
# RECENT
## 1. test filter: seen
# SEEN
## 1. test filter: deleted
# DELETED
## 1. test filter: keywords
# KEYWORD x
# 1. test sort: (receive-date) ARRIVAL
# 1. test sort: (date) DATE
# 1. test sort: CC
# 1. test sort: FROM
# 1. test sort: SIZE
# 1. test sort: SUBJECT
# 1. test sort: TO
self._deleteCollection("test-dav-mail")
def testPROPFIND(self):
"""Message properties"""
@@ -201,11 +527,11 @@ class DAVMailCollectionTest(unittest.TestCase):
("{urn:schemas:httpmail:}textdescription",
"<![CDATA[%s]]>" % message1, 0),
("{urn:schemas:httpmail:}unreadcount", None, 0),
("{urn:schemas:mailheader:}cc","support@inverse.ca, user10@cyril.dev", 0),
("{urn:schemas:mailheader:}date", 1254242534, 1),
("{urn:schemas:mailheader:}from", "Cyril <cyril@cyril.dev>", 0),
("{urn:schemas:mailheader:}cc","message1cc@cyril.dev, user10@cyril.dev", 0),
("{urn:schemas:mailheader:}date", "Tue, 29 Sep 2009 11:42:14 GMT", 0),
("{urn:schemas:mailheader:}from", "Cyril <message1from@cyril.dev>", 0),
("{urn:schemas:mailheader:}in-reply-to", None, 0),
("{urn:schemas:mailheader:}message-id","<4AC1F296.5060801@cyril.dev>", 0),
("{urn:schemas:mailheader:}message-id","<4AC1F29sept6.5060801@cyril.dev>", 0),
("{urn:schemas:mailheader:}received", message1_received, 0),
("{urn:schemas:mailheader:}references",
"<4AC3BF1B.3010806@inverse.ca>", 0),
+62 -27
View File
@@ -22,7 +22,7 @@ class HTTPUnparsedURL:
# ((proto)://((username(:(password)?)@)?hostname(:(port))))(path)?
# if url_re is None:
url_parts = url.split("?")
alpha_match = "[a-zA-Z0-9]+"
alpha_match = "[a-zA-Z0-9%\._-]+"
num_match = "[0-9]+"
pattern = ("((%s)://(((%s)(:(%s)?)@)?(%s)(:(%s))))?(/.*)"
% (alpha_match, alpha_match, alpha_match,
@@ -139,7 +139,15 @@ class WebDAVQuery(HTTPQuery):
self.ns_mgr = _WD_XMLNS_MGR()
self.top_node = None
self.xml_response = None
self.xpath_namespace = { "D": "DAV:" }
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:
@@ -199,11 +207,8 @@ class WebDAVPROPFIND(WebDAVQuery):
def __init__(self, url, properties, depth = None):
WebDAVQuery.__init__(self, url, depth)
self.top_node = _WD_XMLTreeElement("propfind")
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))
if properties is not None and len(properties) > 0:
self._initProperties(properties)
class WebDAVMOVE(WebDAVQuery):
method = "MOVE"
@@ -262,13 +267,10 @@ class CalDAVPOST(WebDAVQuery):
class CalDAVCalendarMultiget(WebDAVREPORT):
def __init__(self, url, properties, hrefs):
WebDAVQuery.__init__(self, url)
multiget_tag = self.ns_mgr.register("calendar-multiget", "urn:ietf:params:xml:ns:caldav")
multiget_tag = self.ns_mgr.register("calendar-multiget", xmlns_caldav)
self.top_node = _WD_XMLTreeElement(multiget_tag)
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))
if properties is not None and len(properties) > 0:
self._initProperties(properties)
for href in hrefs:
href_node = _WD_XMLTreeElement("href")
@@ -278,19 +280,16 @@ class CalDAVCalendarMultiget(WebDAVREPORT):
class CalDAVCalendarQuery(WebDAVREPORT):
def __init__(self, url, properties, component = None, timerange = None):
WebDAVQuery.__init__(self, url)
multiget_tag = self.ns_mgr.register("calendar-query", "urn:ietf:params:xml:ns:caldav")
multiget_tag = self.ns_mgr.register("calendar-query", xmlns_caldav)
self.top_node = _WD_XMLTreeElement(multiget_tag)
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))
if properties is not None and len(properties) > 0:
self._initProperties(properties)
if component is not None:
filter_tag = self.ns_mgr.register("filter",
"urn:ietf:params:xml:ns:caldav")
xmlns_caldav)
compfilter_tag = self.ns_mgr.register("comp-filter",
"urn:ietf:params:xml:ns:caldav")
xmlns_caldav)
filter_node = _WD_XMLTreeElement(filter_tag)
cal_filter_node = _WD_XMLTreeElement(compfilter_tag,
{ "name": "VCALENDAR" })
@@ -312,11 +311,47 @@ class WebDAVSyncQuery(WebDAVREPORT):
if token is not None:
sync_token.append(_WD_XMLTreeTextNode(token))
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))
if properties is not None and len(properties) > 0:
self._initProperties(properties)
class MailDAVMailQuery(WebDAVREPORT):
def __init__(self, url, properties, filters = None, sort = None):
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)
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):
sort_tag = self.ns_mgr.register("sort", xmlns_inversedav)
sort_node = _WD_XMLTreeElement(sort_tag)
self.top_node.append(sort_node)
sort_subtag = self.ns_mgr.register(sort[0], xmlns_inversedav)
if len(sort) > 1:
attributes = sort[1]
else:
attributes = {}
sort_subnode = _WD_XMLTreeElement(sort_subtag, attributes)
sort_node.append(sort_subnode)
# private classes to handle XML stuff
class _WD_XMLNS_MGR:
@@ -339,7 +374,7 @@ class _WD_XMLNS_MGR:
return new_nssym
def register(self, tag, namespace):
if namespace != "DAV:":
if namespace != xmlns_dav:
if self.xmlns.has_key(namespace):
key = self.xmlns[namespace]
else: