Index: mailer.py =================================================================== --- mailer.py (revision 37427) +++ mailer.py (working copy) @@ -44,8 +44,10 @@ # Python <3.0 from cStringIO import StringIO import smtplib +import nntplib import re import tempfile +import subprocess # Minimal version of Subversion's bindings required _MIN_SVN_VERSION = [1, 5, 0] @@ -177,7 +179,7 @@ pipe_ob.wait() -class MailedOutput(OutputBase): +class MessageOutput(OutputBase): def __init__(self, cfg, repos, prefix_param): OutputBase.__init__(self, cfg, repos, prefix_param) @@ -194,6 +196,18 @@ [_f for _f in to_addr_in[3:].split(to_addr_in[1]) if _f] else: self.to_addrs = [_f for _f in to_addr_in.split() if _f] + # whitespace (or another character) separated list of groups + # which must be split into a clean list + to_group_in = self.cfg.get('to_group', group, params) + # if list of addresses starts with '[.]' + # use the character between the square brackets as split char + # else use whitespaces + if len(to_group_in) >= 3 and to_group_in[0] == '[' \ + and to_group_in[2] == ']': + self.to_groups = \ + [_f for _f in to_group_in[3:].split(to_group_in[1]) if _f] + else: + self.to_groups = [_f for _f in to_group_in.split() if _f] self.from_addr = self.cfg.get('from_addr', group, params) \ or self.repos.author or 'no_author' # if the from_addr (also) starts with '[.]' (may happen if one @@ -208,43 +222,63 @@ and self.reply_to[2] == ']': self.reply_to = self.reply_to[3:] - def mail_headers(self, group, params): + def message_headers(self, group, params): subject = self.make_subject(group, params) try: subject.encode('ascii') except UnicodeError: from email.Header import Header subject = Header(subject, 'utf-8').encode() - hdrs = 'From: %s\n' \ - 'To: %s\n' \ - 'Subject: %s\n' \ - 'MIME-Version: 1.0\n' \ - 'Content-Type: text/plain; charset=UTF-8\n' \ - 'Content-Transfer-Encoding: 8bit\n' \ - % (self.from_addr, ', '.join(self.to_addrs), subject) + hdrs = 'From: %s\n' \ + 'Subject: %s\n' \ + % (self.from_addr, subject) + if self.to_groups: + hdrs += 'Newsgroups: %s\n' % ', '.join(self.to_groups) + if self.to_addrs: + hdrs += 'To: %s\n' % ', '.join(self.to_addrs) if self.reply_to: - hdrs = '%sReply-To: %s\n' % (hdrs, self.reply_to) + hdrs += 'Reply-To: %s\n' % self.reply_to + hdrs += 'MIME-Version: 1.0\n' \ + 'Content-Type: text/plain; charset=UTF-8\n' \ + 'Content-Transfer-Encoding: 8bit\n' return hdrs + '\n' -class SMTPOutput(MailedOutput): +class SMTPAndNNTPOutput(MessageOutput): "Deliver a mail message to an MTA using SMTP." + def __init__(self, cfg, repos, prefix_param): + OutputBase.__init__(self, cfg, repos, prefix_param) + self.do_smtp = cfg.is_set('general.smtp_hostname') + self.do_nntp = cfg.is_set('general.nntp_hostname') + self.errorcmd = cfg.is_set('general.nntp_error_cmd') + def start(self, group, params): - MailedOutput.start(self, group, params) + MessageOutput.start(self, group, params) self.buffer = StringIO() self.write = self.buffer.write - self.write(self.mail_headers(group, params)) + self.write(self.message_headers(group, params)) def finish(self): - server = smtplib.SMTP(self.cfg.general.smtp_hostname) - if self.cfg.is_set('general.smtp_username'): - server.login(self.cfg.general.smtp_username, - self.cfg.general.smtp_password) - server.sendmail(self.from_addr, self.to_addrs, self.buffer.getvalue()) - server.quit() + if self.do_smtp: + server = smtplib.SMTP(self.cfg.general.smtp_hostname) + if self.cfg.is_set('general.smtp_username'): + server.login(self.cfg.general.smtp_username, + self.cfg.general.smtp_password) + server.sendmail(self.from_addr, self.to_addrs, self.buffer.getvalue()) + server.quit() + if self.do_nntp: + server = nntplib.NNTP(self.cfg.general.nntp_hostname) + self.buffer.seek(0) + try: + server.post(self.buffer) + except nntplib.NNTPError, errormsg: + subprocess.call([self.errorcmd, str(errormsg)]) + # it should not fail the 2. time because the errorcmd fixed the problem + server.post(self.buffer) + server.quit() class StandardOutput(OutputBase): @@ -262,17 +296,17 @@ pass -class PipeOutput(MailedOutput): +class PipeOutput(MessageOutput): "Deliver a mail message to an MTA via a pipe." def __init__(self, cfg, repos, prefix_param): - MailedOutput.__init__(self, cfg, repos, prefix_param) + MessageOutput.__init__(self, cfg, repos, prefix_param) # figure out the command for delivery self.cmd = cfg.general.mail_command.split() def start(self, group, params): - MailedOutput.start(self, group, params) + MessageOutput.start(self, group, params) ### gotta fix this. this is pretty specific to sendmail and qmail's ### mailwrapper program. should be able to use option param substitution @@ -284,7 +318,7 @@ self.write = self.pipe.stdin.write # start writing out the mail message - self.write(self.mail_headers(group, params)) + self.write(self.message_headers(group, params)) def finish(self): # signal that we're done sending content @@ -302,8 +336,9 @@ if cfg.is_set('general.mail_command'): cls = PipeOutput - elif cfg.is_set('general.smtp_hostname'): - cls = SMTPOutput + elif cfg.is_set('general.smtp_hostname') or \ + cfg.is_set('general.nntp_hostname'): + cls = SMTPAndNNTPOutput else: cls = StandardOutput Index: mailer.conf.example =================================================================== --- mailer.conf.example (revision 37427) +++ mailer.conf.example (working copy) @@ -27,6 +27,10 @@ #smtp_username = example #smtp_password = example +# This option specifies the hostname for delivery via NNTP. +nntp_hostname = nntp.example.com +nntp_error_cmd = /bin/false + # -------------------------------------------------------------------------- # @@ -171,11 +175,18 @@ # NOTE: If you want to use a different character for separating the # addresses put it in front of the addresses included in square # brackets '[ ]'. -to_addr = invalid@example.com +#to_addr = invalid@example.com # If this is set, then a Reply-To: will be inserted into the message. reply_to = +# The default Newsgroup: group for message. One or more groups, +# separated by whitespace (no commas). +# NOTE: If you want to use a different character for separating the +# groups put it in front of the groups included in square +# brackets. '[ ]'. +to_group = tests + # Specify which types of repository changes mailer.py will create # diffs for. Valid options are any combination of # 'add copy modify delete', or 'none' to never create diffs.