# HG changeset patch # User Paul Boddie # Date 1413927595 -7200 # Node ID 1c65f50b9c1df060637e8e12dc6a9f19e571ed9e # Parent e1e6ca8ec42b701966d73be67408d78ae99b69b2 Moved the processing framework into the package, exposing only configuration details in the agent script. diff -r e1e6ca8ec42b -r 1c65f50b9c1d imip_agent.py --- a/imip_agent.py Tue Oct 21 19:58:20 2014 +0200 +++ b/imip_agent.py Tue Oct 21 23:39:55 2014 +0200 @@ -1,136 +1,8 @@ #!/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 imiptools.content import handle_itip_part +from imiptools import Processor import imip_resource -import sys - -MESSAGE_SENDER = "resources+agent@example.com" - -MESSAGE_SUBJECT = "Calendar system message" - -MESSAGE_TEXT = """\ -This is a response to a calendar message sent by your calendar program. -""" - -# Postfix exit codes. - -EX_TEMPFAIL = 75 - -# Permitted iTIP content types. - -itip_content_types = [ - "text/calendar", # from RFC 6047 - "text/x-vcalendar", "application/ics", # other possibilities - ] - -# Sending of outgoing messages. - -def sendmail(sender, recipients, data): - smtp = SMTP("localhost") - smtp.sendmail(sender, recipients, data) - smtp.quit() - -# Processing of incoming messages. - -def process(f, original_recipients, recipients, this_sender): - - """ - Process content from the stream 'f' accompanied by the given - 'original_recipients' and 'recipients', together with details of - 'this_sender' (the sender address on messages sent by this service). - """ - - msg = message_from_file(f) - senders = msg.get_all("Reply-To") or msg.get_all("From") - original_recipients = original_recipients or get_all_values(msg, "To") - - # 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"): - - # NOTE: Act on behalf of resources for now. - - all_parts += handle_itip_part(part, original_recipients, imip_resource.handlers) - - # Pack the parts into a single message. - if all_parts: - text_part = MIMEText(MESSAGE_TEXT) - all_parts.insert(0, text_part) - message = MIMEMultipart("alternative", _subparts=all_parts) - message.preamble = MESSAGE_TEXT - - message["From"] = this_sender - for sender in senders: - message["To"] = sender - message["Subject"] = MESSAGE_SUBJECT - - if "-d" in sys.argv: - print message - else: - sendmail(this_sender, senders, message.as_string()) - -def get_all_values(msg, key): - l = [] - for v in msg.get_all(key) or []: - l += [s.strip() for s in v.split(",")] - return l - -def main(): - - "Interpret program arguments and process input." - - # Obtain the different kinds of recipients plus sender address. - - original_recipients = [] - recipients = [] - senders = [] - - l = [] - - for arg in sys.argv[1:]: - - # Switch to collecting recipients. - - if arg == "-o": - l = original_recipients - elif arg == "-r": - l = recipients - - # Switch to collecting senders. - - elif arg == "-s": - l = senders - - # Ignore debugging options. - - elif arg == "-d": - pass - else: - l.append(arg) - - process(sys.stdin, original_recipients, recipients, senders and senders[0] or MESSAGE_SENDER) - -if __name__ == "__main__": - if "-d" in sys.argv[1:]: - main() - else: - try: - main() - except SystemExit, value: - sys.exit(value) - except Exception, exc: - type, value, tb = sys.exc_info() - print >>sys.stderr, "Exception %s at %d" % (exc, tb.tb_lineno) - sys.exit(EX_TEMPFAIL) - sys.exit(0) +Processor(imip_resource.handlers)() # vim: tabstop=4 expandtab shiftwidth=4 diff -r e1e6ca8ec42b -r 1c65f50b9c1d imiptools/__init__.py --- a/imiptools/__init__.py Tue Oct 21 19:58:20 2014 +0200 +++ b/imiptools/__init__.py Tue Oct 21 23:39:55 2014 +0200 @@ -0,0 +1,165 @@ +#!/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 imiptools.content import handle_itip_part +import imip_resource +import sys + +MESSAGE_SENDER = "resources+agent@example.com" + +MESSAGE_SUBJECT = "Calendar system message" + +MESSAGE_TEXT = """\ +This is a response to a calendar message sent by your calendar program. +""" + +# Postfix exit codes. + +EX_TEMPFAIL = 75 + +# Permitted iTIP content types. + +itip_content_types = [ + "text/calendar", # from RFC 6047 + "text/x-vcalendar", "application/ics", # other possibilities + ] + +# Sending of outgoing messages. + +def sendmail(sender, recipients, data): + smtp = SMTP("localhost") + smtp.sendmail(sender, recipients, data) + smtp.quit() + +# Processing of incoming messages. + +def get_all_values(msg, key): + l = [] + for v in msg.get_all(key) or []: + l += [s.strip() for s in v.split(",")] + return l + +class Processor: + + "The processing framework." + + def __init__(self, handlers, sender=None, subject=None, body_text=None): + self.handlers = handlers + self.sender = sender or MESSAGE_SENDER + self.subject = subject or MESSAGE_SUBJECT + self.body_text = body_text or MESSAGE_TEXT + + def process(self, f, original_recipients, recipients): + + """ + Process content from the stream 'f' accompanied by the given + 'original_recipients' and 'recipients'. + """ + + msg = message_from_file(f) + senders = msg.get_all("Reply-To") or msg.get_all("From") + original_recipients = original_recipients or get_all_values(msg, "To") + + # 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"): + + # NOTE: Act on behalf of resources for now. + + all_parts += handle_itip_part(part, original_recipients, self.handlers) + + # Pack the parts into a single message. + + if all_parts: + message = self.make_message(all_parts, senders) + + if "-d" in sys.argv: + print message + else: + sendmail(self.sender, senders, message.as_string()) + + def make_message(self, parts, recipients): + + "Make a message from the given 'parts' for the given 'recipients'." + + message = MIMEMultipart("alternative", _subparts=parts) + message.preamble = self.body_text + payload = message.get_payload() + payload.insert(0, MIMEText(self.body_text)) + + message["From"] = self.sender + for recipient in recipients: + message["To"] = recipient + message["Subject"] = self.subject + + return message + + def process_args(self, args, stream): + + """ + Interpret the given program arguments 'args' and process input from the + given 'stream'. + """ + + # Obtain the different kinds of recipients plus sender address. + + original_recipients = [] + recipients = [] + senders = [] + + l = [] + + for arg in args: + + # Switch to collecting recipients. + + if arg == "-o": + l = original_recipients + elif arg == "-r": + l = recipients + + # Switch to collecting senders. + + elif arg == "-s": + l = senders + + # Ignore debugging options. + + elif arg == "-d": + pass + else: + l.append(arg) + + self.sender = senders and senders[0] or self.sender + self.process(stream, original_recipients, recipients) + + def __call__(self): + + """ + Obtain arguments from the command line to initialise the processor + before invoking it. + """ + + args = sys.argv[1:] + + if "-d" in args: + self.process_args(args, sys.stdin) + else: + try: + self.process_args(args, sys.stdin) + except SystemExit, value: + sys.exit(value) + except Exception, exc: + type, value, tb = sys.exc_info() + print >>sys.stderr, "Exception %s at %d" % (exc, tb.tb_lineno) + sys.exit(EX_TEMPFAIL) + sys.exit(0) + +# vim: tabstop=4 expandtab shiftwidth=4