#!/usr/bin/python # pylint: disable=missing-docstring from __future__ import with_statement import ConfigParser import nntplib import quopri import re import socket import time import urllib2 try: from ncpp_local import process_patch except ImportError: # I've seen this error happen multiple times at 6AM on Sundays, # coinciding with the AFS restart. Wait five minutes for the # servers to come back. time.sleep(300) from ncpp_local import process_patch STATEFILE = '/mit/ncurses/auto/state.ini' STATE = ConfigParser.ConfigParser() STATE.read(STATEFILE) NC_STATE = dict(STATE.items('ncurses')) ## Patch processing RE_SUBJECT = re.compile(r'(?:ANN: )([^ ]*ncurses-\d+.\d+(?:-\d{8})?(?:.patch.gz)?)') RE_PATCH = re.compile(r' (\S*ncurses-\d+.\d+(?:-\d{8})?.patch.gz)') RE_VERS_PATCH = re.compile(r'(?:ANN: )?ncurses-%s-\d{8}(?:.patch.gz)?' % NC_STATE['version']) def is_patch(subject): if RE_SUBJECT.match(subject): if RE_VERS_PATCH.match(subject): return True print "Unexpected version number in subject:\n%s\n" % subject return False def find_patch_uri(body): body = quopri.decodestring('\n'.join(body)).split('\n') return [line.group(0) for line in map(RE_PATCH.match, body) if line] ## NNTP def response_okay(resp): # As per http://www.faqs.org/rfcs/rfc977.html # 2xx - Command ok return resp[0] == '2' def get_subjects(svr, group, last_seen, **_kw): resp, _count, first, last, _name = svr.group(group) if not response_okay(resp): print "Unexpected response [%s]" % resp elif int(first) < int(last_seen) < int(last): resp, subjects = svr.xhdr('subject', '%i-%s' % (int(last_seen)+1, last)) if not response_okay(resp): print "Unexpected response [%s]" % resp return subjects return None def process_patch_articles(svr, articles): for msg_idx in (article[0] for article in articles if is_patch(article[1])): resp, msg_idx, _msgid, body = svr.body(msg_idx) if response_okay(resp): uris = find_patch_uri(body) if len(uris) != 1: # Unexpected URI count, send mail. print "Unexpected number of URIs: %s" % uris return -1 try: ret = process_patch(uris[0].strip()) if ret != 0: return ret except urllib2.URLError as err: print "URLError: %s" % err.reason print "URL was: %s" % uris[0].strip() return 0 ## High-level def do_connect(): try_count = 0 while True: try: svr = nntplib.NNTP(NC_STATE['server']) return svr except (socket.error, socket.gaierror, nntplib.NNTPError, nntplib.NNTPTemporaryError): if try_count < 9: try_count += 1 time.sleep(30) else: raise # Giving up def main(): svr = do_connect() subjects = get_subjects(svr, **NC_STATE) if subjects: new_last = subjects[-1][0] STATE.set('ncurses', 'last_seen', new_last) ret = process_patch_articles(svr, subjects) STATE.set('ncurses', 'stop', ret) with open(STATEFILE, 'wb') as state_fh: STATE.write(state_fh) if NC_STATE['stop'] == '0': main()