Support MIME multi-part messages.
[ottawa-travel-planner.git] / mailPlanner.py
blob0e5d537dcf4807c52e4c014f33d0ff507bdba2e9
1 #!/usr/bin/python
2 # vi: set softtabstop=4 shiftwidth=4 tabstop=8 expandtab:
4 import email.FeedParser
5 import email.iterators
6 from email.MIMEText import MIMEText
7 from email.Utils import parseaddr
8 import smtplib
9 import sys
10 import traceback
11 import syslog
13 import Planner
14 import PlanTime
15 import time
16 import CommandParser
17 import ShortFormatter
19 FROM_ADDRESS = "tp@hurts.ca"
20 SMTP_SERVER = "127.0.0.1"
22 def logIt(string):
23 syslog.syslog(syslog.LOG_INFO, string)
25 def sendMessage(msg, to):
26 msg['From'] = FROM_ADDRESS
27 msg['To'] = to
29 s = smtplib.SMTP(SMTP_SERVER)
30 s.sendmail(parseaddr(FROM_ADDRESS)[1], [parseaddr(msg['To'])[1]],
31 msg.as_string())
32 s.close()
34 def sendError(e, replyto):
35 sendMessage(MIMEText(e.args[0]), replyto)
37 def findCommand(msg):
38 for line in msg.get_payload().splitlines():
39 s = line.strip()
40 if len(s) != 0:
41 logIt(s)
42 return CommandParser.CommandParser(s).cmd
43 return None
45 def handleMessage(msg, replyto):
46 try:
47 if not msg.is_multipart():
48 cmd = findCommand(msg)
49 else:
50 # Look for a text/plain part.
51 for subpart in email.iterators.typed_subpart_iterator(msg, "text",
52 "plain"):
53 cmd = findCommand(subpart)
54 if cmd is not None:
55 break
56 else:
57 # No text/plain sub-parts
58 raise Exception("Please send a plain text e-mail (no HTML).")
60 if cmd is None:
61 logIt("Empty command from %s" % replyto)
62 return 0
64 # Leave in 3 minutes if time is unspecified.
65 if cmd.time is None:
66 cmd.time = PlanTime.PlanTime(time.time() + 180,
67 PlanTime.MUST_LEAVE_AFTER)
69 itin = Planner.plan(cmd.start, cmd.end, cmd.time)
70 sf = ShortFormatter.ShortFormatter(itin.entries)
72 text = "\n".join(sf.lines)
73 if itin.anyUnparsed():
74 text += "\nWarning: This itinerary display may be incomplete!"
76 logIt("success, %d entries" % len(itin.entries))
77 sendMessage(MIMEText(text), replyto)
78 except Exception, e:
79 logIt("Exception: %s" % e)
80 logIt(traceback.format_exc())
81 sendError(e, replyto)
83 # Always return success. Otherwise Postfix will send a bounce message.
84 return 0
86 def main():
87 fp = email.FeedParser.FeedParser()
88 for line in sys.stdin:
89 fp.feed(line)
90 msg = fp.close()
92 # if these headers are here, we can reply semi-intelligently with errors
93 replyto = msg["reply-to"] or msg["from"]
94 return handleMessage(msg, replyto)
96 if __name__ == '__main__':
97 syslog.openlog(sys.argv[0], syslog.LOG_DAEMON)
98 sys.exit(main())