Modified: 
subversion/branches/gpg-agent-password-store/tools/dist/collect_sigs.py
URL: 
http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/dist/collect_sigs.py?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/tools/dist/collect_sigs.py 
(original)
+++ subversion/branches/gpg-agent-password-store/tools/dist/collect_sigs.py Thu 
Dec  2 20:55:08 2010
@@ -1,14 +1,131 @@
 #!/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?
+#
+
+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 sys, os, string, subprocess, re
+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)
 
-version = '1.6.13'
 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 = '''
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -17,43 +134,67 @@ shell_content = '''
 <title>Signature collection for Subversion $version</title>
 </head>
 <body style="font-size: 14pt; text-align: justify;
-  background-color: #f0f0f0; padding: 0 5%">
-<p>This page is used to collect signatures for the proposed release of
-Apache Subversion $version.</p>
+  background-color: #f0f0f0; padding: 0 5%%">
+<p>This page is used to collect <a href="%s/list">signatures</a> for the
+proposed release of Apache Subversion $version.</p>
 $content
 </body>
 </html>
-'''
+''' % os.getenv('SCRIPT_NAME')
 
-def default_page():
-  c = '''
-<form method="post">
-File: <select name="filename">
-%s
-</select>
-<br/>
-<p>Paste signature in the area below:<br/>
-<textarea name="signature" rows="10" cols="80"></textarea>
+signature_area = '''
+<hr/>
+<form method="post" action="%s">
+<p>Paste one or more signatures in the area below:<br/>
+<textarea name="signatures" rows="20" cols="80"></textarea>
 </p>
 <input type="submit" value="Submit" />
+<p>Any text not between the <tt>BEGIN PGP SIGNATURE</tt>
+and <tt>END PGP SIGNATURE</tt> lines will be ignored.</p>
 </form>
+<hr/>
+''' % os.getenv('SCRIPT_NAME')
+ 
+
+
+def split(sigs):
+  lines = []
+  for line in sigs.split('\n'):
+    if lines or '--BEGIN' in line:
+      lines.append(line)
+    if '--END' in line:
+      yield "\n".join(lines) + "\n"
+      lines = []
+
+def list_signatures():
+  db = sqlite3.connect(os.path.join(config.sigdir, 'sigs.db'))
+  template = '''
+<hr/>
+<p>The following signature files are available:</p>
+<p>%s</p>
 '''
 
-  contents = [f for f in os.listdir('.')
-              if f.endswith('.tar.gz') or f.endswith('.zip')
-                                       or f.endswith('.tar.bz2')]
-  contents.sort()
+  lines = ""
+  curs = db.cursor()
+  curs.execute('''SELECT filename, COUNT(*) FROM signatures
+                  GROUP BY filename ORDER BY filename''')
+  for filename, count in curs:
+    lines += '<a href="%s/%s.asc">%s.asc</a>: %d signature%s<br/>\n' \
+             % (os.getenv('SCRIPT_NAME'), filename, filename,
+                count, ['s', ''][count == 1])
+  return (template % lines) + signature_area
+
+def save_valid_sig(db, filename, keyid, signature):
+  db.execute('INSERT OR REPLACE INTO signatures VALUES (?,?,?);',
+             (keyid, filename, buffer(signature)))
+  db.commit()
 
-  options = ''
-  for f in contents:
-    options = options + '<option value="%s">%s</option>\n' % (f, f)
+  generate_asc_files(config.sigdir)
 
-  return c % options
-
-
-def verify_sig(signature, filename):
+def verify_sig_for_file(signature, filename):
   args = ['gpg', '--logger-fd', '1', '--no-tty',
-          '--status-fd', '2', '--verify', '-', filename]
+          '--status-fd', '2', '--verify', '-',
+          os.path.join(config.filesdir, filename)]
 
   gpg = subprocess.Popen(args,
                          stdin=subprocess.PIPE,
@@ -74,54 +215,124 @@ def verify_sig(signature, filename):
   for line in lines:
     match = r.search(line)
     if match:
-      keyid = match.group(1)[-8:]
+      keyid = match.group(1)
       user = match.group(2)
 
-  return (True, (keyid, user))
-
+  return (True, (filename, keyid, user))
 
-def process_sig(signature, filename):
+def verify_sig(signature):
+  all_failures = ""
+  for filename in files():
+    (verified, result) = verify_sig_for_file(signature, filename)
+    if verified:
+      return (verified, result)
+    else:
+      all_failures += "%s:\n[[[\n%s]]]\n\n" % (filename, result)
+  return (False, all_failures)
+
+def process_sigs(signatures):
+  success = '''
+  <p style="color: green;">All %d signatures verified!</p>
+'''
+  failure = '''
+  <p style="color: red;">%d of %d signatures failed to verify; details 
below.</p>
+'''
   c_verified = '''
   <p style="color: green;">The signature is verified!</p>
   <p>Filename: <code>%s</code></p>
   <p>Key ID: <code>%s</code></p>
   <p>User: <code>%s</code></p>
   <p>This signature has been saved, and will be included as part of the
