3 # Copyright (C) 2009 W. Trevor King <wking@drexel.edu>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 """Provide and email interface to the distributed bugtracker Bugs
19 Everywhere. Recieves incoming email via procmail and allows users to
20 select actions with their subject lines. Subject lines follow the
22 [be-bug] command (options) (args)
23 With the body of the email being used as the final argument for the
24 commands "new" and "comment", and ignored otherwise. The options and
25 arguments are split on whitespace, so don't use whitespace inside a
28 Eventually we'll commit after every message.
31 import libbe.cmdutil, libbe.utility
37 SUBJECT_COMMENT = "[be-bug]"
38 HANDLER_ADDRESS = "wking@thor.physics.drexel.edu"
40 ALLOWED_COMMANDS = ["new", "comment", "list", "show", "help"]
42 class InvalidEmail (ValueError):
43 def __init__(self, msg, message):
44 ValueError.__init__(self, message)
47 class InvalidSubject (InvalidEmail):
50 class InvalidCommand (InvalidEmail):
51 def __init__(self, msg, command):
52 message = "Invalid command '%s'" % command
53 ValueError.__init__(self, msg, message)
54 self.command = command
56 def get_body_type(msg):
57 for part in msg.walk():
58 if part.is_multipart():
60 return (part.get_payload(decode=1), part.get_content_type())
62 def run_message(msg_text):
63 encoding = libbe.encoding.get_encoding()
64 libbe.encoding.set_IO_stream_encodings(encoding)
66 p=email.Parser.Parser()
67 msg=p.parsestr(msg_text)
69 if "subject" not in msg:
70 raise InvalidSubject(msg, "Email must contain a subject")
72 id = msg["message-id"]
73 args = msg["subject"].split()
74 if len(args) < 1 or args[0] != SUBJECT_COMMENT:
75 raise InvalidSubject(msg, "Subject must start with '%s '" % SUBJECT_COMMENT)
77 raise InvalidCommand(msg, "")
79 if command not in ALLOWED_COMMANDS:
80 raise InvalidCommand(msg, command)
82 command_args = args[2:]
85 if command in ["new", "comment"]:
86 body,type = get_body_type(msg)
88 if "--reporter" not in args and "-r" not in args:
89 command_args = ["--reporter", author] + command_args
90 body = body.strip().split("\n", 1)[0] # only take first line
91 elif command == "comment":
92 if "--author" not in args and "-a" not in args:
93 command_args = ["--author", author] + command_args
94 if "--content-type" not in args and "-c" not in args:
95 command_args = ["--content-type", type] + command_args
96 if "--alt-id" not in args:
97 command_args = ["--alt-id", msg["message-id"]] + command_args
98 command_args.append(body)
99 # catch stdout and stderr
100 orig_stdout = sys.stdout
101 orig_stderr = sys.stderr
102 sys.stdout = StringIO.StringIO()
103 sys.stderr = StringIO.StringIO()
105 ret = libbe.cmdutil.execute(command, command_args)
106 # restore stdout and stderr
107 out_text = sys.stdout.getvalue()
108 err_text = sys.stderr.getvalue()
109 sys.stdout = orig_stdout
110 sys.stderr = orig_stderr
112 response_header = [u"From: %s" % HANDLER_ADDRESS,
114 u"Date: %s" % , libbe.utility.time_to_str(time.time()),
115 u"Content-Type: text/plain; charset=%s" % encoding,
116 u"Content-Transfer-Encoding: 8bit",
117 u"In-reply-to: %s" % (id),
118 u"Subject: %s Re: %s" % (SUBJECT_COMMENT, command),
120 response_body = [u"Results of running: (exit code %d)" % ret,
121 u" %s %s" % (command, " ".join(command_args)),]
122 if len(out_text) > 0:
123 response_body.extend([u"", u"stdout:", u"", out_text])
124 if len(err_text) > 0:
125 response_body.extend([u"", u"stderr:", u"", err_text])
126 response_body.append(u"")
127 response_email = send_pgp_mime.Mail(u"\n".join(response_header),
128 u"\n".join(response_body))
129 return response_email
132 msg_text = sys.stdin.read()
133 response_email = handle_message(msg_text)
134 send_pgp_mime.mail(response_email, send_pgp_mime.sendmail)
136 if __name__ == "__main__":