1.1 --- a/imiptools/__init__.py Wed Oct 22 15:46:50 2014 +0200
1.2 +++ b/imiptools/__init__.py Thu Oct 23 22:24:17 2014 +0200
1.3 @@ -3,7 +3,7 @@
1.4 from email import message_from_file
1.5 from email.mime.multipart import MIMEMultipart
1.6 from email.mime.text import MIMEText
1.7 -from smtplib import SMTP
1.8 +from smtplib import LMTP, SMTP
1.9 from imiptools.content import handle_itip_part
1.10 import sys
1.11
1.12 @@ -28,8 +28,11 @@
1.13
1.14 # Sending of outgoing messages.
1.15
1.16 -def sendmail(sender, recipients, data):
1.17 - smtp = SMTP("localhost")
1.18 +def sendmail(sender, recipients, data, lmtp_socket=None):
1.19 + if lmtp_socket:
1.20 + smtp = LMTP(lmtp_socket)
1.21 + else:
1.22 + smtp = SMTP("localhost")
1.23 smtp.sendmail(sender, recipients, data)
1.24 smtp.quit()
1.25
1.26 @@ -50,6 +53,7 @@
1.27 self.sender = sender or MESSAGE_SENDER
1.28 self.subject = subject or MESSAGE_SUBJECT
1.29 self.body_text = body_text or MESSAGE_TEXT
1.30 + self.lmtp_socket = None
1.31
1.32 def process(self, f, original_recipients, recipients):
1.33
1.34 @@ -64,25 +68,55 @@
1.35
1.36 # Handle messages with iTIP parts.
1.37
1.38 - all_parts = []
1.39 + all_responses = []
1.40 + handled = False
1.41
1.42 for part in msg.walk():
1.43 if part.get_content_type() in itip_content_types and \
1.44 part.get_param("method"):
1.45
1.46 - all_parts += handle_itip_part(part, original_recipients, self.handlers)
1.47 + all_responses += handle_itip_part(part, original_recipients, self.handlers)
1.48 + handled = True
1.49
1.50 # Pack any returned parts into a single message.
1.51
1.52 - if all_parts:
1.53 - message = self.make_message(all_parts, senders)
1.54 + if all_responses:
1.55 + outgoing_parts = []
1.56 + forwarded_parts = []
1.57 +
1.58 + for outgoing, part in all_responses:
1.59 + if outgoing:
1.60 + outgoing_parts.append(part)
1.61 + else:
1.62 + forwarded_parts.append(part)
1.63 +
1.64 + # Reply using any outgoing parts in a new message.
1.65 +
1.66 + if outgoing_parts:
1.67 + message = self.make_message(outgoing_parts, senders)
1.68
1.69 + if "-d" in sys.argv:
1.70 + print message
1.71 + else:
1.72 + sendmail(self.sender, senders, message.as_string())
1.73 +
1.74 + # Forward messages to their recipients using the existing message.
1.75 +
1.76 + if forwarded_parts:
1.77 + msg.get_payload()[:0] = forwarded_parts
1.78 +
1.79 + if "-d" in sys.argv:
1.80 + print msg
1.81 + elif self.lmtp_socket:
1.82 + sendmail(self.sender, original_recipients, msg.as_string(), self.lmtp_socket)
1.83 +
1.84 + # Unhandled messages are delivered as they are.
1.85 +
1.86 + if not handled:
1.87 if "-d" in sys.argv:
1.88 - print message
1.89 - else:
1.90 - sendmail(self.sender, senders, message.as_string())
1.91 -
1.92 - # Forward messages to their recipients.
1.93 + print msg
1.94 + elif self.lmtp_socket:
1.95 + sendmail(self.sender, original_recipients, msg.as_string(), self.lmtp_socket)
1.96
1.97 def make_message(self, parts, recipients):
1.98
1.99 @@ -112,6 +146,7 @@
1.100 original_recipients = []
1.101 recipients = []
1.102 senders = []
1.103 + lmtp = []
1.104
1.105 l = []
1.106
1.107 @@ -129,6 +164,11 @@
1.108 elif arg == "-s":
1.109 l = senders
1.110
1.111 + # Switch to getting the LMTP socket.
1.112 +
1.113 + elif arg == "-l":
1.114 + l = lmtp
1.115 +
1.116 # Ignore debugging options.
1.117
1.118 elif arg == "-d":
1.119 @@ -137,6 +177,7 @@
1.120 l.append(arg)
1.121
1.122 self.sender = senders and senders[0] or self.sender
1.123 + self.lmtp_socket = lmtp and lmtp[0] or None
1.124 self.process(stream, original_recipients, recipients)
1.125
1.126 def __call__(self):
1.127 @@ -156,6 +197,8 @@
1.128 except SystemExit, value:
1.129 sys.exit(value)
1.130 except Exception, exc:
1.131 + if "-v" in args:
1.132 + raise
1.133 type, value, tb = sys.exc_info()
1.134 print >>sys.stderr, "Exception %s at %d" % (exc, tb.tb_lineno)
1.135 sys.exit(EX_TEMPFAIL)
2.1 --- a/imiptools/content.py Wed Oct 22 15:46:50 2014 +0200
2.2 +++ b/imiptools/content.py Thu Oct 23 22:24:17 2014 +0200
2.3 @@ -142,7 +142,8 @@
2.4
2.5 """
2.6 Handle the given iTIP 'part' for the given 'recipients' using the given
2.7 - 'handlers'.
2.8 + 'handlers'. Return a list of responses, each response being a tuple of the
2.9 + form (is-outgoing, message-part).
2.10 """
2.11
2.12 method = part.get_param("method")
2.13 @@ -158,17 +159,13 @@
2.14 if not itip:
2.15 return []
2.16
2.17 - # Only handle calendar information.
2.18 -
2.19 - all_parts = []
2.20 -
2.21 # Require consistency between declared and employed methods.
2.22
2.23 if get_value(itip, "METHOD") == method:
2.24
2.25 # Look for different kinds of sections.
2.26
2.27 - all_objects = []
2.28 + all_results = []
2.29
2.30 for name, cls in handlers:
2.31 for details in get_values(itip, name) or []:
2.32 @@ -176,19 +173,18 @@
2.33 # Dispatch to a handler and obtain any response.
2.34
2.35 handler = cls(details, recipients)
2.36 - object = methods[method](handler)()
2.37 + result = methods[method](handler)()
2.38
2.39 # Concatenate responses for a single calendar object.
2.40
2.41 - if object:
2.42 - all_objects += object
2.43 -
2.44 - # Obtain a message part for the objects.
2.45 + if result:
2.46 + response_method, part = result
2.47 + outgoing = method != response_method
2.48 + all_results.append((outgoing, part))
2.49
2.50 - if all_objects:
2.51 - all_parts.append(to_part(response_methods[method], all_objects))
2.52 + return all_results
2.53
2.54 - return all_parts
2.55 + return []
2.56
2.57 def parse_object(f, encoding, objtype):
2.58
2.59 @@ -337,8 +333,4 @@
2.60 "REQUEST" : lambda handler: handler.request,
2.61 }
2.62
2.63 -response_methods = {
2.64 - "REQUEST" : "REPLY",
2.65 - }
2.66 -
2.67 # vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/imiptools/handlers/person.py Wed Oct 22 15:46:50 2014 +0200
3.2 +++ b/imiptools/handlers/person.py Thu Oct 23 22:24:17 2014 +0200
3.3 @@ -4,7 +4,8 @@
3.4 Handlers for a person for whom scheduling is performed.
3.5 """
3.6
3.7 -from imiptools.content import Handler
3.8 +from email.mime.text import MIMEText
3.9 +from imiptools.content import Handler, to_part
3.10 from vCalendar import to_node
3.11
3.12 class Event(Handler):
3.13 @@ -71,6 +72,8 @@
3.14
3.15 # The message is now wrapped and passed on to the recipient.
3.16
3.17 + return "REQUEST", MIMEText("A request has been queued.")
3.18 +
3.19 class Freebusy(Handler):
3.20
3.21 "A free/busy handler."
3.22 @@ -110,22 +113,22 @@
3.23 for attendee, attendee_attr in attendees.items():
3.24 freebusy = self.store.get_freebusy(attendee)
3.25
3.26 - if freebusy:
3.27 - record = []
3.28 - rwrite = record.append
3.29 + record = []
3.30 + rwrite = record.append
3.31
3.32 - rwrite(("ORGANIZER", organiser_attr, organiser))
3.33 - rwrite(("ATTENDEE", attendee_attr, attendee))
3.34 - rwrite(("UID", {}, self.uid))
3.35 + rwrite(("ORGANIZER", organiser_attr, organiser))
3.36 + rwrite(("ATTENDEE", attendee_attr, attendee))
3.37 + rwrite(("UID", {}, self.uid))
3.38
3.39 + if freebusy:
3.40 for start, end, uid in freebusy:
3.41 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, [start, end]))
3.42
3.43 - cwrite(("VFREEBUSY", {}, record))
3.44 + cwrite(("VFREEBUSY", {}, record))
3.45
3.46 # Return the reply.
3.47
3.48 - return calendar
3.49 + return "REPLY", to_part("REPLY", calendar)
3.50
3.51 class Journal(Handler):
3.52
4.1 --- a/imiptools/handlers/resource.py Wed Oct 22 15:46:50 2014 +0200
4.2 +++ b/imiptools/handlers/resource.py Thu Oct 23 22:24:17 2014 +0200
4.3 @@ -5,7 +5,7 @@
4.4 """
4.5
4.6 from datetime import date, datetime, timedelta
4.7 -from imiptools.content import Handler, format_datetime
4.8 +from imiptools.content import Handler, format_datetime, to_part
4.9 from imiptools.period import insert_period, period_overlaps, remove_period
4.10 from vCalendar import to_node
4.11 from vRecurrence import get_parameters, get_rule
4.12 @@ -152,7 +152,7 @@
4.13 {"VEVENT" : [(self.details, {})]}
4.14 ))
4.15
4.16 - return calendar
4.17 + return "REPLY", to_part("REPLY", calendar)
4.18
4.19 class Freebusy(Handler):
4.20
4.21 @@ -190,22 +190,22 @@
4.22 for attendee, attendee_attr in attendees.items():
4.23 freebusy = self.store.get_freebusy(attendee)
4.24
4.25 - if freebusy:
4.26 - record = []
4.27 - rwrite = record.append
4.28 + record = []
4.29 + rwrite = record.append
4.30
4.31 - rwrite(("ORGANIZER", organiser_attr, organiser))
4.32 - rwrite(("ATTENDEE", attendee_attr, attendee))
4.33 - rwrite(("UID", {}, self.uid))
4.34 + rwrite(("ORGANIZER", organiser_attr, organiser))
4.35 + rwrite(("ATTENDEE", attendee_attr, attendee))
4.36 + rwrite(("UID", {}, self.uid))
4.37
4.38 + if freebusy:
4.39 for start, end, uid in freebusy:
4.40 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, [start, end]))
4.41
4.42 - cwrite(("VFREEBUSY", {}, record))
4.43 + cwrite(("VFREEBUSY", {}, record))
4.44
4.45 # Return the reply.
4.46
4.47 - return calendar
4.48 + return "REPLY", to_part("REPLY", calendar)
4.49
4.50 class Journal(Handler):
4.51