[CalendarServer-changes] [8235] CalendarServer/trunk/twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Fri Oct 28 11:22:24 PDT 2011
Revision: 8235
http://trac.macosforge.org/projects/calendarserver/changeset/8235
Author: sagen at apple.com
Date: 2011-10-28 11:22:23 -0700 (Fri, 28 Oct 2011)
Log Message:
-----------
Add podding logic to iMIP reply handling.
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/directory/test/augments.xml
CalendarServer/trunk/twistedcaldav/mail.py
CalendarServer/trunk/twistedcaldav/test/test_mail.py
Modified: CalendarServer/trunk/twistedcaldav/directory/test/augments.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/augments.xml 2011-10-27 22:32:39 UTC (rev 8234)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments.xml 2011-10-28 18:22:23 UTC (rev 8235)
@@ -30,12 +30,14 @@
<enable>true</enable>
<enable-calendar>true</enable-calendar>
<enable-addressbook>true</enable-addressbook>
+ <server-id>00001</server-id>
</record>
<record>
<uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
<enable>true</enable>
<enable-calendar>true</enable-calendar>
<enable-addressbook>true</enable-addressbook>
+ <server-id>00002</server-id>
</record>
<record>
<uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</uid>
Modified: CalendarServer/trunk/twistedcaldav/mail.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/mail.py 2011-10-27 22:32:39 UTC (rev 8234)
+++ CalendarServer/trunk/twistedcaldav/mail.py 2011-10-28 18:22:23 UTC (rev 8235)
@@ -24,6 +24,7 @@
import datetime
import email.utils
import os
+import urlparse
import uuid
from cStringIO import StringIO
@@ -545,9 +546,35 @@
returnValue(Response(code=responsecode.OK))
+def injectionSettingsFromURL(url, config):
+ """
+ Given a url returned from server podding info (or None if not podding),
+ generate the url that should be used to inject an iMIP reply. If the
+ url is None, then compute the url from config.
+ """
+ path = "inbox"
+ if url is None:
+ # Didn't get url from server podding configuration, so use caldavd.plist
+ if config.Scheduling.iMIP.MailGatewayServer == "localhost":
+ hostname = "localhost"
+ else:
+ hostname = config.ServerHostName
+ if config.EnableSSL:
+ useSSL = True
+ port = config.SSLPort
+ else:
+ useSSL = False
+ port = config.HTTPPort
+ scheme = "https:" if useSSL else "http:"
+ url = "%s//%s:%d/%s/" % (scheme, hostname, port, path)
+ else:
+ url = "%s/%s/" % (url.rstrip("/"), path)
+ return url
-def injectMessage(organizer, attendee, calendar, msgId, reactor=None):
+
+def injectMessage(url, organizer, attendee, calendar, msgId, reactor=None):
+
if reactor is None:
reactor = _reactor
@@ -559,23 +586,9 @@
}
data = str(calendar)
+ url = injectionSettingsFromURL(url, config)
+ parsed = urlparse.urlparse(url)
- if config.EnableSSL:
- useSSL = True
- port = config.SSLPort
- else:
- useSSL = False
- port = config.HTTPPort
-
- # If we're running on same host as calendar server, inject via localhost
- if config.Scheduling.iMIP.MailGatewayServer == 'localhost':
- host = 'localhost'
- else:
- host = config.ServerHostName
- path = "inbox"
- scheme = "https:" if useSSL else "http:"
- url = "%s//%s:%d/%s/" % (scheme, host, port, path)
-
log.debug("Injecting to %s: %s %s" % (url, str(headers), data))
factory = client.HTTPClientFactory(url, method='POST', headers=headers,
@@ -584,10 +597,11 @@
factory.noisy = False
factory.protocol = AuthorizedHTTPGetter
- if useSSL:
- reactor.connectSSL(host, port, factory, ssl.ClientContextFactory())
+ if parsed.scheme == "https":
+ reactor.connectSSL(parsed.hostname, parsed.port, factory,
+ ssl.ClientContextFactory())
else:
- reactor.connectTCP(host, port, factory)
+ reactor.connectTCP(parsed.hostname, parsed.port, factory)
def _success(result, msgId):
log.info("Mail gateway successfully injected message %s" % (msgId,))
@@ -601,8 +615,35 @@
return factory.deferred
+def serverForOrganizer(directory, organizer):
+ """
+ Return the URL for the server hosting the organizer, or None if podding
+ is not enabled or organizer is hosted locally.
+ Raises ServerNotFound if we can't find the record for the organizer.
+ @param directory: service to look for organizer in
+ @type directory: L{DirectoryService}
+ @param organizer: CUA of organizer
+ @type organizer: C{str}
+ @return: string URL
+ """
+ record = directory.recordWithCalendarUserAddress(organizer)
+ if record is None:
+ log.warn("Can't find server for %s" % (organizer,))
+ raise ServerNotFound()
+ server = record.server() # None means hosted locally
+ if server is None:
+ return None
+ else:
+ return server.uri
+
+class ServerNotFound(Exception):
+ """
+ Can't determine which server is hosting a given user
+ """
+
+
class MailGatewayTokensDatabase(AbstractSQLDatabase, LoggingMixIn):
"""
A database to maintain "plus-address" tokens for IMIP requests.
@@ -957,9 +998,17 @@
# TODO: what to do in this case?
pass
- self.log_warn("Mail gateway processing DSN %s" % (msgId,))
- return fn(organizer, attendee, calendar, msgId)
+ try:
+ hostname = serverForOrganizer(self.directory, organizer)
+ except ServerNotFound:
+ # We can't determine which server hosts the organizer
+ self.log_error("Unable to determine which server hosts organizer %s"
+ % (organizer,))
+ return succeed(None)
+ self.log_warn("Mail gateway processing DSN %s to server %s" % (msgId, hostname))
+ return fn(hostname, organizer, attendee, calendar, msgId)
+
def processReply(self, msg, injectFunction, testMode=False):
# extract the token from the To header
name, addr = email.utils.parseaddr(msg['To'])
@@ -1076,9 +1125,18 @@
# the appropriate ATTENDEE. This will require a new localizable
# email template for the message.
- return injectFunction(organizer, attendee, calendar, msg['Message-ID'])
+ try:
+ hostname = serverForOrganizer(self.directory, organizer)
+ except ServerNotFound:
+ # We can't determine which server hosts the organizer
+ self.log_error("Unable to determine which server hosts organizer %s"
+ % (organizer,))
+ return succeed(None)
+ return injectFunction(hostname, organizer, attendee, calendar,
+ msg['Message-ID'])
+
def inbound(self, message, fn=injectMessage):
try:
msg = email.message_from_string(message)
Modified: CalendarServer/trunk/twistedcaldav/test/test_mail.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_mail.py 2011-10-27 22:32:39 UTC (rev 8234)
+++ CalendarServer/trunk/twistedcaldav/test/test_mail.py 2011-10-28 18:22:23 UTC (rev 8235)
@@ -16,28 +16,27 @@
from cStringIO import StringIO
-import datetime
-import email
-
from twisted.internet.defer import inlineCallbacks
-
+from twisted.python.filepath import FilePath
+from twisted.python.modules import getModule
from twisted.web.template import Element, renderer, flattenString
-from twisted.python.modules import getModule
-from twisted.python.filepath import FilePath
-
-from twistedcaldav.test.util import TestCase
-
+from twistedcaldav.config import config, ConfigDict
+from twistedcaldav.directory import augment
+from twistedcaldav.directory.xmlfile import XMLDirectoryService
from twistedcaldav.ical import Component
-from twistedcaldav.config import config
-from twistedcaldav.scheduling.itip import iTIPRequestStatus
-
+from twistedcaldav.mail import injectionSettingsFromURL
+from twistedcaldav.mail import MailGatewayTokensDatabase
from twistedcaldav.mail import MailHandler
from twistedcaldav.mail import StringFormatTemplateLoader
-from twistedcaldav.mail import MailGatewayTokensDatabase
+from twistedcaldav.mail import serverForOrganizer
+from twistedcaldav.scheduling.itip import iTIPRequestStatus
+from twistedcaldav.servers import Servers
+from twistedcaldav.test.util import TestCase
+from twistedcaldav.test.util import xmlFile, augmentsFile
+import datetime
+import email
-from twistedcaldav.directory.directory import DirectoryRecord
-
def echo(*args):
return args
@@ -67,12 +66,31 @@
class MailHandlerTests(TestCase):
def setUp(self):
- TestCase.setUp(self)
- self.handler = MailHandler(dataRoot=":memory:")
+ super(MailHandlerTests, self).setUp()
+
+ self._setupServers(serverData)
+ self.directory = XMLDirectoryService(
+ {
+ 'xmlFile' : xmlFile,
+ 'augmentService' :
+ augment.AugmentXMLDB(xmlFiles=(augmentsFile.path,)),
+ }
+ )
+ self.handler = MailHandler(dataRoot=":memory:", directory=self.directory)
module = getModule(__name__)
self.dataPath = module.filePath.sibling("data").child("mail")
+ def _setupServers(self, data):
+ self.patch(config, "ServerHostName", "caldav1.example.com")
+ self.patch(config, "HTTPPort", 8008)
+ self.patch(config.Servers, "Enabled", True)
+
+ xmlFile = StringIO(data)
+ servers = Servers
+ servers.load(xmlFile, ignoreIPLookupFailures=True)
+
+
def dataFile(self, name):
"""
Get the contents of a given data file from the 'data/mail' test
@@ -80,7 +98,23 @@
"""
return self.dataPath.child(name).getContent()
+ def test_serverDetection(self):
+ wsanchez = self.directory.recordWithShortName("users",
+ "wsanchez")
+ cdaboo = self.directory.recordWithShortName("users",
+ "cdaboo")
+ server = wsanchez.server()
+ self.assertEquals(server.uri, "http://caldav1.example.com:8008")
+ server = cdaboo.server()
+ self.assertEquals(server.uri, "https://caldav2.example.com:8843")
+ url = serverForOrganizer(self.directory,
+ "mailto:wsanchez at example.com")
+ self.assertEquals(url, "http://caldav1.example.com:8008")
+ url = serverForOrganizer(self.directory,
+ "mailto:cdaboo at example.com")
+ self.assertEquals(url, "https://caldav2.example.com:8843")
+
def test_purge_and_lowercase(self):
"""
Ensure that purge( ) cleans out old tokens, and that lowercase( )
@@ -263,12 +297,13 @@
None)
# Make sure a known token *is* processed
- token = self.handler.db.createToken("mailto:user01 at example.com",
+ token = self.handler.db.createToken(
+ "urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500",
"mailto:user02 at example.com", "1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C")
calBody = template % token
- organizer, attendee, calendar, msgId = self.handler.processDSN(calBody,
+ url, organizer, attendee, calendar, msgId = self.handler.processDSN(calBody,
"xyzzy", echo)
- self.assertEquals(organizer, 'mailto:user01 at example.com')
+ self.assertEquals(organizer, 'urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500')
self.assertEquals(attendee, 'mailto:user02 at example.com')
self.assertEquals(str(calendar), """BEGIN:VCALENDAR
VERSION:2.0
@@ -298,7 +333,7 @@
DTEND;TZID=US/Pacific:20080812T104500
CREATED:20080812T191857Z
DTSTAMP:20080812T191932Z
-ORGANIZER;CN=User 01:mailto:user01 at example.com
+ORGANIZER;CN=User 01:urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500
REQUEST-STATUS:5.1;Service unavailable
SEQUENCE:2
SUMMARY:New Event
@@ -318,35 +353,85 @@
# Make sure a known token *is* processed
self.handler.db.createToken(
- "urn:uuid:9DC04A70-E6DD-11DF-9492-0800200C9A66",
+ "urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500",
"mailto:xyzzy at example.com",
icaluid="1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C",
token="d7cdf68d-8b73-4df1-ad3b-f08002fb285f"
)
- organizer, attendee, calendar, msgId = self.handler.processReply(msg,
- echo)
+ url, organizer, attendee, calendar, msgId = self.handler.processReply(msg, echo)
+ self.assertEquals(url, "https://caldav2.example.com:8843")
self.assertEquals(organizer,
- 'urn:uuid:9DC04A70-E6DD-11DF-9492-0800200C9A66')
+ 'urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500')
self.assertEquals(attendee, 'mailto:xyzzy at example.com')
self.assertEquals(msgId,
'<1983F777-BE86-4B98-881E-06D938E60920 at example.com>')
+ def test_injectionSettingsFromURL(self):
+ testData = (
+ (
+ None,
+ {
+ "Scheduling": {
+ "iMIP" : {
+ "MailGatewayServer" : "localhost",
+ },
+ },
+ "EnableSSL" : True,
+ "ServerHostName" : "calendar.example.com",
+ "HTTPPort" : 1111,
+ "SSLPort" : 2222,
+ },
+ "https://localhost:2222/inbox/",
+ ),
+ (
+ None,
+ {
+ "Scheduling": {
+ "iMIP" : {
+ "MailGatewayServer" : "mailgateway.example.com",
+ },
+ },
+ "EnableSSL" : False,
+ "ServerHostName" : "calendar.example.com",
+ "HTTPPort" : 1111,
+ "SSLPort" : 2222,
+ },
+ "http://calendar.example.com:1111/inbox/",
+ ),
+ (
+ "https://calendar.example.com:1234/",
+ { },
+ "https://calendar.example.com:1234/inbox/",
+ ),
+ (
+ "https://calendar.example.com:1234",
+ { },
+ "https://calendar.example.com:1234/inbox/",
+ ),
+ )
+
+ for url, configData, expected in testData:
+ self.assertEquals(
+ expected,
+ injectionSettingsFromURL(url, ConfigDict(mapping=configData))
+ )
+
def test_processReplyMissingOrganizer(self):
msg = email.message_from_string(self.dataFile('reply_missing_organizer'))
# stick the token in the database first
self.handler.db.createToken(
- "urn:uuid:9DC04A70-E6DD-11DF-9492-0800200C9A66",
+ "urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500",
"mailto:xyzzy at example.com",
icaluid="1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C",
token="d7cdf68d-8b73-4df1-ad3b-f08002fb285f"
)
- organizer, attendee, calendar, msgId = self.handler.processReply(msg,
- echo)
+ url, organizer, attendee, calendar, msgId = self.handler.processReply(
+ msg, echo)
organizerProp = calendar.mainComponent().getOrganizerProperty()
self.assertTrue(organizerProp is not None)
self.assertEquals(organizer,
- "urn:uuid:9DC04A70-E6DD-11DF-9492-0800200C9A66")
+ "urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500")
def test_processReplyMissingAttendee(self):
@@ -354,14 +439,14 @@
# stick the token in the database first
self.handler.db.createToken(
- "urn:uuid:9DC04A70-E6DD-11DF-9492-0800200C9A66",
+ "urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500",
"mailto:xyzzy at example.com",
icaluid="1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C",
token="d7cdf68d-8b73-4df1-ad3b-f08002fb285f"
)
- organizer, attendee, calendar, msgId = self.handler.processReply(msg,
- echo)
+ url, organizer, attendee, calendar, msgId = self.handler.processReply(
+ msg, echo)
# Since the expected attendee was missing, the reply processor should
# have added an attendee back in with a "5.1;Service unavailable"
@@ -372,20 +457,12 @@
def test_processReplyMissingAttachment(self):
- # Fake a directory record
- record = DirectoryRecord(self.handler.directory, "users",
- "9DC04A70-E6DD-11DF-9492-0800200C9A66", shortNames=("user01",),
- emailAddresses=("user01 at example.com",))
- record.enabled = True
- self.handler.directory._tmpRecords[
- "guids"]["9DC04A70-E6DD-11DF-9492-0800200C9A66"] = record
-
msg = email.message_from_string(
self.dataFile('reply_missing_attachment')
)
# stick the token in the database first
self.handler.db.createToken(
- "urn:uuid:9DC04A70-E6DD-11DF-9492-0800200C9A66",
+ "urn:uuid:5A985493-EE2C-4665-94CF-4DFEA3A89500",
"mailto:xyzzy at example.com",
icaluid="1E71F9C8-AEDA-48EB-98D0-76E898F6BB5C",
token="d7cdf68d-8b73-4df1-ad3b-f08002fb285f"
@@ -393,7 +470,7 @@
self.assertEquals(
self.handler.processReply(msg, echo, testMode=True),
- ("user01 at example.com", "xyzzy at example.com")
+ ("cdaboo at example.com", "xyzzy at example.com")
)
@@ -759,3 +836,29 @@
self.assertEquals(self.db.lookupByToken(token), None)
+serverData = """<?xml version="1.0" encoding="utf-8"?>
+<servers>
+ <server>
+ <id>00001</id>
+ <uri>http://caldav1.example.com:8008</uri>
+ <allowed-from>127.0.0.1</allowed-from>
+ <shared-secret>foobar</shared-secret>
+ </server>
+ <server>
+ <id>00002</id>
+ <uri>https://caldav2.example.com:8843</uri>
+ <partitions>
+ <partition>
+ <id>A</id>
+ <uri>https://machine1.example.com:8443</uri>
+ </partition>
+ <partition>
+ <id>B</id>
+ <uri>https://machine2.example.com:8443</uri>
+ </partition>
+ </partitions>
+ </server>
+</servers>
+"""
+
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20111028/091e3af4/attachment-0001.html>
More information about the calendarserver-changes
mailing list