ALLOWED_COMMANDS = ["new", "comment", "list", "show", "help"]
class InvalidEmail (ValueError):
- def __init__(self, msg, message):
+ def __init__(self, msg, info, message):
ValueError.__init__(self, message)
self.msg = msg
+ self.info = info
+ def response(self):
+ ret = 1
+ out_text = None
+ return (ret, out_text, self.stderr_msg(), self.info)
+ def stderr_msg(self):
+ err_text = [u"Invalid email (particular type unknown):\n",
+ unicode(self), u"",
+ send_pgp_mime.flatten(self.msg)]
+ return u"\n".join(err_text)
class InvalidSubject (InvalidEmail):
- pass
+ def stderr_msg(self):
+ err_text = u"\n".join([u"InvalidSubject:\n",
+ unicode(e), u"",
+ u"full subject was:",
+ e.msg["subject"]])
+ return err_text
class InvalidCommand (InvalidEmail):
- def __init__(self, msg, command):
+ def __init__(self, msg, info, command):
message = "Invalid command '%s'" % command
- ValueError.__init__(self, msg, message)
+ InvalidEmail.__init__(self, msg, info, message)
self.command = command
+ def stderr_msg(self):
+ err_text = u"\n".join([u"InvalidCommand:\n",
+ unicode(self), u"",
+ u"full subject was:",
+ self.msg["subject"]])
+ return err_text
def get_body_type(msg):
for part in msg.walk():
return (part.get_payload(decode=1), part.get_content_type())
def run_message(msg_text):
+ """
+ Attempt to execute the email given in the email string msg_text.
+ Raises assorted subclasses of InvalidEmail in the case of invalid
+ messages, otherwise return the exit code, stdout, and stderr
+ produced by the command, as well as a dictionary of information
+ gleaned from the email.
+ """
encoding = libbe.encoding.get_encoding()
libbe.encoding.set_IO_stream_encodings(encoding)
p=email.Parser.Parser()
msg=p.parsestr(msg_text)
- if "subject" not in msg:
- raise InvalidSubject(msg, "Email must contain a subject")
+ info = {}
+ info["encoding"] = encoding
author = send_pgp_mime.source_email(msg, return_realname=True)
- author_name = author[0]
- author_email = author[1]
- author_addr = email.utils.formataddr((author_name, author_email))
+ info["author_name"] = author[0]
+ info["author_email"] = author[1]
+ info["author_addr"] = email.utils.formataddr(
+ (info["author_name"], info["author_email"]))
+ info["message-id"] = msg["message-id"]
if LOGFILE != None:
f = file(LOGFILE, "w+")
- f.write("handling %s\n" % (author_addr))
+ f.write("handling %s\n" % (info["author_addr"]))
f.write("\n%s\n\n" % msg_text)
f.close()
- id = msg["message-id"]
+ if "subject" not in msg:
+ raise InvalidSubject(msg, info, "Email must contain a subject")
args = msg["subject"].split()
if len(args) < 1 or args[0] != SUBJECT_COMMENT:
- raise InvalidSubject(msg, "Subject must start with '%s '" % SUBJECT_COMMENT)
+ raise InvalidSubject(
+ msg, info, "Subject must start with '%s '" % SUBJECT_COMMENT)
elif len(args) < 2:
- raise InvalidCommand(msg, "")
+ raise InvalidCommand(msg, info, "") # don't accept blank commands
command = args[1]
+ info["command"] = command
if command not in ALLOWED_COMMANDS:
raise InvalidCommand(msg, command)
if len(args) > 2:
if "--alt-id" not in args:
command_args = ["--alt-id", msg["message-id"]] + command_args
command_args.append(body)
+ info["command-args"] = command_args
# catch stdout and stderr
new_stdout = StringIO.StringIO()
new_stderr = StringIO.StringIO()
sys.stdout = new_stdout
sys.stderr = new_stderr
# run the command
+ err = None
os.chdir(BE_DIR)
- ret = libbe.cmdutil.execute(command, command_args,
- manipulate_encodings=False)
+ try:
+ ret = libbe.cmdutil.execute(command, command_args,
+ manipulate_encodings=False)
+ except libbe.cmdutil.GetHelp:
+ print libbe.cmdutil.help(command)
+ except libbe.cmdutil.GetCompletions:
+ err = InvalidCommand(msg, info, "invalid option '--complete'")
+ except libbe.cmdutil.UsageError, e:
+ err = InvalidCommand(msg, info, e)
+ except libbe.cmdutil.UserError, e:
+ err = InvalidCommand(msg, info, e)
# restore stdout and stderr
sys.stdout.flush()
sys.stderr.flush()
sys.stderr = orig_stderr
out_text = new_stdout.getvalue()
err_text = new_stderr.getvalue()
+ if err != None:
+ raise err
+ return (ret, out_text, err_text, info)
- author_addr = "wking"
+def compose_response(ret, out_text, err_text, info):
+ info["author_addr"] = "wking" # for local testing
+ assert "encoding" in info
+ if "author_addr" not in info:
+ return None
+ if "command" not in info:
+ info["command"] = u"-BLANK-"
+ if "command_args" not in info:
+ info["command_args"] = []
response_header = [u"From: %s" % HANDLER_ADDRESS,
- u"To: %s" % author_addr,
+ u"To: %s" % info["author_addr"],
u"Date: %s" % libbe.utility.time_to_str(time.time()),
- u"Content-Type: text/plain; charset=%s" % encoding,
+ u"Content-Type: text/plain; charset=%s"%info["encoding"],
u"Content-Transfer-Encoding: 8bit",
- u"In-reply-to: %s" % (id),
- u"Subject: %s Re: %s" % (SUBJECT_COMMENT, command),
+ u"Subject: %s Re: %s"%(SUBJECT_COMMENT,info["command"]),
]
+ if "message-id" in info:
+ response_header.append(u"In-reply-to: %s" % info["message-id"])
response_body = [u"Results of running: (exit code %d)" % ret,
- u" %s %s" % (command, " ".join(command_args)),
- u""] # trailing endline on body
- if len(out_text) > 0:
+ u" %s %s" % (info["command"],
+ u" ".join(info["command_args"]))]
+ if out_text != None and len(out_text) > 0:
response_body.extend([u"", u"stdout:", u"", out_text])
- if len(err_text) > 0:
+ if err_text != None and len(err_text) > 0:
response_body.extend([u"", u"stderr:", u"", err_text])
- response_body.append(u"")
+ response_body.append(u"") # trailing endline
response_email = send_pgp_mime.Mail(u"\n".join(response_header),
u"\n".join(response_body))
if LOGFILE != None:
f = file(LOGFILE, "a+")
- f.write("responding to %s: %s\n" % (author_addr, command))
+ f.write("responding to %s: %s\n"
+ % (info["author_addr"], info["command"]))
f.write("\n%s\n\n" % send_pgp_mime.flatten(response_email.plain()))
f.close()
return response_email
def main():
msg_text = sys.stdin.read()
- response_email = run_message(msg_text)
+ try:
+ ret,out_text,err_text,info = run_message(msg_text)
+ except InvalidEmail, e:
+ ret,out_text,err_text,info = e.response()
+ except Exception, e:
+ if LOGFILE != None:
+ f = file(LOGFILE, "a+")
+ f.write("Uncaught exception:\n%s\n" % (e,))
+ f.close()
+ sys.exit(1)
+ response_email = compose_response(ret, out_text, err_text, info)
send_pgp_mime.mail(response_email.plain(), send_pgp_mime.sendmail)
if __name__ == "__main__":