[CalendarServer-changes] [2556] CalendarServer/branches/users/cdaboo/server2server-2524/ twistedcaldav
source_changes at macosforge.org
source_changes at macosforge.org
Fri Jun 13 16:42:18 PDT 2008
Revision: 2556
http://trac.macosforge.org/projects/calendarserver/changeset/2556
Author: cdaboo at apple.com
Date: 2008-06-13 16:42:17 -0700 (Fri, 13 Jun 2008)
Log Message:
-----------
Basic outbound iMIP scheduling via the server. Currently hard-coded in place of server-to-server.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/__init__.py
CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/schedule_common.py
Added Paths:
-----------
CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/schedule_imip.py
CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/test/test_imip.py
Modified: CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/__init__.py 2008-06-12 18:47:55 UTC (rev 2555)
+++ CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/__init__.py 2008-06-13 23:42:17 UTC (rev 2556)
@@ -48,6 +48,7 @@
"root",
"schedule",
"schedule_common",
+ "schedule_imip",
"servertoserver",
"servertoserverparser",
"sql",
Modified: CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/schedule_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/schedule_common.py 2008-06-12 18:47:55 UTC (rev 2555)
+++ CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/schedule_common.py 2008-06-13 23:42:17 UTC (rev 2556)
@@ -46,6 +46,7 @@
from twistedcaldav.method import report_common
from twistedcaldav.method.put_common import StoreCalendarObjectResource
from twistedcaldav.resource import isCalendarCollectionResource
+from twistedcaldav.schedule_imip import ServerToIMip
from twistedcaldav.servertoserver import ServerToServer
from twistedcaldav.servertoserver import ServerToServerRequest
import itertools
@@ -385,7 +386,8 @@
# Now process remote recipients
if remote_recipients:
- yield self.generateRemoteSchedulingResponses(remote_recipients, responses)
+ #yield self.generateRemoteSchedulingResponses(remote_recipients, responses, freebusy)
+ yield self.generateIMIPSchedulingResponses(remote_recipients, responses, freebusy)
# Now we have to do auto-respond
if len(autoresponses) != 0:
@@ -403,7 +405,7 @@
returnValue(responses.response())
@inlineCallbacks
- def generateRemoteSchedulingResponses(self, recipients, responses):
+ def generateRemoteSchedulingResponses(self, recipients, responses, freebusy):
"""
Generate scheduling responses for remote recipients.
"""
@@ -447,6 +449,19 @@
yield DeferredList(deferreds)
@inlineCallbacks
+ def generateIMIPSchedulingResponses(self, recipients, responses, freebusy):
+ """
+ Generate scheduling responses for iMIP recipients.
+ """
+
+ # Now we process each server: let's use a DeferredList to aggregate all the Deferred's
+ # we will generate for each request. That way we can have parallel requests in progress
+ # rather than serialize them.
+
+ requestor = ServerToIMip(self, recipients, responses)
+ yield requestor.doEMail(freebusy)
+
+ @inlineCallbacks
def generateLocalResponse(self, recipient, responses, autoresponses):
# Hash the iCalendar data for use as the last path element of the URI path
calendar_str = str(self.calendar)
@@ -568,12 +583,6 @@
)
returnValue(fbresult)
-
- def generateRemoteResponse(self):
- raise NotImplementedError
-
- def generateRemoteFreeBusyResponse(self):
- raise NotImplementedError
class CalDAVScheduler(Scheduler):
Added: CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/schedule_imip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/schedule_imip.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/schedule_imip.py 2008-06-13 23:42:17 UTC (rev 2556)
@@ -0,0 +1,254 @@
+##
+# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+#
+# 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.
+##
+from twisted.mail.smtp import messageid
+from twisted.mail.smtp import rfc822date
+from twisted.mail.smtp import sendmail
+import datetime
+import base64
+import MimeWriter
+import cStringIO
+
+"""
+Server to iMIP scheduling functions.
+"""
+
+__all__ = [
+ "ServerToIMip",
+]
+
+from twisted.internet.defer import inlineCallbacks
+from twisted.python.failure import Failure
+from twisted.web2 import responsecode
+from twisted.web2.dav.http import ErrorResponse
+from twisted.web2.http import HTTPError
+
+from twistedcaldav.caldavxml import caldav_namespace
+from twistedcaldav.log import Logger
+
+log = Logger()
+
+class ServerToIMip(object):
+
+ def __init__(self, scheduler, recipients, responses):
+
+ self.scheduler = scheduler
+ self.recipients = recipients
+ self.responses = responses
+
+ @inlineCallbacks
+ def doEMail(self, freebusy):
+
+ # Generate an HTTP client request
+ try:
+ # We do not do freebusy requests via iMIP
+ if freebusy:
+ raise ValueError("iMIP VFREEBUSY REQUESTs not supported.")
+
+ message = self._generateTemplateMessage(self.scheduler.calendar)
+ fromAddr = self.scheduler.originator.cuaddr
+ if not fromAddr.startswith("mailto:"):
+ raise ValueError("ORGANIZER address '%s' must be mailto: for iMIP operation." % (fromAddr,))
+ fromAddr = fromAddr[7:]
+ message = message.replace("${fromaddress}", fromAddr)
+
+ for recipient in self.recipients:
+ try:
+ toAddr = recipient.cuaddr
+ if not toAddr.startswith("mailto:"):
+ raise ValueError("ATTENDEE address '%s' must be mailto: for iMIP operation." % (toAddr,))
+ toAddr = toAddr[7:]
+ sendit = message.replace("${toaddress}", toAddr)
+ yield sendmail("relay.apple.com", fromAddr, toAddr, sendit)
+
+ except Exception, e:
+ # Generated failed response for this recipient
+ log.err("Could not do server-to-imip request : %s %s" % (self, e))
+ err = HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "recipient-failed")))
+ self.responses.add(recipient.cuaddr, Failure(exc_value=err), reqstatus="5.1;Service unavailable")
+
+ else:
+ self.responses.add(recipient.cuaddr, responsecode.OK, reqstatus="2.0;Success")
+
+ except Exception, e:
+ # Generated failed responses for each recipient
+ log.err("Could not do server-to-server request : %s %s" % (self, e))
+ for recipient in self.recipients:
+ err = HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "recipient-failed")))
+ self.responses.add(recipient.cuaddr, Failure(exc_value=err), reqstatus="5.1;Service unavailable")
+
+ def _generateTemplateMessage(self, calendar):
+
+ caldata = str(calendar)
+ data = cStringIO.StringIO()
+ writer = MimeWriter.MimeWriter(data)
+
+ writer.addheader("From", "${fromaddress}")
+ writer.addheader("To", "${toaddress}")
+ writer.addheader("Date", rfc822date())
+ writer.addheader("Subject", "DO NOT REPLY: calendar invitation test")
+ writer.addheader("Message-ID", messageid())
+ writer.addheader("Mime-Version", "1.0")
+ writer.flushheaders()
+
+ writer.startmultipartbody("mixed")
+
+ # message body
+ part = writer.nextpart()
+ body = part.startbody("text/plain")
+ body.write("""Hi,
+You've been invited to a cool event by CalendarServer's new iMIP processor.
+
+%s
+""" % (self._generateCalendarSummary(calendar),))
+
+ part = writer.nextpart()
+ encoding = "7bit"
+ for i in caldata:
+ if ord(i) > 127:
+ encoding = "base64"
+ caldata = base64.encodestring(caldata)
+ break
+ part.addheader("Content-Transfer-Encoding", encoding)
+ body = part.startbody("text/calendar; charset=utf-8")
+ body.write(caldata.replace("\r", ""))
+
+ # finish
+ writer.lastpart()
+
+ return data.getvalue()
+
+ def _generateCalendarSummary(self, calendar):
+
+ # Get the most appropriate component
+ component = calendar.masterComponent()
+ if component is None:
+ component = calendar.mainComponent(True)
+
+ organizer = component.getOrganizerProperty()
+ if "CN" in organizer.params():
+ organizer = "%s <%s>" % (organizer.params()["CN"][0], organizer.value(),)
+ else:
+ organizer = organizer.value()
+
+ dtinfo = self._getDateTimeInfo(component)
+
+ summary = component.propertyValue("SUMMARY")
+ if summary is None:
+ summary = ""
+
+ description = component.propertyValue("DESCRIPTION")
+ if description is None:
+ description = ""
+
+ return """---- Begin Calendar Event Summary ----
+
+Organizer: %s
+Summary: %s
+%sDescription: %s
+
+---- End Calendar Event Summary ----
+""" % (organizer, summary, dtinfo, description,)
+
+ def _getDateTimeInfo(self, component):
+
+ dtstart = component.propertyNativeValue("DTSTART")
+ tzid_start = component.getProperty("DTSTART").params().get("TZID", "UTC")
+
+ dtend = component.propertyNativeValue("DTEND")
+ if dtend:
+ tzid_end = component.getProperty("DTEND").params().get("TZID", "UTC")
+ duration = dtend - dtstart
+ else:
+ duration = component.propertyNativeValue("DURATION")
+ if duration:
+ dtend = dtstart + duration
+ tzid_end = tzid_start
+ else:
+ if isinstance(dtstart, datetime.date):
+ dtend = None
+ duration = datetime.timedelta(days=1)
+ else:
+ dtend = dtstart + datetime.timedelta(days=1)
+ dtend.hour = dtend.minute = dtend.second = 0
+ duration = dtend - dtstart
+ result = "Starts: %s\n" % (self._getDateTimeText(dtstart, tzid_start),)
+ if dtend is not None:
+ result += "Ends: %s\n" % (self._getDateTimeText(dtend, tzid_end),)
+ result += "Duration: %s\n" % (self._getDurationText(duration),)
+
+ if not isinstance(dtstart, datetime.datetime):
+ result += "All Day\n"
+
+ for property_name in ("RRULE", "RDATE", "EXRULE", "EXDATE", "RECURRENCE-ID",):
+ if component.hasProperty(property_name):
+ result += "Recurring\n"
+ break
+
+ return result
+
+ def _getDateTimeText(self, dtvalue, tzid):
+
+ if isinstance(dtvalue, datetime.datetime):
+ timeformat = "%A, %B %e, %Y %I:%M %p"
+ elif isinstance(dtvalue, datetime.date):
+ timeformat = "%A, %B %e, %Y"
+ tzid = ""
+ if tzid:
+ tzid = " (%s)" % (tzid,)
+
+ return "%s%s" % (dtvalue.strftime(timeformat), tzid,)
+
+ def _getDurationText(self, duration):
+
+ result = ""
+ if duration.days > 0:
+ result += "%d %s" % (
+ duration.days,
+ self._pluralize(duration.days, "day", "days")
+ )
+
+ hours = duration.seconds / 3600
+ minutes = divmod(duration.seconds / 60, 60)[1]
+ seconds = divmod(duration.seconds, 60)[1]
+
+ if hours > 0:
+ if result:
+ result += ", "
+ result += "%d %s" % (
+ hours,
+ self._pluralize(hours, "hour", "hours")
+ )
+
+ if minutes > 0:
+ if result:
+ result += ", "
+ result += "%d %s" % (
+ minutes,
+ self._pluralize(minutes, "minute", "minutes")
+ )
+
+ if seconds > 0:
+ if result:
+ result += ", "
+ result += "%d %s" % (
+ seconds,
+ self._pluralize(seconds, "second", "seconds")
+ )
+
+ return result
+
+ def _pluralize(self, number, unit1, unitS):
+ return unit1 if number == 1 else unitS
Added: CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/test/test_imip.py
===================================================================
--- CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/test/test_imip.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/server2server-2524/twistedcaldav/test/test_imip.py 2008-06-13 23:42:17 UTC (rev 2556)
@@ -0,0 +1,500 @@
+##
+# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+#
+# 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.
+##
+from twistedcaldav.ical import Component
+
+import datetime
+
+from twistedcaldav.schedule_imip import ServerToIMip
+import twistedcaldav.test.util
+
+class iMIP (twistedcaldav.test.util.TestCase):
+ """
+ iCalendar support tests
+ """
+
+ class DummyScheduler(object):
+ def __init__(self, calendar):
+ self.calendar = calendar
+
+ def test_datetime_text(self):
+
+ imip = ServerToIMip(None, [], [])
+
+ data = (
+ (
+ datetime.datetime(2008, 06, 01, 12, 0, 0),
+ "America/New_York",
+ "Sunday, June 1, 2008 12:00 PM (America/New_York)",
+ ),
+ (
+ datetime.date(2008, 06, 02),
+ "",
+ "Monday, June 2, 2008",
+ ),
+ )
+
+ for dt, tzid, result in data:
+ self.assertEqual(imip._getDateTimeText(dt, tzid), result)
+
+ def test_duration_text(self):
+
+ imip = ServerToIMip(None, [], [])
+
+ data = (
+ (
+ datetime.timedelta(days=1),
+ "1 day",
+ ),
+ (
+ datetime.timedelta(days=2),
+ "2 days",
+ ),
+ (
+ datetime.timedelta(seconds=1*60*60),
+ "1 hour",
+ ),
+ (
+ datetime.timedelta(seconds=2*60*60),
+ "2 hours",
+ ),
+ (
+ datetime.timedelta(seconds=1*60),
+ "1 minute",
+ ),
+ (
+ datetime.timedelta(seconds=2*60),
+ "2 minutes",
+ ),
+ (
+ datetime.timedelta(seconds=1),
+ "1 second",
+ ),
+ (
+ datetime.timedelta(seconds=2),
+ "2 seconds",
+ ),
+ (
+ datetime.timedelta(days=1, seconds=1*60*60),
+ "1 day, 1 hour",
+ ),
+ (
+ datetime.timedelta(days=1, seconds=1*60),
+ "1 day, 1 minute",
+ ),
+ (
+ datetime.timedelta(days=1, seconds=1),
+ "1 day, 1 second",
+ ),
+ (
+ datetime.timedelta(days=1, seconds=1*60*60 + 2*60),
+ "1 day, 1 hour, 2 minutes",
+ ),
+ (
+ datetime.timedelta(seconds=2*60*60 + 15*60),
+ "2 hours, 15 minutes",
+ ),
+ )
+
+ for duration, result in data:
+ self.assertEqual(imip._getDurationText(duration), result)
+
+ def test_datetime_info(self):
+ data = (
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """Starts: Sunday, June 1, 2008 12:00 PM (UTC)
+Ends: Sunday, June 1, 2008 01:00 PM (UTC)
+Duration: 1 hour
+""",
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;VALUE=DATE:20080601
+DTEND;VALUE=DATE:20080602
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """Starts: Sunday, June 1, 2008
+Ends: Monday, June 2, 2008
+Duration: 1 day
+All Day
+""",
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;VALUE=DATE:20080601
+DTEND;VALUE=DATE:20080602
+RRULE:FREQ=YEARLY
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+END:VEVENT
+END:VCALENDAR
+""",
+ """Starts: Sunday, June 1, 2008
+Ends: Monday, June 2, 2008
+Duration: 1 day
+All Day
+Recurring
+""",
+ ),
+ )
+
+
+ for data, result in data:
+ imip = ServerToIMip(self.DummyScheduler(Component.fromString(data)), [], [])
+ self.assertEqual(imip._getDateTimeInfo(imip.scheduler.calendar.masterComponent()), result)
+
+ def test_calendar_summary(self):
+ data = (
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN="User 01":mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+SUMMARY:This is an event
+END:VEVENT
+END:VCALENDAR
+""",
+ """---- Begin Calendar Event Summary ----
+
+Organizer: User 01 <mailto:user1 at example.com>
+Summary: This is an event
+Starts: Sunday, June 1, 2008 12:00 PM (UTC)
+Ends: Sunday, June 1, 2008 01:00 PM (UTC)
+Duration: 1 hour
+Description:
+
+---- End Calendar Event Summary ----
+""",
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;VALUE=DATE:20080601
+DTEND;VALUE=DATE:20080602
+ORGANIZER;CN="User 02":mailto:user2 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+SUMMARY:This is an event
+DESCRIPTION:Testing iMIP from the calendar server.
+END:VEVENT
+END:VCALENDAR
+""",
+ """---- Begin Calendar Event Summary ----
+
+Organizer: User 02 <mailto:user2 at example.com>
+Summary: This is an event
+Starts: Sunday, June 1, 2008
+Ends: Monday, June 2, 2008
+Duration: 1 day
+All Day
+Description: Testing iMIP from the calendar server.
+
+---- End Calendar Event Summary ----
+""",
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;VALUE=DATE:20080601
+DTEND;VALUE=DATE:20080602
+RRULE:FREQ=YEARLY
+ORGANIZER;CN="User 03":mailto:user3 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+SUMMARY:This is an event
+DESCRIPTION:Testing iMIP from the calendar server.
+END:VEVENT
+END:VCALENDAR
+""",
+ """---- Begin Calendar Event Summary ----
+
+Organizer: User 03 <mailto:user3 at example.com>
+Summary: This is an event
+Starts: Sunday, June 1, 2008
+Ends: Monday, June 2, 2008
+Duration: 1 day
+All Day
+Recurring
+Description: Testing iMIP from the calendar server.
+
+---- End Calendar Event Summary ----
+""",
+ ),
+ )
+
+
+ for data, result in data:
+ imip = ServerToIMip(self.DummyScheduler(Component.fromString(data)), [], [])
+ self.assertEqual(imip._generateCalendarSummary(imip.scheduler.calendar), result)
+
+
+ def test_template_message(self):
+ data = (
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+SUMMARY:This is an event
+END:VEVENT
+END:VCALENDAR
+""",
+ """From: ${fromaddress}
+To: ${toaddress}
+Subject: DO NOT REPLY: calendar invitation test
+Mime-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="boundary"
+
+
+--boundary
+Content-Type: text/plain
+
+Hi,
+You've been invited to a cool event by CalendarServer's new iMIP processor.
+
+---- Begin Calendar Event Summary ----
+
+Organizer: User 01 <mailto:user1 at example.com>
+Summary: This is an event
+Starts: Sunday, June 1, 2008 12:00 PM (UTC)
+Ends: Sunday, June 1, 2008 01:00 PM (UTC)
+Duration: 1 hour
+Description:
+
+---- End Calendar Event Summary ----
+
+
+--boundary
+Content-Type: text/calendar; charset=utf-8
+Content-Transfer-Encoding: 7bit
+
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DTEND:20080601T130000Z
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 01:mailto:user1 at example.com
+SUMMARY:This is an event
+END:VEVENT
+END:VCALENDAR
+
+--boundary--
+""",
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;VALUE=DATE:20080601
+DTEND;VALUE=DATE:20080602
+ORGANIZER;CN=User 02:mailto:user2 at example.com
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+SUMMARY:This is an event
+DESCRIPTION:Testing iMIP from the calendar server.
+END:VEVENT
+END:VCALENDAR
+""",
+ """From: ${fromaddress}
+To: ${toaddress}
+Subject: DO NOT REPLY: calendar invitation test
+Mime-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="boundary"
+
+
+--boundary
+Content-Type: text/plain
+
+Hi,
+You've been invited to a cool event by CalendarServer's new iMIP processor.
+
+---- Begin Calendar Event Summary ----
+
+Organizer: User 02 <mailto:user2 at example.com>
+Summary: This is an event
+Starts: Sunday, June 1, 2008
+Ends: Monday, June 2, 2008
+Duration: 1 day
+All Day
+Description: Testing iMIP from the calendar server.
+
+---- End Calendar Event Summary ----
+
+
+--boundary
+Content-Type: text/calendar; charset=utf-8
+Content-Transfer-Encoding: 7bit
+
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;VALUE=DATE:20080601
+DTEND;VALUE=DATE:20080602
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+DESCRIPTION:Testing iMIP from the calendar server.
+ORGANIZER;CN=User 02:mailto:user2 at example.com
+SUMMARY:This is an event
+END:VEVENT
+END:VCALENDAR
+
+--boundary--
+""",
+ ),
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;VALUE=DATE:20080601
+DTEND;VALUE=DATE:20080602
+RRULE:FREQ=YEARLY
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN=User 03:mailto:user3 at example.com
+SUMMARY:This is an event
+DESCRIPTION:Testing iMIP from the calendar server.
+END:VEVENT
+END:VCALENDAR
+""",
+ """From: ${fromaddress}
+To: ${toaddress}
+Subject: DO NOT REPLY: calendar invitation test
+Mime-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="boundary"
+
+
+--boundary
+Content-Type: text/plain
+
+Hi,
+You've been invited to a cool event by CalendarServer's new iMIP processor.
+
+---- Begin Calendar Event Summary ----
+
+Organizer: User 03 <mailto:user3 at example.com>
+Summary: This is an event
+Starts: Sunday, June 1, 2008
+Ends: Monday, June 2, 2008
+Duration: 1 day
+All Day
+Recurring
+Description: Testing iMIP from the calendar server.
+
+---- End Calendar Event Summary ----
+
+
+--boundary
+Content-Type: text/calendar; charset=utf-8
+Content-Transfer-Encoding: 7bit
+
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART;VALUE=DATE:20080601
+DTEND;VALUE=DATE:20080602
+ATTENDEE:mailto:user1 at example.com
+ATTENDEE:mailto:user2 at example.com
+DESCRIPTION:Testing iMIP from the calendar server.
+ORGANIZER;CN=User 03:mailto:user3 at example.com
+RRULE:FREQ=YEARLY
+SUMMARY:This is an event
+END:VEVENT
+END:VCALENDAR
+
+--boundary--
+""",
+ ),
+ )
+
+ def _normalizeMessage(text):
+ # First get rid of unwanted headers
+ lines = text.split("\n")
+ lines = [line for line in lines if line.split(":")[0] not in ("Date", "Message-ID",)]
+
+ # Now get rid of boundary string
+ boundary = None
+ newlines = []
+ for line in lines:
+ if line.startswith(" boundary=\""):
+ boundary = line[len(" boundary=\""):-1]
+ line = line.replace(boundary, "boundary")
+ if boundary and line.find(boundary) != -1:
+ line = line.replace(boundary, "boundary")
+ newlines.append(line)
+ return "\n".join(newlines)
+
+ for data, result in data:
+ imip = ServerToIMip(self.DummyScheduler(Component.fromString(data)), [], [])
+ self.assertEqual(_normalizeMessage(imip._generateTemplateMessage(imip.scheduler.calendar)), result)
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080613/1c055f58/attachment-0001.htm
More information about the calendarserver-changes
mailing list