1.1 --- a/imip_agent.py Mon Sep 22 22:51:29 2014 +0200
1.2 +++ b/imip_agent.py Tue Sep 23 00:03:39 2014 +0200
1.3 @@ -1,6 +1,7 @@
1.4 #!/usr/bin/env python
1.5
1.6 from email import message_from_file
1.7 +from email.mime.multipart import MIMEMultipart
1.8 from email.mime.text import MIMEText
1.9 from smtplib import SMTP
1.10 from vCalendar import parse, iterwrite, ParseError, SECTION_TYPES
1.11 @@ -12,6 +13,8 @@
1.12 except ImportError:
1.13 from StringIO import StringIO
1.14
1.15 +OWNER = "resource+manager@example.com"
1.16 +
1.17 # Postfix exit codes.
1.18
1.19 EX_USAGE = 64
1.20 @@ -54,19 +57,31 @@
1.21 """
1.22
1.23 msg = message_from_file(f)
1.24 - print >>open("/tmp/imip.txt", "a"), "----"
1.25 - print >>open("/tmp/imip.txt", "a"), original_recipients, recipients
1.26 - print >>open("/tmp/imip.txt", "a"), "----"
1.27 - print >>open("/tmp/imip.txt", "a"), msg.as_string()
1.28 - print >>open("/tmp/imip.txt", "a")
1.29 + sender = msg.get("Reply-To") or msg["From"]
1.30
1.31 # Handle messages with iTIP parts.
1.32
1.33 + all_parts = []
1.34 +
1.35 for part in msg.walk():
1.36 if part.get_content_type() in itip_content_types and \
1.37 part.get_param("method"):
1.38
1.39 - handle_itip_part(part, original_recipients)
1.40 + all_parts += handle_itip_part(part, original_recipients)
1.41 +
1.42 + # Pack the parts into a single message.
1.43 +
1.44 + if all_parts:
1.45 + if len(all_parts) > 1:
1.46 + message = MIMEMultipart("alternative", _subparts=all_parts)
1.47 + else:
1.48 + message = all_parts[0]
1.49 +
1.50 + message["From"] = OWNER
1.51 + message["To"] = sender
1.52 +
1.53 + if "-d" in sys.argv:
1.54 + print message
1.55
1.56 def get_itip_elements(elements):
1.57 d = {}
1.58 @@ -118,7 +133,23 @@
1.59 def get_uri(value):
1.60 return value.startswith("mailto:") and value or "mailto:%s" % value
1.61
1.62 +def to_part(method, calendar):
1.63 + out = StringIO()
1.64 + try:
1.65 + w = iterwrite(out, encoding="utf-8")
1.66 + calendar[:0] = [
1.67 + ("METHOD", {}, method),
1.68 + ("VERSION", {}, "2.0")
1.69 + ]
1.70 + w.write("VCALENDAR", {}, calendar)
1.71 + return MIMEText(out.getvalue(), "calendar", "utf-8")
1.72 + finally:
1.73 + out.close()
1.74 +
1.75 def handle_itip_part(part, recipients):
1.76 +
1.77 + "Handle the given iTIP 'part' for the given 'recipients'."
1.78 +
1.79 method = part.get_param("method")
1.80
1.81 # Decode the data and parse it.
1.82 @@ -132,6 +163,8 @@
1.83
1.84 # Only handle calendar information.
1.85
1.86 + all_parts = []
1.87 +
1.88 if doctype == "VCALENDAR":
1.89 itip = get_itip_elements(elements)
1.90
1.91 @@ -141,16 +174,27 @@
1.92
1.93 # Look for different kinds of sections.
1.94
1.95 + all_objects = []
1.96 +
1.97 for name, cls in handlers:
1.98 for details in get_values(itip, name) or []:
1.99
1.100 # Dispatch to a handler and obtain any response.
1.101
1.102 handler = cls(details, recipients)
1.103 - part = methods[method](handler)()
1.104 + object = methods[method](handler)()
1.105 +
1.106 + # Concatenate responses for a single calendar object.
1.107 +
1.108 + if object:
1.109 + all_objects += object
1.110
1.111 - if part:
1.112 - print >>open("/tmp/imip.txt", "a"), part.as_string()
1.113 + # Obtain a message part for the objects.
1.114 +
1.115 + if all_objects:
1.116 + all_parts.append(to_part(method, all_objects))
1.117 +
1.118 + return all_parts
1.119
1.120 class Handler:
1.121
1.122 @@ -235,8 +279,8 @@
1.123 def request(self):
1.124
1.125 """
1.126 - Respond to a request by sending a reply containing free/busy information
1.127 - for each indicated attendee.
1.128 + Respond to a request by preparing a reply containing free/busy
1.129 + information for each indicated attendee.
1.130 """
1.131
1.132 attendee_map = self.get_value_map("ATTENDEE")
1.133 @@ -251,39 +295,32 @@
1.134
1.135 organiser, organiser_attr = organiser
1.136
1.137 - # Get the details for the attendee.
1.138 + # Construct an appropriate fragment.
1.139 +
1.140 + calendar = []
1.141 + cwrite = calendar.append
1.142
1.143 - out = StringIO()
1.144 - try:
1.145 - w = iterwrite(out, encoding="utf-8")
1.146 + # Get the details for each attendee.
1.147
1.148 - calendar = []
1.149 - cwrite = calendar.append
1.150 - cwrite(("METHOD", {}, "REPLY"))
1.151 - cwrite(("VERSION", {}, "2.0"))
1.152 + for attendee in attendees:
1.153 + freebusy = self.store.get_freebusy(attendee)
1.154
1.155 - for attendee in attendees:
1.156 - freebusy = self.store.get_freebusy(attendee)
1.157 - if freebusy:
1.158 - record = []
1.159 - rwrite = record.append
1.160 - rwrite(("ORGANIZER", organiser_attr, organiser))
1.161 - rwrite(("ATTENDEE", attendee_map[attendee], attendee))
1.162 - rwrite(("UID", {}, self.uid))
1.163 + if freebusy:
1.164 + record = []
1.165 + rwrite = record.append
1.166 +
1.167 + rwrite(("ORGANIZER", organiser_attr, organiser))
1.168 + rwrite(("ATTENDEE", attendee_map[attendee], attendee))
1.169 + rwrite(("UID", {}, self.uid))
1.170
1.171 - for start, end in freebusy:
1.172 - rwrite(("FREEBUSY", {}, [start, end]))
1.173 -
1.174 - cwrite(("VFREEBUSY", {}, record))
1.175 -
1.176 - # Send a reply with the information.
1.177 + for start, end in freebusy:
1.178 + rwrite(("FREEBUSY", {}, [start, end]))
1.179
1.180 - w.write("VCALENDAR", {}, calendar)
1.181 + cwrite(("VFREEBUSY", {}, record))
1.182
1.183 - return MIMEText(out.getvalue(), "calendar", "utf-8")
1.184 + # Return the reply.
1.185
1.186 - finally:
1.187 - out.close()
1.188 + return calendar
1.189
1.190 class Journal(Handler):
1.191