-    release signatures.  Please send mail to
-    <a href="mailto:[email protected]";>[email protected]</a>
-    acknowledging your successful signature.</p>
+    release signatures.</p>
 '''
   c_unverified = '''
   <p style="color: red;">The signature was not able to be verified!</p>
-  <p>Filename: <code>%s</code></p>
+  <p>Signature: <pre>%s</pre></p>
   <p>Reason:</p><pre>%s</pre>
   <p>Please talk to the release manager if this is in error.</p>
 '''
 
-  (verified, result) = verify_sig(signature, filename)
-
-  if verified:
-    return c_verified % (filename, result[0], result[1])
+  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:
-    return c_unverified % (filename, result)
+    retval += failure % (N_sigs-N_verified, N_sigs)
 
+  # Output details
+  N = 0
+  for outcome in outcomes:
+    N += 1
+    (verified, result) = outcome
+    retval += "<h1>Results for the %s signature</h1>" % ordinal(N)
+    if verified:
+      (filename, keyid, user) = result
+      retval += c_verified % (filename, keyid[-8:], user)
+    else:
+      retval += c_unverified % (signature, result)
+
+  return retval + signature_area
+
+
+def cat_signatures(basename):
+  # strip '.asc' extension
+  assert basename[:-4] in files()
+
+  # cat
+  ascfile = os.path.join(config.sigdir, basename)
+  if os.path.exists(ascfile):
+    return (open(ascfile, 'r').read())
 
-def main():
-  print "Content-Type: text/html"
+def print_content_type(mimetype):
+  print "Content-Type: " + mimetype
   print
 
+def main():
   form = cgi.FieldStorage()
-  if 'signature' not in form:
-    content = default_page()
-  else:
-    content = process_sig(form['signature'].value, form['filename'].value)
+  pathinfo = os.getenv('PATH_INFO')
+
+  # default value, to be changed below
+  content = signature_area
+
+  if 'signatures' in form:
+    content = process_sigs(form['signatures'].value)
+
+  elif pathinfo and pathinfo[1:]:
+    basename = pathinfo.split('/')[-1]
+
+    if basename == 'list':
+      content = list_signatures()
+
+    elif basename[:-4] in files():
+      # early exit; bypass 'content' entirely
+      print_content_type('text/plain')
+      print cat_signatures(basename)
+      return
 
   # These are "global" values, not specific to our action.
   mapping = {
-      'version' : version,
+      'version' : config.version,
       'content' : content,
     }
 
+  print_content_type('text/html')
+
   template = string.Template(shell_content)
   print template.safe_substitute(mapping)
 

Modified: 
subversion/branches/gpg-agent-password-store/tools/dist/construct-rolling-environment.sh
URL: 
http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/dist/construct-rolling-environment.sh?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- 
subversion/branches/gpg-agent-password-store/tools/dist/construct-rolling-environment.sh
 (original)
+++ 
subversion/branches/gpg-agent-password-store/tools/dist/construct-rolling-environment.sh
 Thu Dec  2 20:55:08 2010
@@ -53,11 +53,11 @@ TEMPDIR=$BASEDIR/temp
 case $LOCATION in
     US)
     APACHE_MIRROR=http://www.pangex.com/pub/apache
-    SOURCEFORGE_MIRROR=http://internap.dl.sourceforge.net/sourceforge
+    SOURCEFORGE_MIRROR=softlayer
     ;;
     UK)
     APACHE_MIRROR=http://apache.rmplc.co.uk
-    SOURCEFORGE_MIRROR=http://kent.dl.sourceforge.net/sourceforge
+    SOURCEFORGE_MIRROR=kent
     ;;
     *)
     echo "Unknown LOCATION" >&2
@@ -77,7 +77,7 @@ setup() {
 create_prefix() {
     wget -nc http://ftp.gnu.org/gnu/autoconf/$AUTOCONF.tar.bz2
     wget -nc http://ftp.gnu.org/gnu/libtool/$LIBTOOL.tar.gz
-    wget -nc $SOURCEFORGE_MIRROR/swig/$SWIG.tar.gz
+    wget -nc 
"http://sourceforge.net/projects/swig/files/swig/$SWIG/$SWIG.tar.gz/download?use_mirror=$SOURCEFORGE_MIRROR";
 
     tar jxvf $AUTOCONF.tar.bz2
     cd $AUTOCONF

Modified: subversion/branches/gpg-agent-password-store/tools/dist/dist.sh
URL: 
http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/dist/dist.sh?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/tools/dist/dist.sh (original)
+++ subversion/branches/gpg-agent-password-store/tools/dist/dist.sh Thu Dec  2 
20:55:08 2010
@@ -229,6 +229,10 @@ rm -f "$DISTPATH/STATUS"
 # (See http://svn.haxx.se/dev/archive-2009-04/0166.shtml for discussion.)
 rm -rf "$DISTPATH/contrib"
 
+# Remove notes/ from our distribution tarball.  It's large, but largely
+# blue-sky and out-of-date, and of questionable use to end users.
+rm -rf "$DISTPATH/notes"
+
 # Remove packages/ from the tarball.
 # (See http://svn.haxx.se/dev/archive-2009-12/0205.shtml)
 rm -rf "$DISTPATH/packages"

Modified: 
subversion/branches/gpg-agent-password-store/tools/hook-scripts/mailer/mailer.py
URL: 
http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/hook-scripts/mailer/mailer.py?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- 
subversion/branches/gpg-agent-password-store/tools/hook-scripts/mailer/mailer.py
 (original)
+++ 
subversion/branches/gpg-agent-password-store/tools/hook-scripts/mailer/mailer.py
 Thu Dec  2 20:55:08 2010
@@ -866,12 +866,16 @@ class DiffGenerator:
           content = src_fname = dst_fname = None
         else:
           src_fname, dst_fname = diff.get_files()
-          content = DiffContent(self.cfg.get_diff_cmd(self.group, {
-            'label_from' : label1,
-            'label_to' : label2,
-            'from' : src_fname,
-            'to' : dst_fname,
-            }))
+          try:
+            content = DiffContent(self.cfg.get_diff_cmd(self.group, {
+              'label_from' : label1,
+              'label_to' : label2,
+              'from' : src_fname,
+              'to' : dst_fname,
+              }))
+          except OSError:
+            # diff command does not exist, try difflib.unified_diff()
+            content = DifflibDiffContent(label1, label2, src_fname, dst_fname)
 
       # return a data item for this diff
       return _data(
@@ -890,6 +894,33 @@ class DiffGenerator:
         content=content,
         )
 
+def _classify_diff_line(line, seen_change):
+  # classify the type of line.
+  first = line[:1]
+  ltype = ''
+  if first == '@':
+    seen_change = True
+    ltype = 'H'
+  elif first == '-':
+    if seen_change:
+      ltype = 'D'
+    else:
+      ltype = 'F'
+  elif first == '+':
+    if seen_change:
+      ltype = 'A'
+    else:
+      ltype = 'T'
+  elif first == ' ':
+    ltype = 'C'
+  else:
+    ltype = 'U'
+
+  if line[-2] == '\r':
+    line=line[0:-2] + '\n' # remove carriage return
+
+  return line, ltype, seen_change
+
 
 class DiffContent:
   "This is a generator-like object returning annotated lines of a diff."
@@ -917,36 +948,42 @@ class DiffContent:
       self.pipe = None
       raise IndexError
 
-    # classify the type of line.
-    first = line[:1]
-    if first == '@':
-      self.seen_change = True
-      ltype = 'H'
-    elif first == '-':
-      if self.seen_change:
-        ltype = 'D'
-      else:
-        ltype = 'F'
-    elif first == '+':
-      if self.seen_change:
-        ltype = 'A'
-      else:
-        ltype = 'T'
-    elif first == ' ':
-      ltype = 'C'
-    else:
-      ltype = 'U'
+    line, ltype, self.seen_change = _classify_diff_line(line, self.seen_change)
+    return _data(
+      raw=line,
+      text=line[1:-1],  # remove indicator and newline
+      type=ltype,
+      )
 
-    if line[-2] == '\r':
-      line=line[0:-2] + '\n' # remove carriage return
+class DifflibDiffContent():
+  "This is a generator-like object returning annotated lines of a diff."
+
+  def __init__(self, label_from, label_to, from_file, to_file):
+    import difflib
+    self.seen_change = False
+    fromlines = open(from_file, 'U').readlines()
+    tolines = open(to_file, 'U').readlines()
+    self.diff = difflib.unified_diff(fromlines, tolines,
+                                     label_from, label_to)
+    
+  def __nonzero__(self):
+    # we always have some items
+    return True
 
+  def __getitem__(self, idx):
+
+    try:
+      line = self.diff.next()
+    except StopIteration:
+      raise IndexError
+
+    line, ltype, self.seen_change = _classify_diff_line(line, self.seen_change)
     return _data(
       raw=line,
       text=line[1:-1],  # remove indicator and newline
       type=ltype,
       )
 
-
 class TextCommitRenderer:
   "This class will render the commit mail in plain text."
 

Modified: 
subversion/branches/gpg-agent-password-store/tools/hook-scripts/svnperms.conf.example
URL: 
http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/hook-scripts/svnperms.conf.example?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- 
subversion/branches/gpg-agent-password-store/tools/hook-scripts/svnperms.conf.example
 (original)
+++ 
subversion/branches/gpg-agent-password-store/tools/hook-scripts/svnperms.conf.example
 Thu Dec  2 20:55:08 2010
@@ -9,6 +9,8 @@
 #
 [groups]
 group1 = user1 user2 user3
+group2 = user4 user5
+supergroup = @group1 @group2 user6
 
 #
 # Example repository control, showing allowed syntax.
@@ -20,13 +22,13 @@ group1 = user1 user2 user3
 # - line breaks are accepted
 #
 [example1 groups]
-group2 = user9 user10
+group3 = user9 user10
 
 [example1]
 trunk/.* = *(add,remove,update) @group1,user4,user5(update)
            user6,user7()
 trunk/.* = user8(add,update)
-tags/[^/]+/ = @group2(add)
+tags/[^/]+/ = @group3(add)
 branches/[^/]+/.* = *(add,remove,update)
 
 #

Modified: 
subversion/branches/gpg-agent-password-store/tools/hook-scripts/svnperms.py
URL: 
http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/hook-scripts/svnperms.py?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/tools/hook-scripts/svnperms.py 
(original)
+++ subversion/branches/gpg-agent-password-store/tools/hook-scripts/svnperms.py 
Thu Dec  2 20:55:08 2010
@@ -128,7 +128,17 @@ class Permission:
 
     def parse_groups(self, groupsiter):
         for option, value in groupsiter:
-            self._group[option] = value.split()
+            groupusers = []
+            for token in value.split():
+                # expand nested groups in place; no forward decls
+                if token[0] == "@":
+                    try:
+                        groupusers.extend(self._group[token[1:]])
+                    except KeyError:
+                        raise Error, "group '%s' not found" % token[1:]
+                else:
+                    groupusers.append(token)
+            self._group[option] = groupusers
 
     def parse_perms(self, permsiter):
         for option, value in permsiter:

Modified: 
subversion/branches/gpg-agent-password-store/tools/server-side/svn-rep-sharing-stats.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/server-side/svn-rep-sharing-stats.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- 
subversion/branches/gpg-agent-password-store/tools/server-side/svn-rep-sharing-stats.c
 (original)
+++ 
subversion/branches/gpg-agent-password-store/tools/server-side/svn-rep-sharing-stats.c
 Thu Dec  2 20:55:08 2010
@@ -329,12 +329,10 @@ pretty_print(const char *name,
   for (hi = apr_hash_first(scratch_pool, reps_ref_counts);
        hi; hi = apr_hash_next(hi))
     {
-      const struct key_t *key;
       struct value_t *value;
 
       SVN_ERR(cancel_func(NULL));
 
-      key = svn__apr_hash_index_key(hi);
       value = svn__apr_hash_index_val(hi);
       SVN_ERR(svn_cmdline_printf(scratch_pool, "%s %" APR_UINT64_T_FMT " %s\n",
                                  name, value->refcount,

Modified: subversion/branches/gpg-agent-password-store/win-tests.py
URL: 
http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/win-tests.py?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/win-tests.py (original)
+++ subversion/branches/gpg-agent-password-store/win-tests.py Thu Dec  2 
20:55:08 2010
@@ -226,7 +226,6 @@ if run_httpd:
     base_url = 'http://localhost:' + str(httpd_port)
 
 if base_url:
-  all_tests = client_tests
   repo_loc = 'remote repository ' + base_url + '.'
   if base_url[:4] == 'http':
     log = 'dav-tests.log'


Reply via email to