# HG changeset patch # User Paul Boddie # Date 1411423419 -7200 # Node ID 72b7602bf5315f318ef4d6ce039e0ac16590afeb # Parent b62b34a78ad02693dc838a17ac4170aa6b905685 Return fragments from handlers so that they may be combined into single calendar objects, converted into message parts, and such parts then combined into single messages. diff -r b62b34a78ad0 -r 72b7602bf531 imip_agent.py --- a/imip_agent.py Mon Sep 22 22:51:29 2014 +0200 +++ b/imip_agent.py Tue Sep 23 00:03:39 2014 +0200 @@ -1,6 +1,7 @@ #!/usr/bin/env python from email import message_from_file +from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from smtplib import SMTP from vCalendar import parse, iterwrite, ParseError, SECTION_TYPES @@ -12,6 +13,8 @@ except ImportError: from StringIO import StringIO +OWNER = "resource+manager@example.com" + # Postfix exit codes. EX_USAGE = 64 @@ -54,19 +57,31 @@ """ msg = message_from_file(f) - print >>open("/tmp/imip.txt", "a"), "----" - print >>open("/tmp/imip.txt", "a"), original_recipients, recipients - print >>open("/tmp/imip.txt", "a"), "----" - print >>open("/tmp/imip.txt", "a"), msg.as_string() - print >>open("/tmp/imip.txt", "a") + sender = msg.get("Reply-To") or msg["From"] # Handle messages with iTIP parts. + all_parts = [] + for part in msg.walk(): if part.get_content_type() in itip_content_types and \ part.get_param("method"): - handle_itip_part(part, original_recipients) + all_parts += handle_itip_part(part, original_recipients) + + # Pack the parts into a single message. + + if all_parts: + if len(all_parts) > 1: + message = MIMEMultipart("alternative", _subparts=all_parts) + else: + message = all_parts[0] + + message["From"] = OWNER + message["To"] = sender + + if "-d" in sys.argv: + print message def get_itip_elements(elements): d = {} @@ -118,7 +133,23 @@ def get_uri(value): return value.startswith("mailto:") and value or "mailto:%s" % value +def to_part(method, calendar): + out = StringIO() + try: + w = iterwrite(out, encoding="utf-8") + calendar[:0] = [ + ("METHOD", {}, method), + ("VERSION", {}, "2.0") + ] + w.write("VCALENDAR", {}, calendar) + return MIMEText(out.getvalue(), "calendar", "utf-8") + finally: + out.close() + def handle_itip_part(part, recipients): + + "Handle the given iTIP 'part' for the given 'recipients'." + method = part.get_param("method") # Decode the data and parse it. @@ -132,6 +163,8 @@ # Only handle calendar information. + all_parts = [] + if doctype == "VCALENDAR": itip = get_itip_elements(elements) @@ -141,16 +174,27 @@ # Look for different kinds of sections. + all_objects = [] + for name, cls in handlers: for details in get_values(itip, name) or []: # Dispatch to a handler and obtain any response. handler = cls(details, recipients) - part = methods[method](handler)() + object = methods[method](handler)() + + # Concatenate responses for a single calendar object. + + if object: + all_objects += object - if part: - print >>open("/tmp/imip.txt", "a"), part.as_string() + # Obtain a message part for the objects. + + if all_objects: + all_parts.append(to_part(method, all_objects)) + + return all_parts class Handler: @@ -235,8 +279,8 @@ def request(self): """ - Respond to a request by sending a reply containing free/busy information - for each indicated attendee. + Respond to a request by preparing a reply containing free/busy + information for each indicated attendee. """ attendee_map = self.get_value_map("ATTENDEE") @@ -251,39 +295,32 @@ organiser, organiser_attr = organiser - # Get the details for the attendee. + # Construct an appropriate fragment. + + calendar = [] + cwrite = calendar.append - out = StringIO() - try: - w = iterwrite(out, encoding="utf-8") + # Get the details for each attendee. - calendar = [] - cwrite = calendar.append - cwrite(("METHOD", {}, "REPLY")) - cwrite(("VERSION", {}, "2.0")) + for attendee in attendees: + freebusy = self.store.get_freebusy(attendee) - for attendee in attendees: - freebusy = self.store.get_freebusy(attendee) - if freebusy: - record = [] - rwrite = record.append - rwrite(("ORGANIZER", organiser_attr, organiser)) - rwrite(("ATTENDEE", attendee_map[attendee], attendee)) - rwrite(("UID", {}, self.uid)) + if freebusy: + record = [] + rwrite = record.append + + rwrite(("ORGANIZER", organiser_attr, organiser)) + rwrite(("ATTENDEE", attendee_map[attendee], attendee)) + rwrite(("UID", {}, self.uid)) - for start, end in freebusy: - rwrite(("FREEBUSY", {}, [start, end])) - - cwrite(("VFREEBUSY", {}, record)) - - # Send a reply with the information. + for start, end in freebusy: + rwrite(("FREEBUSY", {}, [start, end])) - w.write("VCALENDAR", {}, calendar) + cwrite(("VFREEBUSY", {}, record)) - return MIMEText(out.getvalue(), "calendar", "utf-8") + # Return the reply. - finally: - out.close() + return calendar class Journal(Handler):