1
0
mirror of https://github.com/vitalif/viewvc-4intranet synced 2019-04-16 04:14:59 +03:00

Compare commits

..

30 Commits

Author SHA1 Message Date
cmpilato
a800c61089 Tag the 1.1.10 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.10@2535 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 16:23:58 +00:00
cmpilato
391f7d8237 Let's get 1.1.10 out today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2534 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 14:57:50 +00:00
cmpilato
141cf5ff10 * CHANGES
Tweak description of a change to not leave room for interpretation that
  the bug was a security vulnerability.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2533 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 14:56:15 +00:00
cmpilato
bfe148eb45 Merge from trunk r2531, whose log message read like so:
Hopefully, fix issue #475 ("regression: required authorization to
   root") and issue #476 ("Stack trace for some users if last commit
   deleted a file or folder") by preventing ViewVC from trying to look up
   the path type of a deleted path at an invalid location.
   
   * lib/vclib/svn/svn_repos.py
     (LocalSubversionRepository._gettype): New helper function.
     (LocalSubversionRepository.itemtype): Now use _gettype() helper.
     (_get_change_copyinfo): New helper.
     (_simple_auth_check): Rework this function to definitively resolve
       the path type of paths it passes through the authz system,
       handling the complication of determining the path of a now-deleted
       path by consulting its last location prior to deletion.

Also:

* CHANGES
  Note this change, sadly confessing to introducing a regression.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2532 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-11 20:22:58 +00:00
cmpilato
6fb28f2198 Add some helpful URLs to the release instructions. (Merged /trunk:2529.)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2530 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 20:02:37 +00:00
cmpilato
02cc53d34b Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2528 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 19:56:28 +00:00
cmpilato
5928918da4 Merge from trunk r2524, a bump in copyright years.
(Also includes an accidental commit to CHANGES naming today as the
1.1.9 release date.  Which sounds good.  What say you?)

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2525 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 19:27:50 +00:00
cmpilato
2e9f84427c Merge from trunk r2522:
* conf/viewvc.conf.dist (log_pagesextra): Set value to match defaults.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2523 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 18:28:55 +00:00
cmpilato
df599031a0 Merge from trunk r2519:
Followup to r2517, for issue #457.
   
   * templates/roots.ezt
     Also show the last author, and mark only the "Name" header as the
     sort choice.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2520 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 16:57:47 +00:00
cmpilato
6311c93298 Merge from trunk r2517, whose log message might be interpreted like so:
Finish issue #457 ("Display repository metadata (last-commit stuff) in
   roots view").
   
   * lib/viewvc.py
     (view_roots): Include revision metadata in the data dictionary.
     (list_roots): If configured to do so, calculate some last-modified
       metadata for Subversion roots.
   
   * lib/config.py
     (Config.set_defaults): Set default value for new show_roots_lastmod
       option.
   
   * conf/viewvc.conf.dist
     (show_roots_lastmod): New option.
   
   * templates/roots.ezt
     Show some roots metadata if configured to do so.
   
   * docs/template-authoring-guide.html
     Document new 'roots' view data dictionary items.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2518 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 15:29:11 +00:00
cmpilato
b982cccbcd Merge from trunk r2514, whose log message read like so:
Finish issue #455 ("ViewVC 1.1.x missing URL parsing in file contents
view").

* lib/viewvc.py
  (_re_rewrite_escaped_url): New compiled regexp.
  (markup_escaped_urls): New helper function.
  (markup_stream_pygments): Use markup_escaped_urls() to wrap URLs in
    <a> tags, something Pygments won't do for us.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2516 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-17 16:12:54 +00:00
