#! /usr/bin/python3 """Open Debian BTS mboxes with Mutt, à la /usr/bin/bts show --mbox. A cache of mboxes is kept, and changed mboxes will be merged with existing files instead of replacing them, so that e.g. read-status is preserved for each message. """ import os import re import sys import mailbox import tempfile import email.parser import urllib.request MBOX_DIR = os.path.expanduser('~/.mail/y.bug-cache') ## def main(): if len(sys.argv) != 2: print('Usage: {0} '.format(sys.argv[0]), file=sys.stderr) return 1 else: bug = re.sub(r'[^0-9]', '', sys.argv[1]) if not re.search(r'^\d{4,}$', bug): print('E: {0} does not seem a valid number'.format(sys.argv[1]), file=sys.stderr) return 1 path = mbox_update(bug) invoke_mailer(path) ## def mbox_update(bug): """Return a path with an up-to-date copy of the mbox for bug.""" path = os.path.join(MBOX_DIR, bug + '.mbox') if not os.path.exists(path): f = open(path, 'wb') try: retrieve_mbox(bug, f) except: os.unlink(path) raise else: f.close() else: # make a list of Message-Id we have msgids = { x.get('Message-Id') for x in mailbox.mbox(path) } with tempfile.NamedTemporaryFile() as tmpfd: # retrieve the remote mbox again retrieve_mbox(bug, tmpfd) # parse its messages parser = email.parser.Parser() new_msgids = { x['Message-Id']: x for x in mailbox.mbox(tmpfd.name, parser.parse) } with open(path, 'a+') as fd: # now append the new messages for msgid in new_msgids.keys() - msgids: fd.write('\n' + new_msgids[msgid].as_string(unixfrom=True)) return path def retrieve_mbox(bug, fileobj): """Retrieve mbox for bug from bugs.debian.org, writing it to fileobj.""" url = urllib.request.urlopen( 'http://bugs.debian.org/cgi-bin/bugreport.cgi?' 'mboxstatus=yes;mboxmaint=yes;mbox=yes;bug={0}'.format(bug)) for line in url.fp: # http://bugs.python.org/issue4608 fileobj.write(line) def invoke_mailer(path): """Exec mutt, opening path.""" os.execlp('mutt', 'mutt', '-f', path) ## if __name__ == '__main__': try: sys.exit(main()) except KeyboardInterrupt: print('\nCancelled.', file=sys.stderr) sys.exit(1)