summaryrefslogtreecommitdiffstats
path: root/wallace/module_resources.py
diff options
context:
space:
mode:
authorThomas Bruederli <bruederli@kolabsys.com>2014-02-24 18:53:10 +0100
committerThomas Bruederli <bruederli@kolabsys.com>2014-02-24 19:59:45 +0100
commit87e2d218a0851fe2a87d167ee9a6ee7e2ff6c6d0 (patch)
tree6104462b95c06ba2af933a04f5c7752ac5d8d619 /wallace/module_resources.py
parent99ba571d7c17f1c6966bce6aafe0d8d4a268837e (diff)
downloadpykolab-87e2d218a0851fe2a87d167ee9a6ee7e2ff6c6d0.tar.gz
Make wallace/module_resources pass the unit tests
- support non-multipart iTip messages - fix sending of delegated itip replies - simplify attendee finding
Diffstat (limited to 'wallace/module_resources.py')
-rw-r--r--wallace/module_resources.py140
1 files changed, 74 insertions, 66 deletions
diff --git a/wallace/module_resources.py b/wallace/module_resources.py
index 7d45216..8b0fccb 100644
--- a/wallace/module_resources.py
+++ b/wallace/module_resources.py
@@ -452,93 +452,100 @@ def itip_events_from_message(message):
"""
# Placeholder for any itip_events found in the message.
itip_events = []
+ seen_uids = []
# iTip methods we are actually interested in. Other methods will be ignored.
itip_methods = [ "REQUEST", "REPLY", "ADD", "CANCEL" ]
- # TODO: Are all iTip messages multipart? RFC 6047, section 2.4 states "A
+ # Are all iTip messages multipart? No! RFC 6047, section 2.4 states "A
# MIME body part containing content information that conforms to this
# document MUST have (...)" but does not state whether an iTip message must
# therefore also be multipart.
- if message.is_multipart():
- # Check each part
- for part in message.walk():
-
- # The iTip part MUST be Content-Type: text/calendar (RFC 6047,
- # section 2.4)
- if part.get_content_type() == "text/calendar":
- if not part.get_param('method') in itip_methods:
- log.error(
- _("Method %r not really interesting for us.") % (
- part.get_param('method')
- )
- )
- # Get the itip_payload
- itip_payload = part.get_payload(decode=True)
+ # Check each part
+ for part in message.walk():
- log.debug(_("Raw iTip payload: %s") % (itip_payload))
+ # The iTip part MUST be Content-Type: text/calendar (RFC 6047, section 2.4)
+ # But in real word, other mime-types are used as well
+ if part.get_content_type() in [ "text/calendar", "text/x-vcalendar", "application/ics" ]:
+ if not part.get_param('method') in itip_methods:
+ log.error(
+ _("Method %r not really interesting for us.") % (
+ part.get_param('method')
+ )
+ )
- # Python iCalendar prior to 3.0 uses "from_string".
- if hasattr(icalendar.Calendar, 'from_ical'):
- cal = icalendar.Calendar.from_ical(itip_payload)
- elif hasattr(icalendar.Calendar, 'from_string'):
- cal = icalendar.Calendar.from_string(itip_payload)
+ # Get the itip_payload
+ itip_payload = part.get_payload(decode=True)
- # If we can't read it, we're out
- else:
- log.error(_("Could not read iTip from message."))
- return []
+ log.debug(_("Raw iTip payload: %s") % (itip_payload))
- for c in cal.walk():
- if c.name == "VEVENT":
- itip = {}
+ # Python iCalendar prior to 3.0 uses "from_string".
+ if hasattr(icalendar.Calendar, 'from_ical'):
+ cal = icalendar.Calendar.from_ical(itip_payload)
+ elif hasattr(icalendar.Calendar, 'from_string'):
+ cal = icalendar.Calendar.from_string(itip_payload)
- # From the event, take the following properties:
- #
- # - start
- # - end (if any)
- # - duration (if any)
- # - organizer
- # - attendees (if any)
- # - resources (if any)
- # - TODO: recurrence rules (if any)
- # Where are these stored actually?
- #
+ # If we can't read it, we're out
+ else:
+ log.error(_("Could not read iTip from message."))
+ return []
+
+ for c in cal.walk():
+ if c.name == "VEVENT":
+ itip = {}
+
+ if c['uid'] in seen_uids:
+ log.debug(_("Duplicate iTip event: %s") % (c['uid']))
+ continue
- if c.has_key('dtstart'):
- itip['start'] = c['dtstart']
- else:
- log.error(_("iTip event without a start"))
- continue
+ # From the event, take the following properties:
+ #
+ # - start
+ # - end (if any)
+ # - duration (if any)
+ # - organizer
+ # - attendees (if any)
+ # - resources (if any)
+ # - TODO: recurrence rules (if any)
+ # Where are these stored actually?
+ #
- if c.has_key('dtend'):
- itip['end'] = c['dtend']
+ if c.has_key('dtstart'):
+ itip['start'] = c['dtstart']
+ else:
+ log.error(_("iTip event without a start"))
+ continue
- if c.has_key('duration'):
- itip['duration'] = c['duration']
+ if c.has_key('dtend'):
+ itip['end'] = c['dtend']
- itip['organizer'] = c['organizer']
+ if c.has_key('duration'):
+ itip['duration'] = c['duration']
- itip['attendees'] = c['attendee']
+ itip['organizer'] = c['organizer']
- if c.has_key('resources'):
- itip['resources'] = c['resources']
+ itip['attendees'] = c['attendee']
- itip['raw'] = itip_payload
- itip['xml'] = event_from_ical(c.to_ical())
+ if c.has_key('resources'):
+ itip['resources'] = c['resources']
- itip_events.append(itip)
+ itip['raw'] = itip_payload
+ itip['xml'] = event_from_ical(c.to_ical())
- # end if c.name == "VEVENT"
+ itip_events.append(itip)
- # end for c in cal.walk()
+ seen_uids.append(c['uid'])
- # end if part.get_content_type() == "text/calendar"
+ # end if c.name == "VEVENT"
- # end for part in message.walk()
+ # end for c in cal.walk()
- else: # if message.is_multipart()
+ # end if part.get_content_type() == "text/calendar"
+
+ # end for part in message.walk()
+
+ if not len(itip_events) and not message.is_multipart():
log.debug(_("Message is not an iTip message (non-multipart message)"), level=5)
return itip_events
@@ -741,20 +748,21 @@ def send_response(from_address, itip_events):
itip_events = [ itip_events ]
for itip_event in itip_events:
- attendee = [a for a in itip_event['xml'].get_attendees() if a.get_email() == from_address][0]
+ attendee = itip_event['xml'].get_attendee(from_address)
participant_status = itip_event['xml'].get_ical_attendee_participant_status(attendee)
if participant_status == "DELEGATED":
# Extra actions to take
- delegator = [a for a in itip_event['xml'].get_attendees() if a.get_email() == from_address][0]
+ delegator = itip_event['xml'].get_attendee(from_address)
delegatee = [a for a in itip_event['xml'].get_attendees() if from_address in [b.email() for b in a.get_delegated_from()]][0]
- itip_event['xml'].event.setAttendees([ delegator, delegatee ])
-
message = itip_event['xml'].to_message_itip(delegatee.get_email(), method="REPLY", participant_status=itip_event['xml'].get_ical_attendee_participant_status(delegatee))
smtp.sendmail(message['From'], message['To'], message.as_string())
- itip_event['xml'].event.setAttendees([a for a in itip_event['xml'].get_attendees() if a.get_email() == from_address])
+ # restore list of attendees after to_message_itip()
+ itip_event['xml']._attendees = [ delegator, delegatee ]
+ itip_event['xml'].event.setAttendees(itip_event['xml']._attendees)
+ participant_status = "DELEGATED"
message = itip_event['xml'].to_message_itip(from_address, method="REPLY", participant_status=participant_status)
smtp.sendmail(message['From'], message['To'], message.as_string())