paul@0 | 1 | #!/usr/bin/env python |
paul@0 | 2 | |
paul@0 | 3 | from email import message_from_file |
paul@12 | 4 | from email.mime.multipart import MIMEMultipart |
paul@8 | 5 | from email.mime.text import MIMEText |
paul@8 | 6 | from smtplib import SMTP |
paul@48 | 7 | from imiptools.content import handle_itip_part |
paul@48 | 8 | import imip_resource |
paul@0 | 9 | import sys |
paul@0 | 10 | |
paul@27 | 11 | MESSAGE_SENDER = "resources+agent@example.com" |
paul@12 | 12 | |
paul@19 | 13 | MESSAGE_SUBJECT = "Calendar system message" |
paul@19 | 14 | |
paul@19 | 15 | MESSAGE_TEXT = """\ |
paul@19 | 16 | This is a response to a calendar message sent by your calendar program. |
paul@19 | 17 | """ |
paul@19 | 18 | |
paul@0 | 19 | # Postfix exit codes. |
paul@0 | 20 | |
paul@0 | 21 | EX_TEMPFAIL = 75 |
paul@0 | 22 | |
paul@0 | 23 | # Permitted iTIP content types. |
paul@0 | 24 | |
paul@0 | 25 | itip_content_types = [ |
paul@0 | 26 | "text/calendar", # from RFC 6047 |
paul@0 | 27 | "text/x-vcalendar", "application/ics", # other possibilities |
paul@0 | 28 | ] |
paul@0 | 29 | |
paul@8 | 30 | # Sending of outgoing messages. |
paul@8 | 31 | |
paul@8 | 32 | def sendmail(sender, recipients, data): |
paul@8 | 33 | smtp = SMTP("localhost") |
paul@8 | 34 | smtp.sendmail(sender, recipients, data) |
paul@8 | 35 | smtp.quit() |
paul@8 | 36 | |
paul@8 | 37 | # Processing of incoming messages. |
paul@8 | 38 | |
paul@27 | 39 | def process(f, original_recipients, recipients, this_sender): |
paul@8 | 40 | |
paul@9 | 41 | """ |
paul@9 | 42 | Process content from the stream 'f' accompanied by the given |
paul@27 | 43 | 'original_recipients' and 'recipients', together with details of |
paul@27 | 44 | 'this_sender' (the sender address on messages sent by this service). |
paul@9 | 45 | """ |
paul@8 | 46 | |
paul@0 | 47 | msg = message_from_file(f) |
paul@13 | 48 | senders = msg.get_all("Reply-To") or msg.get_all("From") |
paul@32 | 49 | original_recipients = original_recipients or get_all_values(msg, "To") |
paul@0 | 50 | |
paul@0 | 51 | # Handle messages with iTIP parts. |
paul@0 | 52 | |
paul@12 | 53 | all_parts = [] |
paul@12 | 54 | |
paul@0 | 55 | for part in msg.walk(): |
paul@0 | 56 | if part.get_content_type() in itip_content_types and \ |
paul@0 | 57 | part.get_param("method"): |
paul@0 | 58 | |
paul@48 | 59 | # NOTE: Act on behalf of resources for now. |
paul@48 | 60 | |
paul@48 | 61 | all_parts += handle_itip_part(part, original_recipients, imip_resource.handlers) |
paul@12 | 62 | |
paul@12 | 63 | # Pack the parts into a single message. |
paul@12 | 64 | |
paul@12 | 65 | if all_parts: |
paul@19 | 66 | text_part = MIMEText(MESSAGE_TEXT) |
paul@19 | 67 | all_parts.insert(0, text_part) |
paul@19 | 68 | message = MIMEMultipart("alternative", _subparts=all_parts) |
paul@19 | 69 | message.preamble = MESSAGE_TEXT |
paul@12 | 70 | |
paul@27 | 71 | message["From"] = this_sender |
paul@13 | 72 | for sender in senders: |
paul@13 | 73 | message["To"] = sender |
paul@19 | 74 | message["Subject"] = MESSAGE_SUBJECT |
paul@12 | 75 | |
paul@12 | 76 | if "-d" in sys.argv: |
paul@12 | 77 | print message |
paul@13 | 78 | else: |
paul@27 | 79 | sendmail(this_sender, senders, message.as_string()) |
paul@0 | 80 | |
paul@32 | 81 | def get_all_values(msg, key): |
paul@32 | 82 | l = [] |
paul@48 | 83 | for v in msg.get_all(key) or []: |
paul@32 | 84 | l += [s.strip() for s in v.split(",")] |
paul@32 | 85 | return l |
paul@32 | 86 | |
paul@11 | 87 | def main(): |
paul@11 | 88 | |
paul@27 | 89 | "Interpret program arguments and process input." |
paul@27 | 90 | |
paul@27 | 91 | # Obtain the different kinds of recipients plus sender address. |
paul@9 | 92 | |
paul@11 | 93 | original_recipients = [] |
paul@11 | 94 | recipients = [] |
paul@27 | 95 | senders = [] |
paul@9 | 96 | |
paul@11 | 97 | l = [] |
paul@9 | 98 | |
paul@11 | 99 | for arg in sys.argv[1:]: |
paul@27 | 100 | |
paul@27 | 101 | # Switch to collecting recipients. |
paul@27 | 102 | |
paul@11 | 103 | if arg == "-o": |
paul@11 | 104 | l = original_recipients |
paul@11 | 105 | elif arg == "-r": |
paul@11 | 106 | l = recipients |
paul@27 | 107 | |
paul@27 | 108 | # Switch to collecting senders. |
paul@27 | 109 | |
paul@27 | 110 | elif arg == "-s": |
paul@27 | 111 | l = senders |
paul@27 | 112 | |
paul@27 | 113 | # Ignore debugging options. |
paul@27 | 114 | |
paul@11 | 115 | elif arg == "-d": |
paul@11 | 116 | pass |
paul@11 | 117 | else: |
paul@11 | 118 | l.append(arg) |
paul@11 | 119 | |
paul@27 | 120 | process(sys.stdin, original_recipients, recipients, senders and senders[0] or MESSAGE_SENDER) |
paul@9 | 121 | |
paul@11 | 122 | if __name__ == "__main__": |
paul@11 | 123 | if "-d" in sys.argv[1:]: |
paul@11 | 124 | main() |
paul@9 | 125 | else: |
paul@11 | 126 | try: |
paul@11 | 127 | main() |
paul@11 | 128 | except SystemExit, value: |
paul@11 | 129 | sys.exit(value) |
paul@11 | 130 | except Exception, exc: |
paul@11 | 131 | type, value, tb = sys.exc_info() |
paul@11 | 132 | print >>sys.stderr, "Exception %s at %d" % (exc, tb.tb_lineno) |
paul@11 | 133 | sys.exit(EX_TEMPFAIL) |
paul@11 | 134 | sys.exit(0) |
paul@0 | 135 | |
paul@0 | 136 | # vim: tabstop=4 expandtab shiftwidth=4 |