3 commits - pykolab/xml tests/functional tests/unit wallace/module_invitationpolicy.py wallace/module_resources.py
Thomas Brüderli
bruederli at kolabsys.com
Fri Jul 18 15:26:37 CEST 2014
pykolab/xml/__init__.py | 1
pykolab/xml/event.py | 73 ++++
tests/functional/test_wallace/test_005_resource_invitation.py | 18 -
tests/functional/test_wallace/test_007_invitationpolicy.py | 27 +
tests/unit/test-003-event.py | 162 ++++++----
tests/unit/test-012-wallace_invitationpolicy.py | 2
wallace/module_invitationpolicy.py | 43 +-
wallace/module_resources.py | 39 +-
8 files changed, 250 insertions(+), 115 deletions(-)
New commits:
commit a674289628249f45ec46c3096b1c476692943ff3
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Jul 7 18:43:25 2014 -0400
Use new pykolab.xml.event_from_message() function in wallace modules and verify that attachments survive event updates
diff --git a/tests/functional/test_wallace/test_005_resource_invitation.py b/tests/functional/test_wallace/test_005_resource_invitation.py
index 946cb5f..d9f2d41 100644
--- a/tests/functional/test_wallace/test_005_resource_invitation.py
+++ b/tests/functional/test_wallace/test_005_resource_invitation.py
@@ -8,6 +8,7 @@ import uuid
from pykolab.imap import IMAP
from wallace import module_resources
+from pykolab.xml import event_from_message
from email import message_from_string
from twisted.trial import unittest
@@ -23,7 +24,7 @@ CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
UID:%s
-DTSTAMP:20140213T1254140
+DTSTAMP:20140213T125414Z
DTSTART;TZID=Europe/London:%s
DTEND;TZID=Europe/London:%s
SUMMARY:test
@@ -43,7 +44,7 @@ CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
UID:%s
-DTSTAMP:20140215T1254140
+DTSTAMP:20140215T125414Z
DTSTART;TZID=Europe/London:%s
DTEND;TZID=Europe/London:%s
SEQUENCE:2
@@ -90,7 +91,7 @@ CALSCALE:GREGORIAN
METHOD:CANCEL
BEGIN:VEVENT
UID:%s
-DTSTAMP:20140218T1254140
+DTSTAMP:20140218T125414Z
DTSTART;TZID=Europe/London:20120713T100000
DTEND;TZID=Europe/London:20120713T110000
SUMMARY:test
@@ -112,7 +113,7 @@ CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
UID:%s
-DTSTAMP:20140213T1254140
+DTSTAMP:20140213T125414Z
DTSTART;VALUE=DATE:%s
DTEND;VALUE=DATE:%s
SUMMARY:test
@@ -133,7 +134,7 @@ CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
UID:%s
-DTSTAMP:20140213T1254140
+DTSTAMP:20140213T125414Z
DTSTART;TZID=Europe/Zurich:%s
DTEND;TZID=Europe/Zurich:%s
RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10
@@ -328,12 +329,7 @@ class TestResourceInvitation(unittest.TestCase):
if uid and event_message['subject'] != uid:
continue
- for part in event_message.walk():
- if part.get_content_type() == "application/calendar+xml":
- payload = part.get_payload(decode=True)
- found = pykolab.xml.event_from_string(payload)
- break
-
+ found = event_from_message(event_message)
if found:
break
diff --git a/tests/functional/test_wallace/test_007_invitationpolicy.py b/tests/functional/test_wallace/test_007_invitationpolicy.py
index 4fd61a7..3b68aef 100644
--- a/tests/functional/test_wallace/test_007_invitationpolicy.py
+++ b/tests/functional/test_wallace/test_007_invitationpolicy.py
@@ -11,6 +11,7 @@ from pykolab.imap import IMAP
from wallace import module_resources
from pykolab.translate import _
+from pykolab.xml import event_from_message
from email import message_from_string
from twisted.trial import unittest
@@ -26,7 +27,7 @@ CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
UID:%(uid)s
-DTSTAMP:20140213T1254140
+DTSTAMP:20140213T125414Z
DTSTART;TZID=Europe/Berlin:%(start)s
DTEND;TZID=Europe/Berlin:%(end)s
SUMMARY:%(summary)s
@@ -48,7 +49,7 @@ CALSCALE:GREGORIAN
METHOD:CANCEL
BEGIN:VEVENT
UID:%(uid)s
-DTSTAMP:20140218T1254140
+DTSTAMP:20140218T125414Z
DTSTART;TZID=Europe/Berlin:20120713T100000
DTEND;TZID=Europe/Berlin:20120713T110000
SUMMARY:%(summary)s
@@ -69,7 +70,7 @@ CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
UID:%(uid)s
-DTSTAMP:20140213T1254140
+DTSTAMP:20140213T125414Z
DTSTART;TZID=Europe/Zurich:%(start)s
DTEND;TZID=Europe/Zurich:%(end)s
RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10
@@ -295,6 +296,14 @@ class TestWallaceInvitationpolicy(unittest.TestCase):
event.set_summary(summary)
event.set_sequence(sequence)
+ # create event with attachment
+ vattach = event.get_attachments()
+ attachment = kolabformat.Attachment()
+ attachment.setLabel('attach.txt')
+ attachment.setData('This is a text attachment', 'text/plain')
+ vattach.append(attachment)
+ event.event.setAttachments(vattach)
+
imap = IMAP()
imap.connect()
@@ -365,12 +374,7 @@ class TestWallaceInvitationpolicy(unittest.TestCase):
if uid and event_message['subject'] != uid:
continue
- for part in event_message.walk():
- if part.get_content_type() == "application/calendar+xml":
- payload = part.get_payload(decode=True)
- found = pykolab.xml.event_from_string(payload)
- break
-
+ found = event_from_message(event_message)
if found:
break
@@ -506,6 +510,11 @@ class TestWallaceInvitationpolicy(unittest.TestCase):
self.assertIsInstance(attendee, pykolab.xml.Attendee)
self.assertEqual(attendee.get_participant_status(), kolabformat.PartAccepted)
+ # check attachments in update event
+ attachments = event.get_attachments()
+ self.assertEqual(len(attachments), 1)
+ self.assertEqual(event.get_attachment_data(0), 'This is a text attachment')
+
def test_007_invitation_cancel(self):
self.purge_mailbox(self.john['mailbox'])
diff --git a/wallace/module_invitationpolicy.py b/wallace/module_invitationpolicy.py
index a141251..7375d2d 100644
--- a/wallace/module_invitationpolicy.py
+++ b/wallace/module_invitationpolicy.py
@@ -39,7 +39,7 @@ from pykolab.auth import Auth
from pykolab.conf import Conf
from pykolab.imap import IMAP
from pykolab.xml import to_dt
-from pykolab.xml import event_from_string
+from pykolab.xml import event_from_message
from pykolab.itip import events_from_message
from pykolab.itip import check_event_conflict
from pykolab.itip import send_reply
@@ -619,15 +619,12 @@ def find_existing_event(itip_event, user_rec):
for num in reversed(data[0].split()):
typ, data = imap.imap.m.fetch(num, '(RFC822)')
- event_message = message_from_string(data[0][1])
-
- if event_message.is_multipart():
- for part in event_message.walk():
- if part.get_content_type() == "application/calendar+xml":
- payload = part.get_payload(decode=True)
- event = event_from_string(payload)
- setattr(event, '_imap_folder', folder)
- break
+ try:
+ event = event_from_message(message_from_string(data[0][1]))
+ setattr(event, '_imap_folder', folder)
+ except Exception, e:
+ log.error(_("Failed to parse event from message %s/%s: %r") % (folder, num, e))
+ continue
if event and event.uid == itip_event['uid']:
return event
@@ -659,20 +656,18 @@ def check_availability(itip_event, receiving_user):
event = None
typ, data = imap.imap.m.fetch(num, '(RFC822)')
- event_message = message_from_string(data[0][1])
-
- if event_message.is_multipart():
- for part in event_message.walk():
- if part.get_content_type() == "application/calendar+xml":
- payload = part.get_payload(decode=True)
- event = event_from_string(payload)
- break
-
- if event and event.uid:
- conflict = check_event_conflict(event, itip_event)
- if conflict:
- log.info(_("Existing event %r conflicts with invitation %r") % (event.uid, itip_event['uid']))
- break
+ try:
+ event = event_from_message(message_from_string(data[0][1]))
+ setattr(event, '_imap_folder', folder)
+ except Exception, e:
+ log.error(_("Failed to parse event from message %s/%s: %r") % (folder, num, e))
+ continue
+
+ if event and event.uid:
+ conflict = check_event_conflict(event, itip_event)
+ if conflict:
+ log.info(_("Existing event %r conflicts with invitation %r") % (event.uid, itip_event['uid']))
+ break
if conflict:
break
diff --git a/wallace/module_resources.py b/wallace/module_resources.py
index f398120..3864f7c 100644
--- a/wallace/module_resources.py
+++ b/wallace/module_resources.py
@@ -40,8 +40,8 @@ import kolabformat
from pykolab.auth import Auth
from pykolab.conf import Conf
from pykolab.imap import IMAP
-from pykolab.xml import event_from_string
from pykolab.xml import to_dt
+from pykolab.xml import event_from_message
from pykolab.itip import events_from_message
from pykolab.itip import check_event_conflict
from pykolab.translate import _
@@ -467,28 +467,29 @@ def read_resource_calendar(resource_rec, itip_events):
event_message = message_from_string(data[0][1])
- if event_message.is_multipart():
- for part in event_message.walk():
- if part.get_content_type() == "application/calendar+xml":
- payload = part.get_payload(decode=True)
- event = pykolab.xml.event_from_string(payload)
+ try:
+ event = event_from_message(message_from_string(data[0][1]))
+ except Exception, e:
+ log.error(_("Failed to parse event from message %s/%s: %r") % (mailbox, num, e))
+ continue
- for itip in itip_events:
- conflict = check_event_conflict(event, itip)
+ if event:
+ for itip in itip_events:
+ conflict = check_event_conflict(event, itip)
- if event.get_uid() == itip['uid']:
- resource_rec['existing_events'].append(itip['uid'])
+ if event.get_uid() == itip['uid']:
+ resource_rec['existing_events'].append(itip['uid'])
- if conflict:
- log.info(
- _("Event %r conflicts with event %r") % (
- itip['xml'].get_uid(),
- event.get_uid()
- )
- )
+ if conflict:
+ log.info(
+ _("Event %r conflicts with event %r") % (
+ itip['xml'].get_uid(),
+ event.get_uid()
+ )
+ )
- resource_rec['conflicting_events'].append(event.get_uid())
- resource_rec['conflict'] = True
+ resource_rec['conflicting_events'].append(event.get_uid())
+ resource_rec['conflict'] = True
return num_messages
commit 6e137a89f6f29975133633e2e70557b3954d4220
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Jul 7 18:29:00 2014 -0400
Add getter for event attachment data
diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index 39034f6..f9b6487 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -303,6 +303,22 @@ class Event(object):
def get_attachments(self):
return self.event.attachments()
+ def get_attachment_data(self, i):
+ vattach = self.event.attachments()
+ if i < len(vattach):
+ attachment = vattach[i]
+ uri = attachment.uri()
+ if uri and uri[0:4] == 'cid:':
+ # get data from MIME part with matching content-id
+ cid = '<' + uri[4:] + '>'
+ for p in self._attachment_parts:
+ if p['Content-ID'] == cid:
+ return p.get_payload(decode=True)
+ else:
+ return attachment.data()
+
+ return None
+
def get_alarms(self):
return self.event.alarms()
diff --git a/tests/unit/test-003-event.py b/tests/unit/test-003-event.py
index 363c75e..81337d9 100644
--- a/tests/unit/test-003-event.py
+++ b/tests/unit/test-003-event.py
@@ -389,6 +389,7 @@ METHOD:REQUEST
self.assertEqual(parts[3]['Content-ID'].strip('<>'), attachments[0].uri()[4:])
self.assertEqual(parts[4].get_content_type(), 'text/plain')
self.assertEqual(parts[4]['Content-ID'].strip('<>'), attachments[1].uri()[4:])
+ self.assertEqual(event.get_attachment_data(1), 'This is a text file')
def test_024_bogus_itip_data(self):
# DTSTAMP contains an invalid date/time value
commit 5de26915e7b66db1dd139b1b6d33687f7e9dfc66
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Jul 7 17:40:54 2014 -0400
Fix event object reading and writing: store attachments as separate MIME parts and forward them to the new MIME message written
diff --git a/pykolab/xml/__init__.py b/pykolab/xml/__init__.py
index 5ca2837..64b06ae 100644
--- a/pykolab/xml/__init__.py
+++ b/pykolab/xml/__init__.py
@@ -9,6 +9,7 @@ from event import EventIntegrityError
from event import InvalidEventDateError
from event import event_from_ical
from event import event_from_string
+from event import event_from_message
from utils import to_dt
diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index fcb3a17..39034f6 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -6,6 +6,7 @@ import kolabformat
import pytz
import time
import uuid
+import base64
import pykolab
from pykolab import constants
@@ -13,6 +14,7 @@ from pykolab import utils
from pykolab.xml import utils as xmlutils
from pykolab.translate import _
+from os import path
from attendee import Attendee
from contact_reference import ContactReference
@@ -24,6 +26,21 @@ def event_from_ical(string):
def event_from_string(string):
return Event(from_string=string)
+def event_from_message(message):
+ event = None
+ if message.is_multipart():
+ for part in message.walk():
+ if part.get_content_type() == "application/calendar+xml":
+ payload = part.get_payload(decode=True)
+ event = event_from_string(payload)
+
+ # append attachment parts to Event object
+ elif event and part.has_key('Content-ID'):
+ event._attachment_parts.append(part)
+
+ return event
+
+
class Event(object):
status_map = {
"TENTATIVE": kolabformat.StatusTentative,
@@ -40,6 +57,7 @@ class Event(object):
def __init__(self, from_ical="", from_string=""):
self._attendees = []
self._categories = []
+ self._attachment_parts = []
if from_ical == "":
if from_string == "":
@@ -751,15 +769,50 @@ class Event(object):
msg["Subject"] = self.get_uid()
- part.set_payload(str(self))
+ # extract attachment data into separate MIME parts
+ vattach = self.event.attachments()
+ i = 0
+ for attach in vattach:
+ if attach.uri():
+ continue
+
+ mimetype = attach.mimetype()
+ (primary, seconday) = mimetype.split('/')
+ name = attach.label()
+ if not name:
+ name = 'unknown.x'
- # TODO: extract attachment data to separate MIME parts
+ (basename, suffix) = path.splitext(name)
+ t = datetime.datetime.now()
+ cid = "%s.%s.%s%s" % (basename, time.mktime(t.timetuple()), t.microsecond + len(self._attachment_parts), suffix)
+
+ p = MIMEBase(primary, seconday)
+ p.add_header('Content-Disposition', 'attachment', filename=name)
+ p.add_header('Content-Transfer-Encoding', 'base64')
+ p.add_header('Content-ID', '<' + cid + '>')
+ p.set_payload(base64.b64encode(attach.data()))
+
+ self._attachment_parts.append(p)
+
+ # modify attachment object
+ attach.setData('', mimetype)
+ attach.setUri('cid:' + cid, mimetype)
+ vattach[i] = attach
+ i += 1
+
+ self.event.setAttachments(vattach)
+
+ part.set_payload(str(self))
part.add_header('Content-Disposition', 'attachment; filename="kolab.xml"')
part.replace_header('Content-Transfer-Encoding', '8bit')
msg.attach(part)
+ # append attachment parts
+ for p in self._attachment_parts:
+ msg.attach(p)
+
return msg
def to_message_itip(self, from_address, method="REQUEST", participant_status="ACCEPTED", subject=None, message_text=None):
diff --git a/tests/unit/test-003-event.py b/tests/unit/test-003-event.py
index f9ef92e..363c75e 100644
--- a/tests/unit/test-003-event.py
+++ b/tests/unit/test-003-event.py
@@ -12,6 +12,58 @@ from pykolab.xml import InvalidAttendeeParticipantStatusError
from pykolab.xml import InvalidEventDateError
from pykolab.xml import event_from_ical
from pykolab.xml import event_from_string
+from pykolab.xml import event_from_message
+
+ical_event = """
+BEGIN:VEVENT
+UID:7a35527d-f783-4b58-b404-b1389bd2fc57
+DTSTAMP;VALUE=DATE-TIME:20140407T122311Z
+CREATED;VALUE=DATE-TIME:20140407T122245Z
+LAST-MODIFIED;VALUE=DATE-TIME:20140407T122311Z
+DTSTART;TZID=Europe/Zurich;VALUE=DATE-TIME:20140523T110000
+DURATION:PT1H30M0S
+RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10
+EXDATE;TZID=Europe/Zurich;VALUE=DATE-TIME:20140530T110000
+EXDATE;TZID=Europe/Zurich;VALUE=DATE-TIME:20140620T110000
+SUMMARY:Summary
+LOCATION:Location
+DESCRIPTION:Description\\n2 lines
+CATEGORIES:Personal
+TRANSP:OPAQUE
+PRIORITY:2
+SEQUENCE:2
+CLASS:PUBLIC
+ATTENDEE;CN="Manager, Jane";PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;CUTYP
+ E=INDIVIDUAL;RSVP=TRUE:mailto:jane.manager at example.org
+ATTENDEE;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;ROLE=OPT-PARTICIPANT;RSVP=FA
+ LSE:MAILTO:max at imum.com
+ORGANIZER;CN=Doe\, John:mailto:john.doe at example.org
+URL:http://somelink.com/foo
+ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=image/png;X-LABEL=silhouette.pn
+ g:iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAIAAADY27xgAAAAGXRFWHRTb2Z0d2FyZQBBZG9i
+ ZSBJbWFnZVJlYWR5ccllPAAAAsRJREFUeNrsmeluKjEMhTswrAWB4P3fECGx79CjsTDmOKRkpF
+ xxpfoHSmchX7ybFrfb7eszpPH1MfKH8ofyH6KUtd/c7/en0wmfWBdF0Wq1Op1Ou91uNGoer6iX
+ V1ar1Xa7xUJeB4qsr9frdyVlWWZH2VZyPp+xPXHIAoK70+m02+1m9JXj8bhcLi+Xi3J4xUCazS
+ bUltdtd7ud7ldUIhC3u+iTwF0sFhlR4Kds4LtRZK1w4te5UM6V6JaqhqC3CQ28OAsKggJfbZ3U
+ eozCqZ4koHIZCGmD9ivuos9YONFirmxrI0UNZG1kbZeUXdJQNJNa91RlqMn0ekYUMZDup6dXVV
+ m+1OSZhqLx6bVCELJGSsyFQtFrF15JGYMZgoxubWGDSDVhvTipDKWhoBOIpFobxtlbJ0Gh0/tg
+ lgXal4woUHi/36fQoBQncDAlupa8DeVwOPRe4lUyGAwQ+dl7W+xBXkJBhEUqR32UoJfYIKrR4d
+ ZBgcdIRqfEqn+mekl9FNRbSTA249la3ev1/kXHD47ZbEYR5L9kMplkd9vNZqMFyIYxxfN8Pk8q
+ QGlagT5QDtfrNYUMlWW9LiGNPPSmC/+OgpK2r4RO6dOatZd+4gAAemdIi6Fg9EKLD4vASWkzv3
+ ew06NSCiA40CumAIoaIrhrcAwjF7aDo58gUchgNV+0n1BAcDgcoAZrXV9mI4qkhtK6FJFhi9Fo
+ ZKPsgQI1ACJieH/Kd570t+xFoIzHYzl5Q40CFGrSqGuks3qmYIKJfIl0nPKLxAMFw7Dv1+2QYf
+ vFSOBQubbOFDSc7ZcfWvHv6DzhOzT6IeOVPuz8Roex0f6EgsE/2IL4qdg7hIXz7/pBie7q1uWr
+ tp66xrif0l1KwUE4P7Y9Gci/ZgtNRFX+Rw06Q2RigsjuDc3urwKHxuNITaaxyD9mT2WvSDAXn/
+ Pvhh8BBgBjyfPSGbSYcwAAAABJRU5ErkJggg==
+ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=text/plain;X-LABEL=text.txt:VGh
+ pcyBpcyBhIHRleHQgZmlsZQo=
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER:-PT30M
+END:VALARM
+END:VEVENT
+"""
+
class TestEventXML(unittest.TestCase):
event = Event()
@@ -122,55 +174,8 @@ PRODID:-//Roundcube//Roundcube libcalendaring 1.1-git//Sabre//Sabre VObject
2.1.3//EN
CALSCALE:GREGORIAN
METHOD:REQUEST
-BEGIN:VEVENT
-UID:7a35527d-f783-4b58-b404-b1389bd2fc57
-DTSTAMP;VALUE=DATE-TIME:20140407T122311Z
-CREATED;VALUE=DATE-TIME:20140407T122245Z
-LAST-MODIFIED;VALUE=DATE-TIME:20140407T122311Z
-DTSTART;TZID=Europe/Zurich;VALUE=DATE-TIME:20140523T110000
-DURATION:PT1H30M0S
-RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=10
-EXDATE;TZID=Europe/Zurich;VALUE=DATE-TIME:20140530T110000
-EXDATE;TZID=Europe/Zurich;VALUE=DATE-TIME:20140620T110000
-SUMMARY:Summary
-LOCATION:Location
-DESCRIPTION:Description\\n2 lines
-CATEGORIES:Personal
-TRANSP:OPAQUE
-PRIORITY:2
-SEQUENCE:2
-CLASS:PUBLIC
-ATTENDEE;CN="Manager, Jane";PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;CUTYP
- E=INDIVIDUAL;RSVP=TRUE:mailto:jane.manager at example.org
-ATTENDEE;CUTYPE=RESOURCE;PARTSTAT=NEEDS-ACTION;ROLE=OPT-PARTICIPANT;RSVP=FA
- LSE:MAILTO:max at imum.com
-ORGANIZER;CN=Doe\, John:mailto:john.doe at example.org
-URL:http://somelink.com/foo
-ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=image/png;X-LABEL=silhouette.pn
- g:iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAIAAADY27xgAAAAGXRFWHRTb2Z0d2FyZQBBZG9i
- ZSBJbWFnZVJlYWR5ccllPAAAAsRJREFUeNrsmeluKjEMhTswrAWB4P3fECGx79CjsTDmOKRkpF
- xxpfoHSmchX7ybFrfb7eszpPH1MfKH8ofyH6KUtd/c7/en0wmfWBdF0Wq1Op1Ou91uNGoer6iX
- V1ar1Xa7xUJeB4qsr9frdyVlWWZH2VZyPp+xPXHIAoK70+m02+1m9JXj8bhcLi+Xi3J4xUCazS
- bUltdtd7ud7ldUIhC3u+iTwF0sFhlR4Kds4LtRZK1w4te5UM6V6JaqhqC3CQ28OAsKggJfbZ3U
- eozCqZ4koHIZCGmD9ivuos9YONFirmxrI0UNZG1kbZeUXdJQNJNa91RlqMn0ekYUMZDup6dXVV
- m+1OSZhqLx6bVCELJGSsyFQtFrF15JGYMZgoxubWGDSDVhvTipDKWhoBOIpFobxtlbJ0Gh0/tg
- lgXal4woUHi/36fQoBQncDAlupa8DeVwOPRe4lUyGAwQ+dl7W+xBXkJBhEUqR32UoJfYIKrR4d
- ZBgcdIRqfEqn+mekl9FNRbSTA249la3ev1/kXHD47ZbEYR5L9kMplkd9vNZqMFyIYxxfN8Pk8q
- QGlagT5QDtfrNYUMlWW9LiGNPPSmC/+OgpK2r4RO6dOatZd+4gAAemdIi6Fg9EKLD4vASWkzv3
- ew06NSCiA40CumAIoaIrhrcAwjF7aDo58gUchgNV+0n1BAcDgcoAZrXV9mI4qkhtK6FJFhi9Fo
- ZKPsgQI1ACJieH/Kd570t+xFoIzHYzl5Q40CFGrSqGuks3qmYIKJfIl0nPKLxAMFw7Dv1+2QYf
- vFSOBQubbOFDSc7ZcfWvHv6DzhOzT6IeOVPuz8Roex0f6EgsE/2IL4qdg7hIXz7/pBie7q1uWr
- tp66xrif0l1KwUE4P7Y9Gci/ZgtNRFX+Rw06Q2RigsjuDc3urwKHxuNITaaxyD9mT2WvSDAXn/
- Pvhh8BBgBjyfPSGbSYcwAAAABJRU5ErkJggg==
-ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=text/plain;X-LABEL=text.txt:VGh
- pcyBpcyBhIHRleHQgZmlsZQo=
-BEGIN:VALARM
-ACTION:DISPLAY
-TRIGGER:-PT30M
-END:VALARM
-END:VEVENT
-END:VCALENDAR
-"""
+ """ + ical_event + "END:VCALENDAR"
+
ical = icalendar.Calendar.from_ical(ical_str)
event = event_from_ical(ical.walk('VEVENT')[0].to_ical())
@@ -193,6 +198,26 @@ END:VCALENDAR
self.assertEqual(len(event.get_alarms()), 1)
self.assertEqual(len(event.get_attachments()), 2)
+ def test_018_ical_to_message(self):
+ event = event_from_ical(ical_event)
+ message = event.to_message()
+
+ self.assertTrue(message.is_multipart())
+ self.assertEqual(message['Subject'], event.uid)
+ self.assertEqual(message['X-Kolab-Type'], 'application/x-vnd.kolab.event')
+
+ parts = [p for p in message.walk()]
+ attachments = event.get_attachments();
+
+ self.assertEqual(len(parts), 5)
+ self.assertEqual(parts[1].get_content_type(), 'text/plain')
+ self.assertEqual(parts[2].get_content_type(), 'application/calendar+xml')
+ self.assertEqual(parts[3].get_content_type(), 'image/png')
+ self.assertEqual(parts[4].get_content_type(), 'text/plain')
+ self.assertEqual(parts[2]['Content-ID'], None)
+ self.assertEqual(parts[3]['Content-ID'].strip('<>'), attachments[0].uri()[4:])
+ self.assertEqual(parts[4]['Content-ID'].strip('<>'), attachments[1].uri()[4:])
+
def test_019_as_string_itip(self):
self.event.set_summary("test")
self.event.set_start(datetime.datetime(2014, 05, 23, 11, 00, 00, tzinfo=pytz.timezone("Europe/London")))
@@ -348,6 +373,44 @@ END:VCALENDAR
self.assertIsInstance(event.get_start(), datetime.datetime)
self.assertEqual(str(event.get_start()), "2014-08-13 10:00:00+00:00")
+ def test_023_load_from_message(self):
+ event = event_from_message(event_from_ical(ical_event).to_message())
+ event.set_sequence(3)
+
+ message = event.to_message()
+ self.assertTrue(message.is_multipart())
+
+ # check attachment MIME parts are kept
+ parts = [p for p in message.walk()]
+ attachments = event.get_attachments();
+
+ self.assertEqual(len(parts), 5)
+ self.assertEqual(parts[3].get_content_type(), 'image/png')
+ self.assertEqual(parts[3]['Content-ID'].strip('<>'), attachments[0].uri()[4:])
+ self.assertEqual(parts[4].get_content_type(), 'text/plain')
+ self.assertEqual(parts[4]['Content-ID'].strip('<>'), attachments[1].uri()[4:])
+
+ def test_024_bogus_itip_data(self):
+ # DTSTAMP contains an invalid date/time value
+ vevent = """BEGIN:VEVENT
+UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271
+DTSTAMP:20120713T1254140
+DTSTART;TZID=Europe/London:20120713T100000
+DTEND;TZID=Europe/London:20120713T110000
+SUMMARY:test
+DESCRIPTION:test
+ORGANIZER;CN="Doe, John":mailto:john.doe at example.org
+ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailt
+ o:jane.doe at example.org
+ATTENDEE;ROLE=OPT-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailt
+ o:user.external at example.com
+SEQUENCE:1
+TRANSP:OPAQUE
+END:VEVENT
+"""
+ event = event_from_ical(vevent)
+ self.assertRaises(EventIntegrityError, event.to_message)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/unit/test-012-wallace_invitationpolicy.py b/tests/unit/test-012-wallace_invitationpolicy.py
index 75939d0..650879b 100644
--- a/tests/unit/test-012-wallace_invitationpolicy.py
+++ b/tests/unit/test-012-wallace_invitationpolicy.py
@@ -44,7 +44,7 @@ CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
UID:626421779C777FBE9C9B85A80D04DDFA-A4BF5BBB9FEAA271
-DTSTAMP:20120713T1254140
+DTSTAMP:20120713T125414Z
DTSTART;TZID=3DEurope/London:20120713T100000
DTEND;TZID=3DEurope/London:20120713T110000
SUMMARY:test
More information about the commits
mailing list