cmpilato
33b8224714 Minor wording tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2515 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-17 16:11:45 +00:00
cmpilato
2f05f570b1 Merge from trunk r2512 which looked a little somethin' like this:
* lib/vclib/svn/__init__.py
     (canonicalize_rootpath): Per the thought found in issue #446
       ("Specifying repo location using file:// prefix in svn_roots leads
       to lack of hyperlinks on revision view"), convert file:/// and
       file://localhost/ URLs into local paths.

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2513 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-16 20:34:37 +00:00
cmpilato
d737657e1d Merge from trunk r2509, whose log message read like so:
Make what should be some decent performance improvements in the
   Subversion revision metadata harvesting logic.
   
   * lib/vclib/svn/svn_repos.py
     (LocalSubversionRepository._revinfo): Split this into two logical
       codepaths, one that needs changed-path information along with the
       revision metadata, one which does not.  (Because if we don't need
       it, there are faster ways to get what we *do* need.)

Also:

* CHANGES:
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2510 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-14 16:00:40 +00:00
cmpilato
3d5294635e Merge from trunk r2507, which is just some code re-org business:
* lib/vclib/svn/svn_repos.py
     (_get_history, _log_helper): Move these into...
     (LocalSubversionRepository._get_history,
      LocalSubversionRepository._log_helper): ...here.  Update callers.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2508 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-11 21:18:11 +00:00
cmpilato
75c3fc2346 Merge from trunk r2505, which did something like this:
* lib/vcauth/svnauthz/__init__.py
     (ViewVCAuthorizer._get_paths_for_root): Trap and return a cleaned-up
       error when the authzfile isn't parse-able.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2506 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-11 16:57:52 +00:00
cmpilato
80bce159af Clarify that Python 3.x is not yet supported. (Merged /trunk:r2502)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2503 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-07 14:49:01 +00:00
cmpilato
7ff9b84ee1 Merge from trunk r2500, whose log message read like so:
* lib/vclib/svn/svn_ra.py
     (client_log): Check for the correct exception.
   
   Reported by: Phil Jeary <Phil.Jeary@dovetailservices.com>

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2501 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-01-04 19:28:10 +00:00
cmpilato
b6acd3c114 Merge from trunk r2498, whose log message read like so (after I corrected it. twice.):
Make the number of "extra pages" of log output configuration.  (An old
   TODO item I'd forgotten about.)
   
   * lib/config.py
     (Config.set_defaults): Set cfg.options.log_pagesextra default value.
   
   * lib/viewvc.py
     (EXTRA_PAGES): Removed.
     (paging_sws): Add 'extra_pages' parameter, used instead of static
       EXTRA_PAGES variable.
     (view_log): Update call to paging_sws().  Replace uses of static
       EXTRA_PAGES with cfg.options.log_pagesextra.
   
   * conf/viewvc.conf.dist
     (log_pagesextra): New configuration item.

Also:

* CHANGES
  Record this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2499 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-09 17:14:20 +00:00
cmpilato
fa5d40caa3 Merge from trunk r2496, whose log message read like so:
Fix issue 425 ("Authorization subsystem needs some optimization").
   Give the 'vcauth' subsystem a way to make universal access
   determinations, which can seriously improve performance in situations
   where a user has universal read access to a repository.
   
   * lib/vcauth/__init__.py
     (GenericViewVCAuthorizer.check_universal_access): New skeletal function.
     (ViewVCAuthorizer.check_universal_access): New function.
   
   * lib/vcauth/svnauthz/__init__.py (ViewVCAuthorizer.check_universal_access),
   * lib/vcauth/forbidden/__init__.py (ViewVCAuthorizer.check_universal_access),
   * lib/vcauth/forbiddenre/__init__.py (ViewVCAuthorizer.check_universal_access)
     New functions.
   
   * lib/vclib/ccvs/bincvs.py
     (BaseCVSRepository.open): New function.
   
   * lib/vclib/svn/svn_repos.py
     (LocalSubversionRepository.open): Now check for universal read access.
   
   * lib/vclib/svn/svn_ra.py
     (RemoteSubversionRepository.open): Now check for universal read access.

Also:

* CHANGES
  Record this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2497 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-09 16:22:05 +00:00
cmpilato
a9cbd4c6a6 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2488 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:59:12 +00:00
cmpilato
882cdaa46b Let's get 1.1.8 out today, m'kay?
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2485 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:46:06 +00:00
cmpilato
58e64cb28d Minor tweak to the release process notes. (Merged /trunk:2483)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2484 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:38:43 +00:00
cmpilato
779e67653b Re-do a bit of conflict resolution from r2479 to reduce release-to-release
delta.

* bin/standalone.py
  (main): Describe --htpasswd-file before --gui now.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2482 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:34:29 +00:00
cmpilato
0381a772b7 Merge from trunk r2480, whose log message read like so:
Issue #472 ("Using view=markup on folder causes errors").
   
   * lib/viewvc.py
     (view_markup, view_annotate): Raise a more graceful error message
       when asked for markup or annotate view on a non-file.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2481 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 21:13:17 +00:00
cmpilato
048b2c8033 Merge from trunk r2478, whose log message read like so at the time:
* bin/standalone.py
     (has_crypt, _check_passwd): If 'crypt' isn't available, look for
       'fcrypt' and use it instead.
     (main): Always show --htpasswd-file option, but raise an informative
       error if someone tries to use it when there's no cryptographic support.

Also:

* CHANGES
  Update a change item to reflect the new state of code 'round these parts.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2479 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 18:26:44 +00:00
cmpilato
e354ab302c Merge from trunk r2476, whose log message read a bit like this:
* lib/config.py
     (Config._is_allowed_section, Config._is_allowed_override): Lose
       unused 'parser' parameter.  Callers updated.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2477 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 16:44:16 +00:00
cmpilato
5f931c6cf1 Merge from trunk r2473, whose log message read like so:
Fix issue #471 ("standalone.py won't start on Windows: no module named
   'crypt'") by making htpasswd file support optional (based on the
   availability of the 'crypt' module).
   
   * bin/standalone.py
     (): Conditionally import 'crypt' and define _check_passwd()
       helper function.
     (ViewVCHTTPRequestHandler.validate_password): Use _check_passwd() now.
     (main): Conditionally built accepted options list and usage string based
       on whether 'crypt' is available.

Also:

* CHANGES
  Note this fix, and tweak the announcement of Basic authn support for
  standalone.py to note "Unix only".


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2474 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-16 16:16:09 +00:00
cmpilato
576837351a Merge from trunk r2471, whose log message read like so:
Fix issue #467 (I think...) by not claiming a particular content
   length when compression would skew that.
   
   * lib/viewvc.py
     (get_writeready_server_file): Add optional 'content_length'
       parameter, and the code to handle it.
     (view_doc): Update call to get_writeready_server_file().

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2472 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-10-05 15:04:32 +00:00
cmpilato
6084ccf877 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2468 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 18:49:00 +00:00
18 changed files with 542 additions and 186 deletions

23
CHANGES
View File

@@ -1,9 +1,30 @@
Version 1.1.10 (released 15-Mar-2011)
* fix stack trace in Subversion revision info logic (issue #475, issue #476)
Version 1.1.9 (released 18-Feb-2011)
* vcauth universal access determinations (issue #425)
* rework svn revision info cache for performance
* make revision log "extra pages" count configurable
* fix Subversion 1.4.x revision log compatibility code regression
* display sanitized error when authzfile is malformed
* handle file:/// Subversion rootpaths as local roots (issue #446)
* restore markup of URLs in file contents (issue #455)
* optionally display last-committed metadata in roots view (issue #457)
Version 1.1.8 (released 02-Dec-2010)
* fix slowness triggered by allow_compress=1 configuration (issue #467)
* allow use of 'fcrypt' for Windows standalone.py authn support (issue #471)
* yield more useful error on directory markup/annotate request (issue #472)
Version 1.1.7 (released 09-Sep-2010)
* display Subversion revision properties in the revision view (issue #453)
* fix exception in 'standalone.py -r REPOS' when run without a config file
* fix standalone.py server root deployments (--script-alias='')
* add rudimentary Basic authentication support to standalone.py (issue #49)
* add Basic authentication support to standalone.py (Unix only) (issue #49)
* fix obscure "unexpected NULL parent pool" Subversion bindings error
* enable path info / link display in remote Subversion root revision view
* fix vhost name case handling inconsistency (issue #466)

View File

@@ -19,7 +19,7 @@ Congratulations on getting this far. :-)
For CVS Support:
* Python 1.5.2 or later
* Python 1.5.2 or later (sorry, no 3.x support yet)
(http://www.python.org/)
* RCS, Revision Control System
(http://www.cs.purdue.edu/homes/trinkle/RCS/)
@@ -30,7 +30,7 @@ Congratulations on getting this far. :-)
For Subversion Support:
* Python 2.0 or later
* Python 2.0 or later (sorry, no 3.x support yet)
(http://www.python.org/)
* Subversion, Version Control System, 1.3.1 or later
(binary installation and Python bindings)

View File

@@ -15,7 +15,7 @@
<blockquote>
<p><strong>Copyright &copy; 1999-2010 The ViewCVS Group. All rights
<p><strong>Copyright &copy; 1999-2011 The ViewCVS Group. All rights
reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth
@@ -61,6 +61,7 @@
<li>February 22, 2008 &mdash; copyright years updated</li>
<li>March 18, 2009 &mdash; copyright years updated</li>
<li>March 29, 2010 &mdash; copyright years updated</li>
<li>February 18, 2011 &mdash; copyright years updated</li>
</ul>
</body>

View File

@@ -42,7 +42,6 @@ import rfc822
import socket
import select
import base64
import crypt
import BaseHTTPServer
if LIBRARY_DIR:
@@ -55,6 +54,26 @@ import viewvc
import compat; compat.for_standalone()
# The 'crypt' module is only available on Unix platforms. We'll try
# to use 'fcrypt' if it's available (for more information, see
# http://carey.geek.nz/code/python-fcrypt/).
has_crypt = False
try:
import crypt
has_crypt = True
def _check_passwd(user_passwd, real_passwd):
return real_passwd == crypt.crypt(user_passwd, real_passwd[:2])
except ImportError:
try:
import fcrypt
has_crypt = True
def _check_passwd(user_passwd, real_passwd):
return real_passwd == fcrypt.crypt(user_passwd, real_passwd[:2])
except ImportError:
def _check_passwd(user_passwd, real_passwd):
return False
class Options:
port = 49152 # default TCP/IP port used for the server
start_gui = 0 # No GUI unless requested.
@@ -182,7 +201,7 @@ class ViewVCHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
for line in lines:
file_user, file_pass = string.split(line.rstrip(), ':', 1)
if username == file_user:
return file_pass == crypt.crypt(password, file_pass[:2])
return _check_passwd(password, file_pass)
except:
pass
return False
@@ -693,11 +712,26 @@ def cli(argv):
import getopt
class BadUsage(Exception): pass
short_opts = string.join(['c',
'd:',
'g',
'h:',
'p:',
'r:',
's:',
], '')
long_opts = ['daemon',
'config-file=',
'gui',
'host=',
'htpasswd-file=',
'port=',
'repository=',
'script-alias=',
]
try:
opts, args = getopt.getopt(argv[1:], 'gdc:p:r:h:s:',
['gui', 'daemon', 'config-file=', 'host=',
'port=', 'repository=', 'script-alias=',
'htpasswd-file='])
opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
for opt, val in opts:
if opt in ('-g', '--gui'):
options.start_gui = 1
@@ -729,6 +763,13 @@ def cli(argv):
if not os.path.isfile(val):
raise BadUsage, "'%s' does not appear to be a valid " \
"htpasswd file." % (val)
if not has_crypt:
raise BadUsage, "Unable to locate suitable `crypt' module for use " \
"with --htpasswd-file option. If your Python " \
"distribution does not include this module (as is " \
"the case on many non-Unix platforms), consider " \
"installing the `fcrypt' module instead (see " \
"http://carey.geek.nz/code/python-fcrypt/)."
options.htpasswd_file = val
if options.start_gui and options.config_file:
raise BadUsage, "--config-file option is not valid in GUI mode."
@@ -785,10 +826,11 @@ Options:
"cgi-bin/viewvc", then ViewVC will be accessible
at "http://%(host)s:%(port)s/cgi-bin/viewvc".
[default: %(script_alias)s]
--htpasswd-file=FILE Demand authentication from clients, validating
authentication credentials against Apache
htpasswd file FILE.
authentication credentials against FILE, which is
an Apache htpasswd file that employs CRYPT
encryption. (Sorry, no DIGEST support yet.)
--gui (-g) Pop up a graphical interface for serving and
testing ViewVC. NOTE: this requires a valid

View File

@@ -572,6 +572,14 @@
##
#show_subdir_lastmod = 0
## show_roots_lastmod: In the root listing view, show the most recent
## modifications made to the root. (Subversion roots only.)
##
## NOTE: Enabling this feature will significantly reduce the
## performance of the root listing view.
##
#show_roots_lastmod = 0
## show_logs: Show the most recent log entry in directory listings.
##
#show_logs = 1
@@ -649,6 +657,26 @@
##
#log_pagesize = 0
## log_pagesextra: Maximum number of extra pages (based on
## log_pagesize) of revision log data to fetch and present to the user
## as additional options for display. Revision log information
## "beyond" this window is still accessible, but must be navigated to
## in multiple steps.
##
## Example:
## log_pagesize = 100
## log_pagesextra = 3
##
## For a versioned file with 1000 revisions, the above settings would
## present to the user the first 100 of those 1000 revisions, with
## links to three additional pages (the 200-299th revisions, 300-399th
## revisions, and 400-499th revisions) plus a link to the 500th
## revision. Following these links slides the display "window",
## showing the requested set of revisions plus links to three
## additional pages beyond those, and so on.
##
#log_pagesextra = 3
## limit_changes: Maximum number of changed paths shown per commit in
## the Subversion revision view and in query results. This is not a
## hard limit (the UI provides options to show all changed paths), but

View File

@@ -2144,6 +2144,32 @@ td {
<td>List</td>
<td>Set of configured viewable repositories.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.ago</td>
<td>String</td>
<td>Textual description of the time since <var>roots.date</var>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.author</td>
<td>String</td>
<td>Username of the last modifier of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">root.date</td>
<td>String</td>
<td>Date (in UTC if not otherwise configured) of the last
modification of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.href</td>
<td>String</td>
<td>URL of root directory view for a configured repository.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.log</td>
<td>String</td>
<td>Log message of last modification to the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.name</td>
<td>String</td>
@@ -2157,17 +2183,24 @@ td {
configuration can have negative security implications. Use this
token at your own risk.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.rev</td>
<td>String</td>
<td>Youngest revision of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.short_log</td>
<td>String</td>
<td>Log message of last modification to the root, truncated to
contain no more than the number of characters specified by
the <code>short_log_len</code> configuration option.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.type</td>
<td>String</td>
<td>Version control type of a configured repository. Valid
values: <tt>cvs</tt>, <tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.href</td>
<td>String</td>
<td>URL of root directory view for a configured repository.</td>
</tr>
</tbody>
</table>

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -155,8 +155,7 @@ class Config:
self.parser.read(self.conf_path or [])
for section in self.parser.sections():
if self._is_allowed_section(self.parser, section,
self._base_sections):
if self._is_allowed_section(section, self._base_sections):
self._process_section(self.parser, section, section)
if vhost and self.parser.has_section('vhosts'):
@@ -222,7 +221,7 @@ class Config:
setattr(sc, opt, value)
def _is_allowed_section(self, parser, section, allowed_sections):
def _is_allowed_section(self, section, allowed_sections):
"""Return 1 iff SECTION is an allowed section, defined as being
explicitly present in the ALLOWED_SECTIONS list or present in the
form 'someprefix-*' in that list."""
@@ -235,7 +234,7 @@ class Config:
return 1
return 0
def _is_allowed_override(self, parser, sectype, secspec, section):
def _is_allowed_override(self, sectype, secspec, section):
"""Test if SECTION is an allowed override section for sections of
type SECTYPE ('vhosts' or 'root', currently) and type-specifier
SECSPEC (a rootname or vhostname, currently). If it is, return
@@ -248,7 +247,7 @@ class Config:
if section[:lcv] != cv:
return None
base_section = section[lcv:]
if self._is_allowed_section(parser, base_section,
if self._is_allowed_section(base_section,
self._allowed_overrides[sectype]):
return base_section
raise IllegalOverrideSection(sectype, section)
@@ -261,8 +260,7 @@ class Config:
# Overlay any option sections associated with this vhost name.
for section in parser.sections():
base_section = self._is_allowed_override(parser, 'vhost',
canon_vhost, section)
base_section = self._is_allowed_override('vhost', canon_vhost, section)
if base_section:
self._process_section(parser, section, base_section)
@@ -288,8 +286,7 @@ class Config:
return
for section in self.parser.sections():
base_section = self._is_allowed_override(self.parser, 'root',
rootname, section)
base_section = self._is_allowed_override('root', rootname, section)
if base_section:
# We can currently only deal with root overlays happening
# once, so check that we've not yet done any overlaying of
@@ -423,6 +420,7 @@ class Config:
self.options.template_dir = "templates"
self.options.docroot = None
self.options.show_subdir_lastmod = 0
self.options.show_roots_lastmod = 0
self.options.show_logs = 1
self.options.show_log_in_markup = 1
self.options.cross_copies = 1
@@ -436,6 +434,7 @@ class Config:
self.options.use_re_search = 0
self.options.dir_pagesize = 0
self.options.log_pagesize = 0
self.options.log_pagesextra = 3
self.options.limit_changes = 100
self.templates.diff = None

View File

@@ -29,7 +29,15 @@ class GenericViewVCAuthorizer:
def check_root_access(self, rootname):
"""Return 1 iff the associated username is permitted to read ROOTNAME."""
pass
def check_universal_access(self, rootname):
"""Return 1 if the associated username is permitted to read every
path in the repository at every revision, 0 if the associated
username is prohibited from reading any path in the repository, or
None if no such determination can be made (perhaps because the
cost of making it is too great)."""
pass
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
"""Return 1 iff the associated username is permitted to read
revision REV of the path PATH_PARTS (of type PATHTYPE) in
@@ -44,6 +52,9 @@ class ViewVCAuthorizer(GenericViewVCAuthorizer):
"""The uber-permissive authorizer."""
def check_root_access(self, rootname):
return 1
def check_universal_access(self, rootname):
return 1
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
return 1

View File

@@ -23,7 +23,14 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
def check_root_access(self, rootname):
return 1
def check_universal_access(self, rootname):
# If there aren't any forbidden paths, we can grant universal read
# access. Otherwise, we make no claim.
if not self.forbidden:
return 1
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
# No path? No problem.
if not path_parts:

View File

@@ -46,6 +46,13 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
def check_root_access(self, rootname):
return self._check_root_path_access(rootname)
def check_universal_access(self, rootname):
# If there aren't any forbidden regexps, we can grant universal
# read access. Otherwise, we make no claim.
if not self.forbidden:
return 1
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
root_path = rootname
if path_parts:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2011 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -54,7 +54,10 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# option names.
cp = ConfigParser()
cp.optionxform = lambda x: x
cp.read(self.authz_file)
try:
cp.read(self.authz_file)
except:
raise debug.ViewVCException("Unable to parse configured authzfile file")
# Figure out if there are any aliases for the current username
aliases = []
@@ -221,6 +224,36 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
paths = self._get_paths_for_root(rootname)
return (paths is not None) and 1 or 0
def check_universal_access(self, rootname):
paths = self._get_paths_for_root(rootname)
if not paths: # None or empty.
return 0
# Search the access determinations. If there's a mix, we can't
# claim a universal access determination.
found_allow = 0
found_deny = 0
for access in paths.values():
if access:
found_allow = 1
else:
found_deny = 1
if found_allow and found_deny:
return None
# We didn't find both allowances and denials, so we must have
# found one or the other. Denials only is a universal denial.
if found_deny:
return 0
# ... but allowances only is only a universal allowance if read
# access is granted to the root directory.
if found_allow and paths.has_key('/'):
return 1
# Anything else is indeterminable.
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
# Crawl upward from the path represented by PATH_PARTS toward to
# the root of the repository, looking for an explicitly grant or

View File

@@ -40,6 +40,11 @@ class BaseCVSRepository(vclib.Repository):
if not vclib.check_root_access(self):
raise vclib.ReposNotFound(name)
def open(self):
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,6 +15,7 @@
import os
import os.path
import re
import urllib
_re_url = re.compile('^(http|https|file|svn|svn\+[^:]+)://')
@@ -23,8 +24,20 @@ def canonicalize_rootpath(rootpath):
import svn.core
return svn.core.svn_path_canonicalize(rootpath)
except:
if os.name == 'posix':
rootpath_lower = rootpath.lower()
if rootpath_lower in ['file://localhost',
'file://localhost/',
'file://',
'file:///'
]:
return '/'
if rootpath_lower.startswith('file://localhost/'):
return os.path.normpath(urllib.unquote(rootpath[16:]))
elif rootpath_lower.startswith('file:///'):
return os.path.normpath(urllib.unquote(rootpath[7:]))
if re.search(_re_url, rootpath):
return rootpath[-1] == '/' and rootpath[:-1] or rootpath
return rootpath.rstrip('/')
return os.path.normpath(rootpath)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -57,7 +57,7 @@ def client_log(url, start_rev, end_rev, log_limit, cross_copies,
client.svn_client_log4([url], start_rev, start_rev, end_rev,
log_limit, 1, not cross_copies, 0, None,
cb_func, ctx)
except NameError:
except AttributeError:
# Wrap old svn_log_message_receiver_t interface with a
# svn_log_entry_t one.
def cb_convert(paths, revision, author, date, message, pool):
@@ -203,6 +203,10 @@ class RemoteSubversionRepository(vclib.Repository):
self.youngest = ra.svn_ra_get_latest_revnum(self.ra_session)
self._dirent_cache = { }
self._revinfo_cache = { }
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -204,59 +204,6 @@ class NodeHistory:
def __getitem__(self, idx):
return self.histories[idx]
def _get_history(svnrepos, path, rev, path_type, limit=0, options={}):
if svnrepos.youngest == 0:
return []
rev_paths = []
fsroot = svnrepos._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs:
# See if the path is a file or directory.
kind = fs.check_path(fsroot, path)
if kind is core.svn_node_file:
show_all_logs = 1
# Instantiate a NodeHistory collector object, and use it to collect
# history items for PATH@REV.
history = NodeHistory(svnrepos.fs_ptr, show_all_logs, limit)
try:
repos.svn_repos_history(svnrepos.fs_ptr, path, history.add_history,
1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
raise
# Now, iterate over those history items, checking for changes of
# location, pruning as necessitated by authz rules.
for hist_rev, hist_path in history:
path_parts = _path_parts(hist_path)
if not vclib.check_path_access(svnrepos, path_parts, path_type, hist_rev):
break
rev_paths.append([hist_rev, hist_path])
return rev_paths
def _log_helper(svnrepos, path, rev, lockinfo):
rev_root = fs.revision_root(svnrepos.fs_ptr, rev)
# Was this path@rev the target of a copy?
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
# Assemble our LogEntry
date, author, msg, revprops, changes = svnrepos._revinfo(rev)
if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path)
else:
size = None
entry = Revision(rev, date, author, msg, size, lockinfo, path,
copyfrom_path and _cleanup_path(copyfrom_path),
copyfrom_rev)
return entry
def _get_last_history_rev(fsroot, path):
history = fs.node_history(fsroot, path)
history = fs.history_prev(history, 0)
@@ -427,6 +374,10 @@ class LocalSubversionRepository(vclib.Repository):
self._fsroots = {}
self._revinfo_cache = {}
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name
@@ -442,13 +393,8 @@ class LocalSubversionRepository(vclib.Repository):
def itemtype(self, path_parts, rev):
rev = self._getrev(rev)
basepath = self._getpath(path_parts)
kind = fs.check_path(self._getroot(rev), basepath)
pathtype = None
if kind == core.svn_node_dir:
pathtype = vclib.DIR
elif kind == core.svn_node_file:
pathtype = vclib.FILE
else:
pathtype = self._gettype(basepath, rev)
if pathtype is None:
raise vclib.ItemNotFound(path_parts)
if not vclib.check_path_access(self, path_parts, pathtype, rev):
raise vclib.ItemNotFound(path_parts)
@@ -542,20 +488,19 @@ class LocalSubversionRepository(vclib.Repository):
# 'limit' parameter here as numeric cut-off for the depth of our
# history search.
if options.get('svn_latest_log', 0):
revision = _log_helper(self, path, rev, lockinfo)
revision = self._log_helper(path, rev, lockinfo)
if revision:
revision.prev = None
revs.append(revision)
else:
history = _get_history(self, path, rev, path_type,
first + limit, options)
history = self._get_history(path, rev, path_type, first + limit, options)
if len(history) < first:
history = []
if limit:
history = history[first:first+limit]
for hist_rev, hist_path in history:
revision = _log_helper(self, hist_path, hist_rev, lockinfo)
revision = self._log_helper(hist_path, hist_rev, lockinfo)
if revision:
# If we have unreadable copyfrom data, obscure it.
if revision.copy_path is not None:
@@ -583,33 +528,56 @@ class LocalSubversionRepository(vclib.Repository):
raise vclib.Error("Path '%s' is not a file." % path)
rev = self._getrev(rev)
fsroot = self._getroot(rev)
history = _get_history(self, path, rev, path_type, 0,
{'svn_cross_copies': 1})
history = self._get_history(path, rev, path_type, 0,
{'svn_cross_copies': 1})
youngest_rev, youngest_path = history[0]
oldest_rev, oldest_path = history[-1]
source = BlameSource(_rootpath2url(self.rootpath, path),
youngest_rev, oldest_rev, self.config_dir)
return source, youngest_rev
def revinfo(self, rev):
return self._revinfo(rev, 1)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
p2 = self._getpath(path_parts2)
r1 = self._getrev(rev1)
r2 = self._getrev(rev2)
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
raise vclib.ItemNotFound(path_parts1)
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
raise vclib.ItemNotFound(path_parts2)
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, revprops, changes = self._revinfo(rev)
return date
try:
temp1 = temp_checkout(self, p1, r1)
temp2 = temp_checkout(self, p2, r2)
info1 = p1, _date_from_rev(r1), r1
info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
def isexecutable(self, path_parts, rev):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
##--- helpers ---##
def _revinfo(self, rev, include_changed_paths=0):
"""Internal-use, cache-friendly revision information harvester."""
def _revinfo_helper(rev, include_changed_paths):
# Get the revision property info. (Would use
# editor.get_root_props(), but something is broken there...)
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg, author, date, revprops = _split_revprops(revprops)
# Optimization: If our caller doesn't care about the changed
# paths, and we don't need them to do authz determinations, let's
# get outta here.
if self.auth is None and not include_changed_paths:
return date, author, msg, revprops, None
# If we get here, then we either need the changed paths because we
# were asked for them, or we need them to do authorization checks.
# Either way, we need 'em, so let's get 'em.
fsroot = self._getroot(rev)
def _get_changed_paths(fsroot):
"""Return a 3-tuple: found_readable, found_unreadable, changed_paths."""
editor = repos.ChangeCollector(self.fs_ptr, fsroot)
e_ptr, e_baton = delta.make_editor(editor)
repos.svn_repos_replay(fsroot, e_ptr, e_baton)
@@ -662,7 +630,8 @@ class LocalSubversionRepository(vclib.Repository):
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
if not vclib.check_path_access(self, parts, pathtype,
change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
@@ -674,24 +643,108 @@ class LocalSubversionRepository(vclib.Repository):
found_readable = 1
else:
found_unreadable = 1
return found_readable, found_unreadable, changedpaths.values()
def _get_change_copyinfo(fsroot, path, change):
if hasattr(change, 'copyfrom_known') and change.copyfrom_known:
copyfrom_path = change.copyfrom_path
copyfrom_rev = change.copyfrom_rev
else:
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
return copyfrom_path, copyfrom_rev
def _simple_auth_check(fsroot):
"""Return a 2-tuple: found_readable, found_unreadable."""
found_unreadable = found_readable = 0
if hasattr(fs, 'paths_changed2'):
changes = fs.paths_changed2(fsroot)
else:
changes = fs.paths_changed(fsroot)
paths = changes.keys()
for path in paths:
change = changes[path]
pathtype = None
if hasattr(change, 'node_kind'):
if change.node_kind == core.svn_node_file:
pathtype = vclib.FILE
elif change.node_kind == core.svn_node_dir:
pathtype = vclib.DIR
parts = _path_parts(path)
if pathtype is None:
# Figure out the pathtype so we can query the authz subsystem.
if change.change_kind == fs.path_change_delete:
# Deletions are annoying, because they might be underneath
# copies (make their previous location non-trivial).
prev_parts = parts
prev_rev = rev - 1
parent_parts = parts[:-1]
while parent_parts:
parent_path = '/' + self._getpath(parent_parts)
parent_change = changes.get(parent_path)
if not (parent_change and \
(parent_change.change_kind == fs.path_change_add or
parent_change.change_kind == fs.path_change_replace)):
del(parent_parts[-1])
continue
copyfrom_path, copyfrom_rev = \
_get_change_copyinfo(fsroot, parent_path, parent_change)
if copyfrom_path:
prev_rev = copyfrom_rev
prev_parts = _path_parts(copyfrom_path) + \
parts[len(parent_parts):]
break
del(parent_parts[-1])
pathtype = self._gettype(self._getpath(prev_parts), prev_rev)
else:
pathtype = self._gettype(self._getpath(parts), rev)
if vclib.check_path_access(self, parts, pathtype, rev):
found_readable = 1
copyfrom_path, copyfrom_rev = \
_get_change_copyinfo(fsroot, path, change)
if copyfrom_path and copyfrom_path != path:
parts = _path_parts(copyfrom_path)
if not vclib.check_path_access(self, parts, pathtype,
copyfrom_rev):
found_unreadable = 1
else:
found_unreadable = 1
if found_readable and found_unreadable:
break
return found_readable, found_unreadable
def _revinfo_helper(rev, include_changed_paths):
# Get the revision property info. (Would use
# editor.get_root_props(), but something is broken there...)
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg, author, date, revprops = _split_revprops(revprops)
# If our caller doesn't care about changed paths, we must be
# here for authz reasons only. That means the minute we've
# found both a readable and an unreadable path, we can bail out.
if (not include_changed_paths) and found_readable and found_unreadable:
return date, author, None, None, None
# Optimization: If our caller doesn't care about the changed
# paths, and we don't need them to do authz determinations, let's
# get outta here.
if self.auth is None and not include_changed_paths:
return date, author, msg, revprops, None
# If we get here, then we either need the changed paths because we
# were asked for them, or we need them to do authorization checks.
#
# If we only need them for authorization checks, though, we
# won't bother generating fully populated ChangedPath items (the
# cost is too great).
fsroot = self._getroot(rev)
if include_changed_paths:
found_readable, found_unreadable, changedpaths = \
_get_changed_paths(fsroot)
else:
changedpaths = None
found_readable, found_unreadable = _simple_auth_check(fsroot)
# Okay, we've process all our paths. Let's filter our metadata,
# and return the requested data.
# Filter our metadata where necessary, and return the requested data.
if found_unreadable:
msg = None
if not found_readable:
author = None
date = None
if include_changed_paths:
return date, author, msg, revprops, changedpaths.values()
else:
return date, author, msg, revprops, None
return date, author, msg, revprops, changedpaths
# Consult the revinfo cache first. If we don't have cached info,
# or our caller wants changed paths and we don't have those for
@@ -704,40 +757,50 @@ class LocalSubversionRepository(vclib.Repository):
self._revinfo_cache[rev] = cached_info
return tuple(cached_info)
def revinfo(self, rev):
return self._revinfo(rev, 1)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
p2 = self._getpath(path_parts2)
r1 = self._getrev(rev1)
r2 = self._getrev(rev2)
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
raise vclib.ItemNotFound(path_parts1)
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
raise vclib.ItemNotFound(path_parts2)
args = vclib._diff_args(type, options)
def _log_helper(self, path, rev, lockinfo):
rev_root = fs.revision_root(self.fs_ptr, rev)
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
date, author, msg, revprops, changes = self._revinfo(rev)
if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path)
else:
size = None
return Revision(rev, date, author, msg, size, lockinfo, path,
copyfrom_path and _cleanup_path(copyfrom_path),
copyfrom_rev)
def _date_from_rev(rev):
date, author, msg, revprops, changes = self._revinfo(rev)
return date
def _get_history(self, path, rev, path_type, limit=0, options={}):
if self.youngest == 0:
return []
rev_paths = []
fsroot = self._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs:
# See if the path is a file or directory.
kind = fs.check_path(fsroot, path)
if kind is core.svn_node_file:
show_all_logs = 1
# Instantiate a NodeHistory collector object, and use it to collect
# history items for PATH@REV.
history = NodeHistory(self.fs_ptr, show_all_logs, limit)
try:
temp1 = temp_checkout(self, p1, r1)
temp2 = temp_checkout(self, p2, r2)
info1 = p1, _date_from_rev(r1), r1
info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
repos.svn_repos_history(self.fs_ptr, path, history.add_history,
1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
raise
def isexecutable(self, path_parts, rev):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
# Now, iterate over those history items, checking for changes of
# location, pruning as necessitated by authz rules.
for hist_rev, hist_path in history:
path_parts = _path_parts(hist_path)
if not vclib.check_path_access(self, path_parts, path_type, hist_rev):
break
rev_paths.append([hist_rev, hist_path])
return rev_paths
def _getpath(self, path_parts):
return string.join(path_parts, '/')
@@ -760,7 +823,20 @@ class LocalSubversionRepository(vclib.Repository):
r = self._fsroots[rev] = fs.revision_root(self.fs_ptr, rev)
return r
##--- custom --##
def _gettype(self, path, rev):
# Similar to itemtype(), but without the authz check. Returns
# None for missing paths.
try:
kind = fs.check_path(self._getroot(rev), path)
except:
return None
if kind == core.svn_node_dir:
return vclib.DIR
if kind == core.svn_node_file:
return vclib.FILE
return None
##--- custom ---##
def get_youngest_revision(self):
return self.youngest

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,7 @@
#
# -----------------------------------------------------------------------
__version__ = '1.1.7'
__version__ = '1.1.10'
# this comes from our library; measure the startup time
import debug
@@ -79,10 +79,6 @@ _sticky_vars = [
'limit_changes',
]
# number of extra pages of information on either side of the current
# page to fetch (see dir_pagesize/log_pagesize configuration option)
EXTRA_PAGES = 3
# for reading/writing between a couple descriptors
CHUNK_SIZE = 8192
@@ -897,25 +893,37 @@ def get_view_template(cfg, view_name, language="en"):
return template
def get_writeready_server_file(request, content_type=None, encoding=None):
def get_writeready_server_file(request, content_type=None, encoding=None,
content_length=None):
"""Return a file handle to a response body stream, after outputting
any queued special headers (on REQUEST.server) and (optionally) a
'Content-Type' header whose value is CONTENT_TYPE and character set
is ENCODING. After this is called, it is too late to add new
headers to the response."""
is ENCODING.
If CONTENT_LENGTH is provided and compression is not in use, also
generate a 'Content-Length' header for this response.
After this function is called, it is too late to add new headers to
the response."""
if request.gzip_compress_level:
request.server.addheader('Content-Encoding', 'gzip')
elif content_length is not None:
request.server.addheader('Content-Length', content_length)
if content_type and encoding:
request.server.header("%s; charset=%s" % (content_type, encoding))
elif content_type:
request.server.header(content_type)
else:
request.server.header()
if request.gzip_compress_level:
fp = gzip.GzipFile('', 'wb', request.gzip_compress_level,
request.server.file())
else:
fp = request.server.file()
return fp
def generate_page(request, view_name, data, content_type=None):
@@ -1519,6 +1527,21 @@ class MarkupPipeWrapper:
if self.posttext:
ctx.fp.write(self.posttext)
_re_rewrite_escaped_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)'
'(://[-a-zA-Z0-9%.~:_/]+)'
'((\?|\&amp;amp;|\&amp;|\&)'
'([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*'
'(#([-a-zA-Z0-9%.~:_]+)?)?)')
def markup_escaped_urls(s):
# Return a copy of S with all URL references -- which are expected
# to be already HTML-escaped -- wrapped in <a href=""></a>.
def _url_repl(match_obj):
url = match_obj.group(0)
unescaped_url = string.replace(url, "&amp;amp;", "&amp;")
return "<a href=\"%s\">%s</a>" % (unescaped_url, url)
return re.sub(_re_rewrite_escaped_url, _url_repl, s)
def markup_stream_pygments(request, cfg, blame_data, fp, filename,
mime_type, encoding):
# Determine if we should use Pygments to highlight our output.
@@ -1582,6 +1605,7 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
def __getitem__(self, idx):
item = self.blame_source.__getitem__(idx)
item.text = string.expandtabs(item.text, self.tabsize)
item.text = markup_escaped_urls(item.text)
return item
return BlameSourceTabsizeWrapper(blame_source, cfg.options.tabsize)
else:
@@ -1593,6 +1617,7 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
break
line_no = line_no + 1
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
line = markup_escaped_urls(line)
item = vclib.Annotation(line, line_no, None, None, None, None)
item.diff_href = None
lines.append(item)
@@ -1610,6 +1635,7 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
self.line_no = 0
def write(self, buf):
### FIXME: Don't bank on write() being called once per line
buf = markup_escaped_urls(buf)
if self.has_blame_data:
self.blame_data[self.line_no].text = buf
else:
@@ -1808,12 +1834,18 @@ def view_markup(request):
if 'markup' not in request.cfg.options.allowed_views:
raise debug.ViewVCException('Markup view is disabled',
'403 Forbidden')
if request.pathtype != vclib.FILE:
raise debug.ViewVCException('Unsupported feature: markup view on '
'directory', '400 Bad Request')
markup_or_annotate(request, 0)
def view_annotate(request):
if 'annotate' not in request.cfg.options.allowed_views:
raise debug.ViewVCException('Annotation view is disabled',
'403 Forbidden')
if request.pathtype != vclib.FILE:
raise debug.ViewVCException('Unsupported feature: annotate view on '
'directory', '400 Bad Request')
markup_or_annotate(request, 1)
def revcmp(rev1, rev2):
@@ -1888,9 +1920,16 @@ def view_roots(request):
href = request.get_url(view_func=view_directory,
where='', pathtype=vclib.DIR,
params={'root': rootname}, escape=1)
lastmod = allroots[rootname][2]
roots.append(_item(name=request.server.escape(rootname),
type=allroots[rootname][1],
path=allroots[rootname][0],
author=lastmod and lastmod.author or None,
ago=lastmod and lastmod.ago or None,
date=lastmod and lastmod.date or None,
log=lastmod and lastmod.log or None,
short_log=lastmod and lastmod.short_log or None,
rev=lastmod and lastmod.rev or None,
href=href))
data = common_template_data(request)
@@ -2215,10 +2254,11 @@ def paging(data, key, pagestart, local_name, pagesize):
# Slice
return data[key][pagestart:pageend]
def paging_sws(data, key, pagestart, local_name, pagesize, offset):
def paging_sws(data, key, pagestart, local_name, pagesize,
extra_pages, offset):
"""Implement sliding window-style paging."""
# Create the picklist
last_requested = pagestart + (EXTRA_PAGES * pagesize)
last_requested = pagestart + (extra_pages * pagesize)
picklist = data['picklist'] = []
has_more = ezt.boolean(0)
for i in range(0, len(data[key]), pagesize):
@@ -2347,9 +2387,9 @@ def view_log(request):
first = last = 0
if cfg.options.log_pagesize:
log_pagestart = int(request.query_dict.get('log_pagestart', 0))
first = log_pagestart - min(log_pagestart,
(EXTRA_PAGES * cfg.options.log_pagesize))
last = log_pagestart + ((EXTRA_PAGES + 1) * cfg.options.log_pagesize) + 1
total = cfg.options.log_pagesextra * cfg.options.log_pagesize
first = log_pagestart - min(log_pagestart, total)
last = log_pagestart + (total + cfg.options.log_pagesize) + 1
show_revs = request.repos.itemlog(request.path_parts, request.pathrev,
sortby, first, last - first, options)
@@ -2610,7 +2650,8 @@ def view_log(request):
request.get_form(params={'log_pagestart': None})
data['log_pagestart'] = int(request.query_dict.get('log_pagestart',0))
data['entries'] = paging_sws(data, 'entries', data['log_pagestart'],
'rev', cfg.options.log_pagesize, first)
'rev', cfg.options.log_pagesize,
cfg.options.log_pagesextra, first)
generate_page(request, "log", data)
@@ -2751,7 +2792,6 @@ def view_doc(request):
raise debug.ViewVCException('Static file "%s" not available (%s)'
% (document, str(v)), '404 Not Found')
request.server.addheader('Content-Length', content_length)
if document[-3:] == 'png':
mime_type = 'image/png'
elif document[-3:] == 'jpg':
@@ -2762,7 +2802,8 @@ def view_doc(request):
mime_type = 'text/css'
else: # assume HTML:
mime_type = None
copy_stream(fp, get_writeready_server_file(request, mime_type))
copy_stream(fp, get_writeready_server_file(request, mime_type,
content_length=content_length))
fp.close()
def rcsdiff_date_reformat(date_str, cfg):
@@ -4249,11 +4290,26 @@ def list_roots(request):
for root in cfg.general.svn_roots.keys():
auth = setup_authorizer(cfg, request.username, root)
try:
vclib.svn.SubversionRepository(root, cfg.general.svn_roots[root], auth,
cfg.utilities, cfg.options.svn_config_dir)
repos = vclib.svn.SubversionRepository(root, cfg.general.svn_roots[root],
auth, cfg.utilities,
cfg.options.svn_config_dir)
lastmod = None
if cfg.options.show_roots_lastmod:
try:
repos.open()
youngest_rev = repos.youngest
date, author, msg, revprops, changes = repos.revinfo(youngest_rev)
date_str = make_time_string(date, cfg)
ago = html_time(request, date)
log = format_log(request, msg)
short_log = format_log(request, msg, maxlen=cfg.options.short_log_len)
lastmod = _item(ago=ago, author=author, date=date_str, log=log,
short_log=short_log, rev=str(youngest_rev))
except:
lastmod = None
except vclib.ReposNotFound:
continue
allroots[root] = [cfg.general.svn_roots[root], 'svn']
allroots[root] = [cfg.general.svn_roots[root], 'svn', lastmod]
# Add the viewable CVS roots
for root in cfg.general.cvs_roots.keys():
@@ -4263,7 +4319,7 @@ def list_roots(request):
cfg.utilities, cfg.options.use_rcsparse)
except vclib.ReposNotFound:
continue
allroots[root] = [cfg.general.cvs_roots[root], 'cvs']
allroots[root] = [cfg.general.cvs_roots[root], 'cvs', None]
return allroots

View File

@@ -27,7 +27,9 @@ numbers, and not literal):
3. Verify that copyright years are correct in both the license-1.html
file and the source code.
4. Update and commit the 'CHANGES' file.
4. Update and commit the 'CHANGES' file, using any available crystal
balls or other forward-looking devices to take a stab at the
release date.
5. Test, test, test! There is no automatic testsuite available. So
just run with permuting different `viewvc.conf' settings... and
@@ -83,9 +85,12 @@ numbers, and not literal):
14. Upload the created archive files (tar.gz and zip) into the Files
and Documents section of the Tigris.org project, and modify the
CHECKSUMS document there accordingly. Also, drop a copy of the
archive files into the root directory of the viewvc.org website
(unversioned).
CHECKSUMS document there accordingly:
http://viewvc.tigris.org/servlets/ProjectDocumentList?folderID=6004
Also, drop a copy of the archive files into the root directory of
the viewvc.org website (unversioned).
15. On trunk, update the websites (both the viewvc.org/ and www/ ones)
to refer to the new release files, and copy the CHANGES for the
@@ -102,6 +107,9 @@ numbers, and not literal):
(and possibly, minor or major) release. (For the Milestone sort
key, use a packed integer XXYYZZ: 1.0.3 == 10003, 2.11.4 == 21104.)
http://viewvc.tigris.org/issues/editversions.cgi?component=viewvc&action=add
http://viewvc.tigris.org/issues/editmilestones.cgi?component=viewvc&action=add
18. Send to the announce@ list a message explaining all the cool new
features, and post similar announcements to other places interested
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).

View File

@@ -9,6 +9,12 @@
<thead>
<tr>
<th class="vc_header_sort">Name</th>
[is cfg.options.show_roots_lastmod "1"]
<th class="vc_header">Revision</th>
<th class="vc_header">Age</th>
<th class="vc_header">Author</th>
<th class="vc_header">Log</th>
[end]
</tr>
</thead>
@@ -20,6 +26,12 @@
<img src="[docroot]/images/dir.png" alt="" class="vc_icon" />
[roots.name]</a>
</td>
[is cfg.options.show_roots_lastmod "1"]
<td style="width:20">&nbsp;[roots.rev]</td>
<td style="width:20">&nbsp;[roots.ago]</td>
<td style="width:20">&nbsp;[roots.author]</td>
<td style="width:20">&nbsp;[roots.short_log]</td>
[end]
</tr>
[end]
</tbody>