Author: buildbot
Date: Fri Aug 3 15:22:07 2012
New Revision: 827830
Log:
Staging update by buildbot for vcl
Added:
websites/staging/vcl/trunk/content/downloads/
websites/staging/vcl/trunk/content/downloads/.htaccess
websites/staging/vcl/trunk/content/downloads/download.cgi
websites/staging/vcl/trunk/content/downloads/download.html
websites/staging/vcl/trunk/content/downloads/ezt.py
websites/staging/vcl/trunk/content/downloads/mirrors.cgi
Modified:
websites/staging/vcl/trunk/content/ (props changed)
Propchange: websites/staging/vcl/trunk/content/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Fri Aug 3 15:22:07 2012
@@ -1 +1 @@
-1367718
+1369024
Added: websites/staging/vcl/trunk/content/downloads/.htaccess
==============================================================================
--- websites/staging/vcl/trunk/content/downloads/.htaccess (added)
+++ websites/staging/vcl/trunk/content/downloads/.htaccess Fri Aug 3 15:22:07
2012
@@ -0,0 +1 @@
+Options +ExecCGI
\ No newline at end of file
Added: websites/staging/vcl/trunk/content/downloads/download.cgi
==============================================================================
--- websites/staging/vcl/trunk/content/downloads/download.cgi (added)
+++ websites/staging/vcl/trunk/content/downloads/download.cgi Fri Aug 3
15:22:07 2012
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Wrapper script around mirrors.cgi script
+# (we must change to that directory in order for python to pick up the
+# python includes correctly)
+cd /www/vcl.apache.org/content/downloads
+/www/vcl.apache.org/content/downloads/mirrors.cgi $*
\ No newline at end of file
Added: websites/staging/vcl/trunk/content/downloads/download.html
==============================================================================
--- websites/staging/vcl/trunk/content/downloads/download.html (added)
+++ websites/staging/vcl/trunk/content/downloads/download.html Fri Aug 3
15:22:07 2012
@@ -0,0 +1,207 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<!--
+
+ 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.
+-->
+
+ <link href="/css/vcl.css" rel="stylesheet" type="text/css">
+ <link href="/css/code.css" rel="stylesheet" type="text/css">
+ <title>Apache VCL - Downloads</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+
+<body>
+ <div id="sitetitle">
+ <table width="100%" border="0" cellspacing="0" cellpadding="5">
+ <tr>
+ <td><a href="/index.html">Apache VCL</a></td>
+ <td><img src="/img/vcllogo.png" width="63" height="52"
align="right"></td>
+ </tr>
+ </table>
+ </div>
+
+ <div id="navigation">
+ <ul>
+<li>Information<ul>
+<li><a href="/info/about.html">What is VCL?</a></li>
+<li><a href="/info/features.html">Features</a></li>
+<li><a href="/info/architecture.html">Architecture</a></li>
+<li><a href="/info/use-cases.html">Use Cases</a></li>
+<li><a href="/download.cgi">Download</a></li>
+<li><a href="/info/license.html">License</a></li>
+<li><a href="/info/faq.html">FAQ</a></li>
+</ul>
+</li>
+<li><a href="/docs/index.html">Documentation</a><ul>
+<li><a href="/docs/using-vcl.html">Using VCL</a></li>
+<li><a href="/docs/image-creation.html">Image Creation</a></li>
+<li><a href="/docs/administration.html">Administration</a></li>
+<li><a href="/docs/installation.html">Installation</a></li>
+<li><a href="/docs/deployment-planning.html">Deployment Planning</a></li>
+</ul>
+</li>
+<li><a href="/comm/index.html">Community</a><ul>
+<li><a href="/comm/index.html#getInvolved">Getting Involved</a></li>
+<li><a href="/comm/index.html#mail-list">Mailing Lists</a></li>
+<li><a href="/comm/index.html#how-do-i-join-the-project">How can I
Join</a></li>
+<li><a href="/comm/wiki.html">Wiki</a></li>
+<li><a href="/dev/index.html">Development</a><ul>
+<li><a href="/dev/jira.html">Issue Tracking</a></li>
+<li><a href="/dev/code-documentation.html">Code Documentation</a></li>
+<li><a href="/dev/roadmap.html">Roadmap</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="http://www.apache.org">Apache Software Foundation</a><ul>
+<li><a href="http://www.apache.org/foundation/thanks.html">Thanks</a></li>
+<li><a href="http://www.apache.org/foundation/sponsorship.html">Become a
Sponsor</a></li>
+</ul>
+</li>
+</ul>
+ </div>
+
+ <div id="content">
+ <h1 class="title">Downloads</h1>
+ <h2 id="download-links">Download Links</h2>
+<p>Use the links below to download Apache VCL from one of our mirrors. You
<strong>must</strong>
+<a href="download.cgi#verify">verify the integrity</a> of the downloaded files
using
+signatures downloaded from our main distribution directory,
<strong>not</strong> from
+a mirror.</p>
+<p>Only current recommended releases are available on the main distribution
site and
+its mirrors. Older releases are available from the archive download site.</p>
+<p>Stable Release - Latest Version:</p>
+<ul>
+<li><a
href="[location]?Preferred=[preferred]&action=download&filename=%2Fvcl%2Fapache-VCL-2.3.tar.bz2">apache-VCL-2.3.tar.bz2</a>
+[ <a href="http://www.apache.org/dist/vcl/apache-VCL-2.3.tar.bz2.asc">GPG</a> ]
+[ <a
href="http://www.apache.org/dist/vcl/apache-VCL-2.3.tar.bz2.sha1">SHA1</a> ]
+[ <a href="http://www.apache.org/dist/vcl/apache-VCL-2.3.tar.bz2.md5">MD5</a> ]
+(released 2012-07-20)</li>
+</ul>
+<p>Previous Releases:</p>
+<ul>
+<li><a
href="[location]?Preferred=http://archive.apache.org/dist/&action=download&filename=%2Fincubator%2Fvcl%2Fapache-VCL-2.2.1-incubating.tar.bz2">apache-VCL-2.2.1-incubating.tar.bz2</a>
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.2.1-incubating.tar.bz2.asc">GPG</a>
]
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.2.1-incubating.tar.bz2.sha1">SHA1</a>
]
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.2.1-incubating.tar.bz2.md5">MD5</a>
]
+(released 2011-04-08)</li>
+<li><a
href="[location]?Preferred=http://archive.apache.org/dist/&action=download&filename=%2Fincubator%2Fvcl%2Fapache-VCL-2.2-incubating.tar.bz2">apache-VCL-2.2-incubating.tar.bz2</a>
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.2-incubating.tar.bz2.asc">GPG</a>
]
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.2-incubating.tar.bz2.sha1">SHA1</a>
]
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.2-incubating.tar.bz2.md5">MD5</a>
]
+(released 2010-10-05)</li>
+<li><a
href="[location]?Preferred=http://archive.apache.org/dist/&action=download&filename=%2Fincubator%2Fvcl%2Fapache-VCL-2.1-incubating.tar.bz2">apache-VCL-2.1-incubating.tar.bz2</a>
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.1-incubating.tar.bz2.asc">GPG</a>
]
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.1-incubating.tar.bz2.sha1">SHA1</a>
]
+[ <a
href="http://archive.apache.org/dist/incubator/vcl/apache-VCL-2.1-incubating.tar.bz2.md5">MD5</a>
]
+(released 2009-12-04)</li>
+</ul>
+<h2 id="mirror">Mirror</h2>
+<p>The currently selected mirror is <strong>[preferred]</strong>. If you
encounter a problem with this
+mirror, please select another mirror. If all mirrors are failing, there are
backup
+mirrors (at the end of the mirrors list) that should be available.</p>
+<form name="setmirror" method="get" action="[location]">
+Other mirrors:
+<select name="Preferred">
+[if-any http]
+ [for http]
+ <option value="[http]">[http]</option>
+ [end]
+[end]
+[if-any ftp]
+ [for ftp]
+ <option value="[ftp]">[ftp]</option>
+ [end]
+[end]
+[if-any backup]
+ [for backup]
+ <option value="[backup]">[backup] (backup)</option>
+ [end]
+[end]
+</select>
+<input type="submit" value="Change"></input>
+</form>
+
+<p>You may also consult the <a href="http://www.apache.org/mirrors/">complete
list of mirrors</a>.</p>
+<p><a name="verify"></a></p>
+<h2 id="verifying-the-integrity-of-the-files">Verifying the integrity of the
files</h2>
+<p>It is essential that you verify the integrity of the downloaded files using
the PGP or
+MD5 signatures. Security of the mirrors cannot be guaranteed, which means
malicious code
+could be added to the downloads from the mirrors. By verifying the integrity
of
+downloaded release files, you ensure they have not been tainted.</p>
+<p>Run the following command to verify the MD5 sum. It should give output
similar to
+"apache-VCL-2.3.tar.bz2: OK":</p>
+<pre>
+md5sum -c apache-VCL-2.3.tar.bz2.md5
+</pre>
+
+<p>Similarly, run the following command to verify the SHA1 sum. You should get
output
+similar to "apache-VCL-2.3.tar.bz2: OK":</p>
+<pre>
+sha1sum -c apache-VCL-2.3.tar.bz2.sha1
+</pre></p>
+<p>To verify the GPG signature (you'll need to have
+<a href="http://www.gnupg.org/">GnuPG</a> installed):</p>
+<ol>
+<li>download and import the VCL KEYS file (if you've imported the KEYS file
for previously releases, you do not need to import it again):<br>
+gpg --import KEYS</li>
+<li>download the GPG Signature to the same location as the release file</li>
+<li>from the directory containing both the release file and the GPG signature,
run<br>
+gpg --verify apache-VCL-2.3.tar.bz2.asc</li>
+</ol>
+<h2 id="installation">Installation</h2>
+<p>For new installs, visit the on-line installation guide:</p>
+<p>Latest Version:</p>
+<ul>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/VCL+2.3+Installation">Apache
VCL 2.3 Installation Guide</a></li>
+</ul>
+<p>Previous Versions:</p>
+<ul>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/VCL+2.3+Installation">Apache
VCL 2.2.1 Installation Guide</a></li>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/VCL+2.3+Installation">Apache
VCL 2.2 Installation Guide</a></li>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/VCL+2.3+Installation">Apache
VCL 2.1 Installation Guide</a></li>
+</ul>
+<h2 id="upgrading">Upgrading</h2>
+<p>For upgrades, visit the on-line upgrade guide:</p>
+<p>Upgrade to latest version:</p>
+<ul>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/Upgrade+From+Previous+Version+%282.2.1+to+2.3%29">upgrade
2.2.1 to 2.3</a></li>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/Upgrade+From+Previous+Version+%282.2+to+2.3%29">upgrade
2.2 to 2.3</a></li>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/Upgrade+From+Previous+Version+%282.1+to+2.3%29">upgrade
2.1 to 2.3</a></li>
+</ul>
+<p>Upgrade really old versions to older versions:</p>
+<ul>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/Upgrade+From+Previous+Version+%282.2+to+2.2.1%29">upgrade
2.2 to 2.2.1</a></li>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/Upgrade+From+Previous+Version+%282.1+to+2.2.1%29">upgrade
2.1 to 2.2.1</a></li>
+<li><a
href="http://cwiki.apache.org/confluence/display/VCL/Upgrade+From+Previous+Version">upgrade
2.1 to 2.2</a></li>
+</ul>
+ </div>
+
+ <div id="footer">
+ <div class="copyright">
+ <p>
+ Copyright © 2012 The Apache Software Foundation, Licensed under
+ the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache
License, Version 2.0</a>.
+ <br />
+ Apache and the Apache feather logo are trademarks of The Apache
Software Foundation.
+ </p>
+ </div>
+ </div>
+
+</body>
+</html>
Added: websites/staging/vcl/trunk/content/downloads/ezt.py
==============================================================================
--- websites/staging/vcl/trunk/content/downloads/ezt.py (added)
+++ websites/staging/vcl/trunk/content/downloads/ezt.py Fri Aug 3 15:22:07 2012
@@ -0,0 +1,653 @@
+#!/usr/bin/env python
+"""ezt.py -- EaZy Templating
+
+For documentation, please see: http://code.google.com/p/ezt/wiki/Syntax
+"""
+#
+# Copyright (C) 2001-2011 Greg Stein. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+#
+# This software is maintained by Greg and is available at:
+# http://code.google.com/p/ezt/
+#
+
+__author__ = 'Greg Stein'
+__version__ = '1.0'
+__license__ = 'BSD'
+
+import re
+from types import StringType, IntType, FloatType, LongType
+import os
+import urllib
+try:
+ import cStringIO
+except ImportError:
+ import StringIO
+ cStringIO = StringIO
+
+#
+# Formatting types
+#
+FORMAT_RAW = 'raw'
+FORMAT_HTML = 'html'
+FORMAT_XML = 'xml'
+FORMAT_JS = 'js'
+FORMAT_URL = 'url'
+
+#
+# This regular expression matches three alternatives:
+# expr: NEWLINE | DIRECTIVE | BRACKET | COMMENT
+# DIRECTIVE: '[' ITEM (whitespace ITEM)* ']
+# ITEM: STRING | NAME
+# STRING: '"' (not-slash-or-dquote | '\' anychar)* '"'
+# NAME: (alphanum | '_' | '-' | '.')+
+# BRACKET: '[[]'
+# COMMENT: '[#' not-rbracket* ']'
+#
+# When used with the split() method, the return value will be composed of
+# non-matching text and the three paren groups (NEWLINE, DIRECTIVE and
+# BRACKET). Since the COMMENT matches are not placed into a group, they are
+# considered a "splitting" value and simply dropped.
+#
+_item = r'(?:"(?:[^\\"]|\\.)*"|[-\w.]+)'
+_re_parse = re.compile(r'(\r?\n)|\[(%s(?: +%s)*)\]|(\[\[\])|\[#[^\]]*\]' %
+ (_item, _item))
+
+_re_args = re.compile(r'"(?:[^\\"]|\\.)*"|[-\w.]+')
+
+# block commands and their argument counts
+_block_cmd_specs = { 'if-index':2, 'for':1, 'is':2, 'define':1, 'format':1 }
+_block_cmds = _block_cmd_specs.keys()
+
+# two regular expressions for compressing whitespace. the first is used to
+# compress any whitespace including a newline into a single newline. the
+# second regex is used to compress runs of whitespace into a single space.
+_re_newline = re.compile('[ \t\r\f\v]*\n\\s*')
+_re_whitespace = re.compile(r'\s\s+')
+
+# this regex is used to substitute arguments into a value. we split the value,
+# replace the relevant pieces, and then put it all back together. splitting
+# will produce a list of: TEXT ( splitter TEXT )*. splitter will be '%' or
+# an integer.
+_re_subst = re.compile('%(%|[0-9]+)')
+
+class Template:
+
+ def __init__(self, fname=None, compress_whitespace=1,
+ base_format=FORMAT_RAW):
+ self.compress_whitespace = compress_whitespace
+ if fname:
+ self.parse_file(fname, base_format)
+
+ def parse_file(self, fname, base_format=FORMAT_RAW):
+ "fname -> a string object with pathname of file containg an EZT template."
+
+ self.parse(_FileReader(fname), base_format)
+
+ def parse(self, text_or_reader, base_format=FORMAT_RAW):
+ """Parse the template specified by text_or_reader.
+
+ The argument should be a string containing the template, or it should
+ specify a subclass of ezt.Reader which can read templates. The base
+ format for printing values is given by base_format.
+ """
+ if not isinstance(text_or_reader, Reader):
+ # assume the argument is a plain text string
+ text_or_reader = _TextReader(text_or_reader)
+
+ self.program = self._parse(text_or_reader,
+ base_printer=_parse_format(base_format))
+
+ def generate(self, fp, data):
+ if hasattr(data, '__getitem__') or callable(getattr(data, 'keys', None)):
+ # a dictionary-like object was passed. convert it to an
+ # attribute-based object.
+ class _data_ob:
+ def __init__(self, d):
+ vars(self).update(d)
+ data = _data_ob(data)
+
+ ctx = _context()
+ ctx.data = data
+ ctx.for_index = { }
+ ctx.defines = { }
+ self._execute(self.program, fp, ctx)
+
+ def _parse(self, reader, for_names=None, file_args=(), base_printer=None):
+ """text -> string object containing the template.
+
+ This is a private helper function doing the real work for method parse.
+ It returns the parsed template as a 'program'. This program is a sequence
+ made out of strings or (function, argument) 2-tuples.
+
+ Note: comment directives [# ...] are automatically dropped by _re_parse.
+ """
+
+ filename = reader.filename()
+ # parse the template program into: (TEXT NEWLINE DIRECTIVE BRACKET)* TEXT
+ parts = _re_parse.split(reader.text)
+
+ program = [ ]
+ stack = [ ]
+ if not for_names:
+ for_names = [ ]
+
+ if base_printer is None:
+ base_printer = ()
+ printers = [ base_printer ]
+
+ one_newline_copied = False
+ line_number = 1
+ for i in range(len(parts)):
+ piece = parts[i]
+ which = i % 4 # discriminate between: TEXT NEWLINE DIRECTIVE BRACKET
+ if which == 0:
+ # TEXT. append if non-empty.
+ if piece:
+ if self.compress_whitespace:
+ piece = _re_whitespace.sub(' ', piece)
+ program.append(piece)
+ one_newline_copied = False
+ elif which == 1:
+ # NEWLINE. append unless compress_whitespace requested
+ if piece:
+ line_number += 1
+ if self.compress_whitespace:
+ if not one_newline_copied:
+ program.append('\n')
+ one_newline_copied = True
+ else:
+ program.append(piece)
+ elif which == 3:
+ # BRACKET directive. append '[' if present.
+ if piece:
+ program.append('[')
+ one_newline_copied = False
+ elif piece:
+ # DIRECTIVE is present.
+ one_newline_copied = False
+ args = _re_args.findall(piece)
+ cmd = args[0]
+ if cmd == 'else':
+ if len(args) > 1:
+ raise ArgCountSyntaxError(str(args[1:]), filename, line_number)
+ ### check: don't allow for 'for' cmd
+ idx = stack[-1][1]
+ true_section = program[idx:]
+ del program[idx:]
+ stack[-1][3] = true_section
+ elif cmd == 'end':
+ if len(args) > 1:
+ raise ArgCountSyntaxError(str(args[1:]), filename, line_number)
+ # note: true-section may be None
+ try:
+ cmd, idx, args, true_section, start_line_number = stack.pop()
+ except IndexError:
+ raise UnmatchedEndError(None, filename, line_number)
+ else_section = program[idx:]
+ if cmd == 'format':
+ printers.pop()
+ else:
+ func = getattr(self, '_cmd_' + re.sub('-', '_', cmd))
+ program[idx:] = [ (func, (args, true_section, else_section),
+ filename, line_number) ]
+ if cmd == 'for':
+ for_names.pop()
+ elif cmd in _block_cmds:
+ if len(args) > _block_cmd_specs[cmd] + 1:
+ raise ArgCountSyntaxError(str(args[1:]), filename, line_number)
+ ### this assumes arg1 is always a ref unless cmd is 'define'
+ if cmd != 'define':
+ args[1] = _prepare_ref(args[1], for_names, file_args)
+
+ # handle arg2 for the 'is' command
+ if cmd == 'is':
+ args[2] = _prepare_ref(args[2], for_names, file_args)
+ elif cmd == 'for':
+ for_names.append(args[1][0]) # append the refname
+ elif cmd == 'format':
+ if args[1][0]:
+ raise BadFormatConstantError(str(args[1:]), filename,
line_number)
+ printers.append(_parse_format(args[1][1]))
+
+ # remember the cmd, current pos, args, and a section placeholder
+ stack.append([cmd, len(program), args[1:], None, line_number])
+ elif cmd == 'include' or cmd == 'insertfile':
+ is_insertfile = (cmd == 'insertfile')
+ # extra arguments are meaningless when using insertfile
+ if is_insertfile and len(args) != 2:
+ raise ArgCountSyntaxError(str(args), filename, line_number)
+ if args[1][0] == '"':
+ include_filename = args[1][1:-1]
+ if is_insertfile:
+ program.append(reader.read_other(include_filename).text)
+ else:
+ f_args = [ ]
+ for arg in args[2:]:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ program.extend(self._parse(reader.read_other(include_filename),
+ for_names, f_args, printers[-1]))
+ else:
+ if len(args) != 2:
+ raise ArgCountSyntaxError(str(args), filename, line_number)
+ if is_insertfile:
+ cmd = self._cmd_insertfile
+ else:
+ cmd = self._cmd_include
+ program.append((cmd,
+ (_prepare_ref(args[1], for_names, file_args),
+ reader, printers[-1]), filename, line_number))
+ elif cmd == 'if-any':
+ f_args = [ ]
+ for arg in args[1:]:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ stack.append(['if-any', len(program), f_args, None, line_number])
+ else:
+ # implied PRINT command
+ if len(args) > 1:
+ f_args = [ ]
+ for arg in args:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ program.append((self._cmd_subst,
+ (printers[-1], f_args[0], f_args[1:]),
+ filename, line_number))
+ else:
+ valref = _prepare_ref(args[0], for_names, file_args)
+ program.append((self._cmd_print, (printers[-1], valref),
+ filename, line_number))
+
+ if stack:
+ raise UnclosedBlocksError('Block opened at line %s' % stack[-1][4],
+ filename=filename)
+ return program
+
+ def _execute(self, program, fp, ctx):
+ """This private helper function takes a 'program' sequence as created
+ by the method '_parse' and executes it step by step. strings are written
+ to the file object 'fp' and functions are called.
+ """
+ for step in program:
+ if isinstance(step, StringType):
+ fp.write(step)
+ else:
+ method, method_args, filename, line_number = step
+ method(method_args, fp, ctx, filename, line_number)
+
+ def _cmd_print(self, (transforms, valref), fp, ctx, filename, line_number):
+ value = _get_value(valref, ctx, filename, line_number)
+ # if the value has a 'read' attribute, then it is a stream: copy it
+ if hasattr(value, 'read'):
+ while 1:
+ chunk = value.read(16384)
+ if not chunk:
+ break
+ for t in transforms:
+ chunk = t(chunk)
+ fp.write(chunk)
+ else:
+ for t in transforms:
+ value = t(value)
+ fp.write(value)
+
+ def _cmd_subst(self, (transforms, valref, args), fp, ctx, filename,
+ line_number):
+ fmt = _get_value(valref, ctx, filename, line_number)
+ parts = _re_subst.split(fmt)
+ for i in range(len(parts)):
+ piece = parts[i]
+ if i%2 == 1 and piece != '%':
+ idx = int(piece)
+ if idx < len(args):
+ piece = _get_value(args[idx], ctx, filename, line_number)
+ else:
+ piece = '<undef>'
+ for t in transforms:
+ piece = t(piece)
+ fp.write(piece)
+
+ def _cmd_include(self, (valref, reader, printer), fp, ctx, filename,
+ line_number):
+ fname = _get_value(valref, ctx, filename, line_number)
+ ### note: we don't have the set of for_names to pass into this parse.
+ ### I don't think there is anything to do but document it
+ self._execute(self._parse(reader.read_other(fname), base_printer=printer),
+ fp, ctx)
+
+ def _cmd_insertfile(self, (valref, reader, printer), fp, ctx, filename,
+ line_number):
+ fname = _get_value(valref, ctx, filename, line_number)
+ fp.write(reader.read_other(fname).text)
+
+ def _cmd_if_any(self, args, fp, ctx, filename, line_number):
+ "If any value is a non-empty string or non-empty list, then T else F."
+ (valrefs, t_section, f_section) = args
+ value = 0
+ for valref in valrefs:
+ if _get_value(valref, ctx, filename, line_number):
+ value = 1
+ break
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _cmd_if_index(self, args, fp, ctx, filename, line_number):
+ ((valref, value), t_section, f_section) = args
+ list, idx = ctx.for_index[valref[0]]
+ if value == 'even':
+ value = idx % 2 == 0
+ elif value == 'odd':
+ value = idx % 2 == 1
+ elif value == 'first':
+ value = idx == 0
+ elif value == 'last':
+ value = idx == len(list)-1
+ else:
+ value = idx == int(value)
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _cmd_is(self, args, fp, ctx, filename, line_number):
+ ((left_ref, right_ref), t_section, f_section) = args
+ right_value = _get_value(right_ref, ctx, filename, line_number)
+ left_value = _get_value(left_ref, ctx, filename, line_number)
+ value = left_value.lower() == right_value.lower()
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _do_if(self, value, t_section, f_section, fp, ctx):
+ if t_section is None:
+ t_section = f_section
+ f_section = None
+ if value:
+ section = t_section
+ else:
+ section = f_section
+ if section is not None:
+ self._execute(section, fp, ctx)
+
+ def _cmd_for(self, args, fp, ctx, filename, line_number):
+ ((valref,), unused, section) = args
+ list = _get_value(valref, ctx, filename, line_number)
+ refname = valref[0]
+ if isinstance(list, StringType):
+ raise NeedSequenceError(refname, filename, line_number)
+ ctx.for_index[refname] = idx = [ list, 0 ]
+ for item in list:
+ self._execute(section, fp, ctx)
+ idx[1] = idx[1] + 1
+ del ctx.for_index[refname]
+
+ def _cmd_define(self, args, fp, ctx, filename, line_number):
+ ((name,), unused, section) = args
+ valfp = cStringIO.StringIO()
+ if section is not None:
+ self._execute(section, valfp, ctx)
+ ctx.defines[name] = valfp.getvalue()
+
+def boolean(value):
+ "Return a value suitable for [if-any bool_var] usage in a template."
+ if value:
+ return 'yes'
+ return None
+
+
+def _prepare_ref(refname, for_names, file_args):
+ """refname -> a string containing a dotted identifier. example:"foo.bar.bang"
+ for_names -> a list of active for sequences.
+
+ Returns a `value reference', a 3-tuple made out of (refname, start, rest),
+ for fast access later.
+ """
+ # is the reference a string constant?
+ if refname[0] == '"':
+ return None, refname[1:-1], None
+
+ parts = refname.split('.')
+ start = parts[0]
+ rest = parts[1:]
+
+ # if this is an include-argument, then just return the prepared ref
+ if start[:3] == 'arg':
+ try:
+ idx = int(start[3:])
+ except ValueError:
+ pass
+ else:
+ if idx < len(file_args):
+ orig_refname, start, more_rest = file_args[idx]
+ if more_rest is None:
+ # the include-argument was a string constant
+ return None, start, None
+
+ # prepend the argument's "rest" for our further processing
+ rest[:0] = more_rest
+
+ # rewrite the refname to ensure that any potential 'for' processing
+ # has the correct name
+ ### this can make it hard for debugging include files since we lose
+ ### the 'argNNN' names
+ if not rest:
+ return start, start, [ ]
+ refname = start + '.' + '.'.join(rest)
+
+ if for_names:
+ # From last to first part, check if this reference is part of a for loop
+ for i in range(len(parts), 0, -1):
+ name = '.'.join(parts[:i])
+ if name in for_names:
+ return refname, name, parts[i:]
+
+ return refname, start, rest
+
+def _get_value((refname, start, rest), ctx, filename, line_number):
+ """(refname, start, rest) -> a prepared `value reference' (see above).
+ ctx -> an execution context instance.
+
+ Does a name space lookup within the template name space. Active
+ for blocks take precedence over data dictionary members with the
+ same name.
+ """
+ if rest is None:
+ # it was a string constant
+ return start
+
+ # get the starting object
+ if ctx.for_index.has_key(start):
+ list, idx = ctx.for_index[start]
+ ob = list[idx]
+ elif ctx.defines.has_key(start):
+ ob = ctx.defines[start]
+ elif hasattr(ctx.data, start):
+ ob = getattr(ctx.data, start)
+ else:
+ raise UnknownReference(refname, filename, line_number)
+
+ # walk the rest of the dotted reference
+ for attr in rest:
+ try:
+ ob = getattr(ob, attr)
+ except AttributeError:
+ raise UnknownReference(refname, filename, line_number)
+
+ # make sure we return a string instead of some various Python types
+ if isinstance(ob, (IntType, FloatType, LongType)):
+ return str(ob)
+ if ob is None:
+ return ''
+
+ # string or a sequence
+ return ob
+
+def _replace(s, replace_map):
+ for orig, repl in replace_map:
+ s = s.replace(orig, repl)
+ return s
+
+REPLACE_JS_MAP = (
+ ('\\', r'\\'), ('\t', r'\t'), ('\n', r'\n'), ('\r', r'\r'),
+ ('"', r'\x22'), ('\'', r'\x27'), ('&', r'\x26'),
+ ('<', r'\x3c'), ('>', r'\x3e'), ('=', r'\x3d'),
+)
+
+# Various unicode whitespace
+REPLACE_JS_UNICODE_MAP = (
+ (u'\u0085', r'\u0085'), (u'\u2028', r'\u2028'), (u'\u2029', r'\u2029'),
+)
+
+# Why not cgi.escape? It doesn't do single quotes which are occasionally
+# used to contain HTML attributes and event handler definitions (unfortunately)
+REPLACE_HTML_MAP = (
+ ('&', '&'), ('<', '<'), ('>', '>'),
+ ('"', '"'), ('\'', '''),
+)
+
+def _js_escape(s):
+ s = _replace(s, REPLACE_JS_MAP)
+ ### perhaps attempt to coerce the string to unicode and then replace?
+ if isinstance(s, unicode):
+ s = _replace(s, REPLACE_JS_UNICODE_MAP)
+ return s
+
+def _html_escape(s):
+ return _replace(s, REPLACE_HTML_MAP)
+
+def _url_escape(s):
+ ### quote_plus barfs on non-ASCII characters. According to
+ ### http://www.w3.org/International/O-URL-code.html URIs should be
+ ### UTF-8 encoded first.
+ if isinstance(s, unicode):
+ s = s.encode('utf8')
+ return urllib.quote_plus(s)
+
+FORMATTERS = {
+ FORMAT_RAW: None,
+ FORMAT_HTML: _html_escape,
+ FORMAT_XML: _html_escape, ### use the same quoting as HTML for now
+ FORMAT_JS: _js_escape,
+ FORMAT_URL: _url_escape,
+}
+
+def _parse_format(format_string=FORMAT_RAW):
+ format_funcs = []
+ try:
+ for fspec in format_string.split(','):
+ format_func = FORMATTERS[fspec]
+ if format_func is not None:
+ format_funcs.append(format_func)
+ except KeyError:
+ raise UnknownFormatConstantError(format_string)
+ return format_funcs
+
+class _context:
+ """A container for the execution context"""
+
+
+class Reader:
+ """Abstract class which allows EZT to detect Reader objects."""
+ def filename(self):
+ return '(%s does not provide filename() method)' % repr(self)
+
+class _FileReader(Reader):
+ """Reads templates from the filesystem."""
+ def __init__(self, fname):
+ self.text = open(fname, 'rb').read()
+ self._dir = os.path.dirname(fname)
+ self.fname = fname
+ def read_other(self, relative):
+ return _FileReader(os.path.join(self._dir, relative))
+ def filename(self):
+ return self.fname
+
+class _TextReader(Reader):
+ """'Reads' a template from provided text."""
+ def __init__(self, text):
+ self.text = text
+ def read_other(self, relative):
+ raise BaseUnavailableError()
+ def filename(self):
+ return '(text)'
+
+
+class EZTException(Exception):
+ """Parent class of all EZT exceptions."""
+ def __init__(self, message=None, filename=None, line_number=None):
+ self.message = message
+ self.filename = filename
+ self.line_number = line_number
+ def __str__(self):
+ ret = []
+ if self.message is not None:
+ ret.append(self.message)
+ if self.filename is not None:
+ ret.append('in file ' + str(self.filename))
+ if self.line_number is not None:
+ ret.append('at line ' + str(self.line_number))
+ return ' '.join(ret)
+
+class ArgCountSyntaxError(EZTException):
+ """A bracket directive got the wrong number of arguments."""
+
+class UnknownReference(EZTException):
+ """The template references an object not contained in the data dictionary."""
+
+class NeedSequenceError(EZTException):
+ """The object dereferenced by the template is no sequence (tuple or list)."""
+
+class UnclosedBlocksError(EZTException):
+ """This error may be simply a missing [end]."""
+
+class UnmatchedEndError(EZTException):
+ """This error may be caused by a misspelled if directive."""
+
+class BaseUnavailableError(EZTException):
+ """Base location is unavailable, which disables includes."""
+
+class BadFormatConstantError(EZTException):
+ """Format specifiers must be string constants."""
+
+class UnknownFormatConstantError(EZTException):
+ """The format specifier is an unknown value."""
+
+
+# --- standard test environment ---
+def test_parse():
+ assert _re_parse.split('[a]') == ['', '[a]', None, '']
+ assert _re_parse.split('[a] [b]') == \
+ ['', '[a]', None, ' ', '[b]', None, '']
+ assert _re_parse.split('[a c] [b]') == \
+ ['', '[a c]', None, ' ', '[b]', None, '']
+ assert _re_parse.split('x [a] y [b] z') == \
+ ['x ', '[a]', None, ' y ', '[b]', None, ' z']
+ assert _re_parse.split('[a "b" c "d"]') == \
+ ['', '[a "b" c "d"]', None, '']
+ assert _re_parse.split(r'["a \"b[foo]" c.d f]') == \
+ ['', '["a \\"b[foo]" c.d f]', None, '']
+
+def _test(argv):
+ import doctest, ezt
+ verbose = "-v" in argv
+ return doctest.testmod(ezt, verbose=verbose)
+
+if __name__ == "__main__":
+ # invoke unit test for this module:
+ import sys
+ sys.exit(_test(sys.argv)[0])
Added: websites/staging/vcl/trunk/content/downloads/mirrors.cgi
==============================================================================
--- websites/staging/vcl/trunk/content/downloads/mirrors.cgi (added)
+++ websites/staging/vcl/trunk/content/downloads/mirrors.cgi Fri Aug 3
15:22:07 2012
@@ -0,0 +1,274 @@
+#!/usr/local/bin/python2.7
+# -*- 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.
+#
+#
+# This is a Python CGI script that uses EZT to produce templated
+# mirror content and GeoIP to choose the appropriate mirror
+#
+
+import sys
+import os
+import cgi
+import stat
+import random
+import time
+import traceback
+import cStringIO
+
+# Should be installed in the main system library
+import GeoIP
+
+# Insert this directory into PATH so that we can import ezt
+this_dir = os.path.dirname(__file__) or '.'
+sys.path.insert(0, this_dir)
+
+import ezt
+
+
+# Configurable stuff
+MIRRORS_LIST = "/x1/www/www.apache.org/mirrors/mirrors.list"
+DEFAULT_REGION = "us"
+DEFAULT_TEMPLATE = "/x1/www/www.apache.org/dyn/closer.html"
+DEFAULT_LOCATION = "http://www.apache.org/dyn/closer.cgi"
+
+
+def get_region(environ):
+ """Use GeoIP to find the client's country, falling back to
+ DEFAULT_REGION on failure."""
+ try:
+ remote_ip = environ['REMOTE_ADDR'];
+ gi = GeoIP.new(GeoIP.GEOIP_STANDARD)
+ region = gi.country_code_by_addr(remote_ip).lower().strip()
+ if region == 'gb':
+ return 'uk'
+ else:
+ return region
+ except:
+ ### should we log an error here? absorbing without reporting is
+ ### generally bad form.
+ return DEFAULT_REGION
+
+
+def parse_mirrors(filename, country, preferred, mingood):
+ """Parse the mirror database to find the best mirrors for a client.
+
+ The Format of the mirror database is (last two fields are optional):
+ ftp au ftp://ftp.planetmirror.com/pub/apache/dist/ 1117724635
http://example.com/logo.gif http://example.com/ """
+
+ output = { 'http' : [ ],
+ 'ftp' : [ ],
+ 'backup' : [ ],
+ 'preferred' : None,
+ 'logo' : None,
+ 'link' : None,
+ }
+
+ # Read the mirror database and put it in a list of lists
+ # skip empty lines and comment
+ mirrors = [line.split() for line in open(filename).readlines()
+ if line.strip() and not line.startswith('#')]
+
+ mirrors.append(['http', 'us', 'http://archive.apache.org/dist/',
'9999999999'])
+
+ # Add trailing slashes where missing. Otherwise,
+ # strcat("http://www.mirror.org", pathinfo=".foo.evil") would link to
+ # http://www.mirror.org.foo.evil
+ for mir in mirrors:
+ if not mir[2].endswith('/'):
+ mir[2] += '/'
+
+ # grab the backup mirrors
+ backupmirrors = [mir for mir in mirrors if mir[1] == 'Backup']
+
+ # Grab the mirrors for the requested country or, failing that,
+ # from the default region (us)
+ for region in (country, DEFAULT_REGION):
+ countrymirrors = [mir for mir in mirrors if mir[1] == region]
+ random.shuffle(countrymirrors)
+ goodmirror = None
+ for mir in countrymirrors:
+ if mir[0] == 'http' and int(mir[3]) > mingood:
+ goodmirror = mir
+ break
+ if goodmirror:
+ break
+
+ # Check if the requested Preferred mirror is in the list
+ # Note the user-requested mirror doesn't have a trailing-slash
+ prefmir = None
+ if preferred:
+ for mir in mirrors:
+ if mir[2] == preferred:
+ prefmir = mir
+ break
+ # Otherwise pick a preferred mirror from our country
+ if not prefmir and goodmirror:
+ prefmir = goodmirror
+ if not prefmir: # In the worst case, choose a backup
+ prefmir = random.choice(backupmirrors)
+
+ # Record the preferred mirror and, if available, its logo and link
+ # Keep the trailing-slash on the URL (it is later joined to the path_info)
+ output['preferred'] = prefmir[2]
+ if len(prefmir) > 5:
+ output['logo'] = prefmir[4]
+ output['link'] = prefmir[5]
+
+ # Now assemble a list of all the other mirrors.
+ # Keep the trailing-slash on the URL (it is later joined to the path_info)
+ output['http'] = [mir[2] for mir in countrymirrors if mir[0] == 'http']
+ output['ftp'] = [mir[2] for mir in countrymirrors if mir[0] == 'ftp']
+ output['backup'] = [mir[2] for mir in backupmirrors]
+
+ return output
+
+
+def mirrorwrap(environ, start_response):
+ try:
+ return mirrorsapp(environ, start_response)
+ except:
+ status = "500 Oops"
+ response_headers = [("content-type","text/plain")]
+ start_response(status, response_headers, sys.exc_info())
+ return ["Problem running mirror.cgi, contact <[email protected]> "
+ "if it persists.\n\n"
+ + traceback.format_exc() ]
+
+
+def locate_template(environ):
+ # Determine the correct template by noting our filesystem location
+ if environ.has_key('ASF_MIRROR_FILENAME'):
+ template_file = environ['ASF_MIRROR_FILENAME'].replace(".cgi", ".html")
+ elif environ.has_key('SCRIPT_FILENAME'):
+ template_file = environ['SCRIPT_FILENAME'].replace(".cgi", ".html")
+ else:
+ template_file = sys.argv[0].replace(".cgi", ".html")
+
+ if not os.path.isfile(template_file):
+ # look in docroot instead if this is in a cgi-bin dir
+ template_file = template_file.replace("/cgi-bin/", "/content/")
+ if not os.path.isfile(template_file):
+ template_file = DEFAULT_TEMPLATE
+
+ return template_file
+
+
+def locate_mirrors(environ):
+ # Allow the MIRRORS_LIST environment variable to override the default
+ mirrors = environ.get('MIRRORS_LIST')
+ if mirrors and os.path.isfile(mirrors):
+ return mirrors
+ return MIRRORS_LIST # the default
+
+
+def mirrorsapp(environ, start_response):
+ headers = [ ]
+ resp_code = '200 OK'
+
+ # Where is the client coming from
+ region = get_region(environ)
+
+ # Was there a preferred mirror or update requirement?
+ form = cgi.FieldStorage(fp=environ['wsgi.input'],
+ environ=environ,
+ keep_blank_values=True)
+ preferred = form.getfirst("Preferred", "")
+ update = form.getfirst("update", "")
+
+ # Get the last update time of the mirror database
+ mirrors = locate_mirrors(environ)
+ base_time = os.path.getmtime(mirrors)
+
+ # convert from YYYYMMDDhhmm to time-since-unix-epoch
+ try:
+ mingood = time.mktime(time.strptime(update, "%Y%m%d%H%M"))
+ # Never use a mirror more than a week old
+ mingood = max(mingood, base_time - 7*24*60*60)
+ except:
+ # if we didn't get a time, or we can't convert it, then
+ # use the time the mirror database was last updated minus 24 hours
+ mingood = base_time - 24*60*60
+
+ # Load the mirrors file and parse it out
+ data = parse_mirrors(mirrors, region, preferred, mingood)
+
+ # ======== new download tracking code ==========
+ action = form.getfirst("action", "")
+ filename = form.getfirst("filename", "")
+ if action == 'download' and filename != '':
+ url = "%s%s" % (data['preferred'][:-1], filename)
+ headers.append(('Location', url))
+ start_response(resp_code, headers)
+ #log_download(url, data['preferred'], environ, region)
+ return ''
+ # ====== end new download tracking code ========
+
+ # Note location to self
+ data['location'] = environ.get('SCRIPT_NAME', DEFAULT_LOCATION)
+
+ path_param = form.getfirst("path", None)
+ if path_param:
+ path_info = cgi.escape(path_param, 1)
+ else:
+ # Note any PATH_INFO
+ if environ.has_key('PATH_INFO'):
+ path_info = cgi.escape(environ['PATH_INFO'], 1)
+ if environ.has_key('SCRIPT_NAME'):
+ if environ['PATH_INFO'] == environ['SCRIPT_NAME']:
+ path_info = ''
+ else:
+ path_info = ''
+ # The mirror URL already has a trailing slash. Avoid doubling it up.
+ if path_info.startswith('/'):
+ path_info = path_info[1:]
+ data['path_info'] = path_info
+
+ template_file = locate_template(environ)
+
+ # Print out the CGI header component
+ # using xml if the filename ends with the magic '--xml' string
+ if template_file.endswith('--xml.html'):
+ headers.append(('Content-type', 'text/xml'))
+ else:
+ headers.append(('Content-type', 'text/html'))
+
+ start_response(resp_code, headers)
+
+ output = cStringIO.StringIO()
+ template = ezt.Template(template_file)
+ template.generate(output, data)
+ return [ output.getvalue() ]
+
+# ======== new download tracking code ==========
+def log_download(url, preferred, environ, region):
+ timestamp = int(time.time())
+ # format: timestamp,url,mirror,IP,region,"useragent"
+ try:
+ fh = open('/home/jfthomps/debug', 'a')
+ fh.write('%s,%s,%s,%s,%s,"%s"\n' % (timestamp, url, preferred,
environ['REMOTE_ADDR'], region, environ['HTTP_USER_AGENT']))
+ fh.close
+ except:
+ pass
+# ====== end new download tracking code ========
+
+if __name__ == '__main__':
+ #from flup.server.fcgi import WSGIServer
+ from flup.server.cgi import WSGIServer
+ WSGIServer(mirrorwrap).run()