#!/usr/bin/env python # # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # # # A script intended to be useful in helping to collect signatures for a # release. This is a pretty rough, and patches are welcome to improve it. # # Some thoughts about future improvement: # * Display of per-file and per-release statistics # * Make use of the python-gpg package (http://code.google.com/p/python-gnupg/) # * Post to IRC when a new signature is collected # - Since we don't want to have a long running bot, perhaps we could # also patch wayita to accept and then echo a privmsg? # * Mail dev@ when somebody submits a successful signature, and include a # comments field which could be included in the mail. # * Use a subversion repository instead of sqlite backend # - no need to re-invent storage and retrieval # - perhaps we could re-use existing CIA/mailer hooks? # import sys, os import sqlite3 def make_config(): 'Output a blank config file' if os.path.exists('config.py'): print "'config.py' already exists!'" sys.exit(1) conf = open('config.py', 'w') conf.write("version = ''\n") conf.write("sigdir = ''\n") conf.write("filesdir = ''\n") conf.close() print "'config.py' generated" def make_db(): 'Initialize a blank database' db = sqlite3.connect('sigs.db') db.execute(''' CREATE TABLE signatures ( keyid TEXT, filename TEXT, signature BLOB, UNIQUE(keyid,filename) ); '''); # This function is web-facing def generate_asc_files(target_dir='.'): fds = {} def _open(filename): if not fds.has_key(filename): fd = open(os.path.join(target_dir, filename + '.asc'), 'w') fds[filename] = fd return fds[filename] db = sqlite3.connect(os.path.join(target_dir, 'sigs.db')) curs = db.cursor() curs.execute('SELECT filename, signature FROM signatures;') for filename, signature in curs: fd = _open(filename) fd.write(signature + "\n") for fd in fds.values(): fd.flush() fd.close() actions = { 'make_config' : make_config, 'make_db' : make_db, 'make_asc' : generate_asc_files, } if __name__ == '__main__': if len(sys.argv) > 1: if sys.argv[1] in actions: actions[sys.argv[1]]() sys.exit(0) # Stuff below this line is the web-facing side # ====================================================================== import cgi import cgitb cgitb.enable() import string, subprocess, re try: sys.path.append(os.path.dirname(sys.argv[0])) import config except: print 'Content-type: text/plain' print print 'Cannot find config file' sys.exit(1) r = re.compile('^\[GNUPG\:\] GOODSIG (\w*) (.*)') def files(): for f in os.listdir(config.filesdir): if config.version in f and (f.endswith('.tar.gz') or f.endswith('.zip') or f.endswith('.tar.bz2')): yield f def ordinal(N): try: return [None, 'first', 'second', 'third', 'fourth', 'fifth', 'sixth'][N] except: # Huh? We only have six files to sign. return "%dth" % N shell_content = '''
This page is used to collect signatures for the proposed release of Apache Subversion $version.
$content ''' % os.getenv('SCRIPT_NAME') signature_area = '''The following signature files are available:
%s
''' lines = "" curs = db.cursor() curs.execute('''SELECT filename, COUNT(*) FROM signatures GROUP BY filename ORDER BY filename''') for filename, count in curs: lines += '%s.asc: %d signature%sAll %d signatures verified!
''' failure = '''%d of %d signatures failed to verify; details below.
''' c_verified = '''The signature is verified!
Filename: %s
Key ID: %s
User: %s
This signature has been saved, and will be included as part of the release signatures.
''' c_unverified = '''The signature was not able to be verified!
Signature:
%s
Reason:
%s
Please talk to the release manager if this is in error.
''' outcomes = [] N_sigs = 0 N_verified = 0 retval = '' # Verify db = sqlite3.connect(os.path.join(config.sigdir, 'sigs.db')) for signature in split(signatures): N_sigs += 1 (verified, result) = verify_sig(signature) outcomes.append((verified, result)) if verified: (filename, keyid, user) = result save_valid_sig(db, filename, keyid, signature) N_verified += 1 # Output header if N_verified == N_sigs: retval += success % N_sigs else: retval += failure % (N_sigs-N_verified, N_sigs) # Output details N = 0 for outcome in outcomes: N += 1 (verified, result) = outcome retval += "