Hello community, here is the log from the commit of package viewvc for openSUSE:Factory checked in at Thu Apr 21 13:46:17 CEST 2011. -------- --- viewvc/viewvc.changes 2010-12-02 23:47:13.000000000 +0100 +++ /mounts/work_src_done/STABLE/viewvc/viewvc.changes 2011-03-15 23:22:12.000000000 +0100 @@ -1,0 +2,20 @@ +Tue Mar 15 22:17:29 UTC 2011 - pascal.bleser@opensuse.org + +- update to 1.1.10: + * 1.1.9 shipped with a stack-trace-causing bug in the Subversion revision + info gathering logic + +------------------------------------------------------------------- +Sat Feb 19 00:42:56 UTC 2011 - pascal.bleser@opensuse.org + +- update to 1.1.9: + * 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) + +------------------------------------------------------------------- @@ -12 +32,2 @@ - * maintenance release that includes all the bugfixes and enhancements made thus far to the 1.1.x line + * maintenance release that includes all the bugfixes and enhancements made + thus far to the 1.1.x line calling whatdependson for head-i586 Old: ---- viewvc-1.1.8.tar.bz2 New: ---- viewvc-1.1.10.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ viewvc.spec ++++++ --- /var/tmp/diff_new_pack.7SVTR5/_old 2011-04-21 13:44:58.000000000 +0200 +++ /var/tmp/diff_new_pack.7SVTR5/_new 2011-04-21 13:44:58.000000000 +0200 @@ -1,5 +1,5 @@ # -# spec file for package viewvc (Version 1.1.8) +# spec file for package viewvc # # Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. # @@ -20,7 +20,7 @@ Name: viewvc BuildRequires: apache2-devel python-devel -Version: 1.1.8 +Version: 1.1.10 Release: 1 # %define apxs /usr/sbin/apxs2 ++++++ viewvc-1.1.8.tar.bz2 -> viewvc-1.1.10.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/CHANGES new/viewvc-1.1.10/CHANGES --- old/viewvc-1.1.8/CHANGES 2010-12-02 21:46:06.000000000 +0100 +++ new/viewvc-1.1.10/CHANGES 2011-03-15 15:57:50.000000000 +0100 @@ -1,3 +1,18 @@ +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) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/INSTALL new/viewvc-1.1.10/INSTALL --- old/viewvc-1.1.8/INSTALL 2010-09-07 21:20:22.000000000 +0200 +++ new/viewvc-1.1.10/INSTALL 2011-02-07 15:49:01.000000000 +0100 @@ -19,7 +19,7 @@ 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 @@ 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) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/LICENSE.html new/viewvc-1.1.10/LICENSE.html --- old/viewvc-1.1.8/LICENSE.html 2010-03-29 17:37:39.000000000 +0200 +++ new/viewvc-1.1.10/LICENSE.html 2011-02-18 20:27:50.000000000 +0100 @@ -15,7 +15,7 @@ <blockquote> -<p><strong>Copyright © 1999-2010 The ViewCVS Group. All rights +<p><strong>Copyright © 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 — copyright years updated</li> <li>March 18, 2009 — copyright years updated</li> <li>March 29, 2010 — copyright years updated</li> + <li>February 18, 2011 — copyright years updated</li> </ul> </body> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/conf/viewvc.conf.dist new/viewvc-1.1.10/conf/viewvc.conf.dist --- old/viewvc-1.1.8/conf/viewvc.conf.dist 2010-09-09 17:31:52.000000000 +0200 +++ new/viewvc-1.1.10/conf/viewvc.conf.dist 2011-02-18 19:28:55.000000000 +0100 @@ -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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/docs/template-authoring-guide.html new/viewvc-1.1.10/docs/template-authoring-guide.html --- old/viewvc-1.1.8/docs/template-authoring-guide.html 2009-10-25 21:57:16.000000000 +0100 +++ new/viewvc-1.1.10/docs/template-authoring-guide.html 2011-02-18 16:29:11.000000000 +0100 @@ -2145,6 +2145,32 @@ <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> <td>Name of a configured repository.</td> @@ -2158,15 +2184,22 @@ token at your own risk.</td> </tr> <tr class="varlevel2"> - <td class="varname">roots.type</td> + <td class="varname">roots.rev</td> <td>String</td> - <td>Version control type of a configured repository. Valid - values: <tt>cvs</tt>, <tt>svn</tt>.</td> + <td>Youngest revision of the root.</td> </tr> <tr class="varlevel2"> - <td class="varname">roots.href</td> + <td class="varname">roots.short_log</td> <td>String</td> - <td>URL of root directory view for a configured repository.</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> </tbody> </table> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/config.py new/viewvc-1.1.10/lib/config.py --- old/viewvc-1.1.8/lib/config.py 2010-11-30 17:44:16.000000000 +0100 +++ new/viewvc-1.1.10/lib/config.py 2011-02-18 20:27:50.000000000 +0100 @@ -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 @@ -420,6 +420,7 @@ 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 @@ -433,6 +434,7 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/vcauth/__init__.py new/viewvc-1.1.10/lib/vcauth/__init__.py --- old/viewvc-1.1.8/lib/vcauth/__init__.py 2008-02-28 17:11:24.000000000 +0100 +++ new/viewvc-1.1.10/lib/vcauth/__init__.py 2010-12-09 17:22:05.000000000 +0100 @@ -29,7 +29,15 @@ 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 @@ """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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/vcauth/forbidden/__init__.py new/viewvc-1.1.10/lib/vcauth/forbidden/__init__.py --- old/viewvc-1.1.8/lib/vcauth/forbidden/__init__.py 2008-02-28 17:11:24.000000000 +0100 +++ new/viewvc-1.1.10/lib/vcauth/forbidden/__init__.py 2010-12-09 17:22:05.000000000 +0100 @@ -23,7 +23,14 @@ 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: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/vcauth/forbiddenre/__init__.py new/viewvc-1.1.10/lib/vcauth/forbiddenre/__init__.py --- old/viewvc-1.1.8/lib/vcauth/forbiddenre/__init__.py 2008-03-18 22:08:19.000000000 +0100 +++ new/viewvc-1.1.10/lib/vcauth/forbiddenre/__init__.py 2010-12-09 17:22:05.000000000 +0100 @@ -46,6 +46,13 @@ 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: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/vcauth/svnauthz/__init__.py new/viewvc-1.1.10/lib/vcauth/svnauthz/__init__.py --- old/viewvc-1.1.8/lib/vcauth/svnauthz/__init__.py 2009-06-05 21:05:00.000000000 +0200 +++ new/viewvc-1.1.10/lib/vcauth/svnauthz/__init__.py 2011-02-18 20:27:50.000000000 +0100 @@ -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 @@ # 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 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/vclib/ccvs/bincvs.py new/viewvc-1.1.10/lib/vclib/ccvs/bincvs.py --- old/viewvc-1.1.8/lib/vclib/ccvs/bincvs.py 2008-08-21 15:37:46.000000000 +0200 +++ new/viewvc-1.1.10/lib/vclib/ccvs/bincvs.py 2010-12-09 17:22:05.000000000 +0100 @@ -40,6 +40,11 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/vclib/svn/__init__.py new/viewvc-1.1.10/lib/vclib/svn/__init__.py --- old/viewvc-1.1.8/lib/vclib/svn/__init__.py 2008-06-12 18:26:33.000000000 +0200 +++ new/viewvc-1.1.10/lib/vclib/svn/__init__.py 2011-02-18 20:27:50.000000000 +0100 @@ -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 @@ 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) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/vclib/svn/svn_ra.py new/viewvc-1.1.10/lib/vclib/svn/svn_ra.py --- old/viewvc-1.1.8/lib/vclib/svn/svn_ra.py 2010-09-07 21:22:26.000000000 +0200 +++ new/viewvc-1.1.10/lib/vclib/svn/svn_ra.py 2011-02-18 20:27:50.000000000 +0100 @@ -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 @@ 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 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/vclib/svn/svn_repos.py new/viewvc-1.1.10/lib/vclib/svn/svn_repos.py --- old/viewvc-1.1.8/lib/vclib/svn/svn_repos.py 2010-09-08 20:28:25.000000000 +0200 +++ new/viewvc-1.1.10/lib/vclib/svn/svn_repos.py 2011-03-11 21:22:58.000000000 +0100 @@ -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 @@ 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 @@ 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 @@ 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 @@ # '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 @@ 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 @@ 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 @@ 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 @@ 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 _date_from_rev(rev): - date, author, msg, revprops, changes = self._revinfo(rev) - return date + 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 _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 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/lib/viewvc.py new/viewvc-1.1.10/lib/viewvc.py --- old/viewvc-1.1.8/lib/viewvc.py 2010-12-02 21:47:46.000000000 +0100 +++ new/viewvc-1.1.10/lib/viewvc.py 2011-03-15 17:23:58.000000000 +0100 @@ -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.8' +__version__ = '1.1.10' # this comes from our library; measure the startup time import debug @@ -79,10 +79,6 @@ '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 @@ -1531,6 +1527,21 @@ 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%.~:_/]+)' + '((\?|\&|\&|\&)' + '([-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, "&", "&") + return "%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. @@ -1594,6 +1605,7 @@ 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: @@ -1605,6 +1617,7 @@ 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) @@ -1622,6 +1635,7 @@ 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: @@ -1906,9 +1920,16 @@ 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) @@ -2233,10 +2254,11 @@ # 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): @@ -2365,9 +2387,9 @@ 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) @@ -2628,7 +2650,8 @@ 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) @@ -4267,11 +4290,26 @@ 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(): @@ -4281,7 +4319,7 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/templates/roots.ezt new/viewvc-1.1.10/templates/roots.ezt --- old/viewvc-1.1.8/templates/roots.ezt 2006-04-14 06:35:13.000000000 +0200 +++ new/viewvc-1.1.10/templates/roots.ezt 2011-02-18 17:57:47.000000000 +0100 @@ -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"> [roots.rev]</td> + <td style="width:20"> [roots.ago]</td> + <td style="width:20"> [roots.author]</td> + <td style="width:20"> [roots.short_log]</td> +[end] </tr> [end] </tbody> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/viewvc-1.1.8/templates-contrib/newvc/templates/roots.ezt new/viewvc-1.1.10/templates-contrib/newvc/templates/roots.ezt --- old/viewvc-1.1.8/templates-contrib/newvc/templates/roots.ezt 2008-05-23 15:09:17.000000000 +0200 +++ new/viewvc-1.1.10/templates-contrib/newvc/templates/roots.ezt 2011-02-18 18:07:40.000000000 +0100 @@ -8,10 +8,13 @@ <div id="vc_main_body"> <!-- ************************************************************** --> -<table cellspacing="1" id="dirlist"> +<table cellspacing="1" class="fixed" id="dirlist"> <thead> <tr> - <th class="vc_header_sort">Name</th> + <th style="width: 200px" class="vc_header_sort">Name</th> +[is cfg.options.show_roots_lastmod "1"] + <th class="vc_header">Last Change</th> +[end] </tr> </thead> @@ -20,6 +23,10 @@ [for roots] <tr class="vc_row_[if-index roots even]even[else]odd[end]"> <td onclick="jumpTo('[roots.href]')"><a href="[roots.href]"><img src="[docroot]/images/[roots.type]-logo.png" alt="" class="vc_icon" />[roots.name]</a></td> +[is cfg.options.show_roots_lastmod "1"] + <td>[if-any roots.rev]<strong>[roots.rev]</strong> ([roots.ago] ago) + by <em>[roots.author]</em>: [roots.log][end]</td> +[end] </tr> [end] [end] ++++++ viewvc-buglink.patch ++++++ --- /var/tmp/diff_new_pack.7SVTR5/_old 2011-04-21 13:44:59.000000000 +0200 +++ /var/tmp/diff_new_pack.7SVTR5/_new 2011-04-21 13:44:59.000000000 +0200 @@ -1,5 +1,5 @@ ---- conf/viewvc.conf.dist.orig 2010-09-12 03:47:52.000000000 +0200 -+++ conf/viewvc.conf.dist 2010-09-12 03:48:38.000000000 +0200 +--- conf/viewvc.conf.dist.orig 2011-02-18 19:28:55.000000000 +0100 ++++ conf/viewvc.conf.dist 2011-02-19 01:46:36.000000000 +0100 @@ -323,6 +323,21 @@ ##--------------------------------------------------------------------------- [options] @@ -22,9 +22,9 @@ ## root_as_url_component: Interpret the first path component in the URL ## after the script location as the root to use. This is an ## alternative to using the "root=" query key. If ViewVC is configured ---- lib/viewvc.py.orig 2010-09-12 03:49:13.000000000 +0200 -+++ lib/viewvc.py 2010-09-12 03:50:28.000000000 +0200 -@@ -1092,6 +1092,10 @@ +--- lib/viewvc.py.orig 2011-02-18 20:43:26.000000000 +0100 ++++ lib/viewvc.py 2011-02-19 01:46:36.000000000 +0100 +@@ -1100,6 +1100,10 @@ # Matches revision references _re_rewrite_svnrevref = re.compile(r'\b(r|rev #?|revision #?)([0-9]+)\b') @@ -35,7 +35,7 @@ class ViewVCHtmlFormatter: """Format a string as HTML-encoded output with customizable markup rules, for example turning strings that look like URLs into anchor links. -@@ -1104,6 +1108,19 @@ +@@ -1112,6 +1116,19 @@ def __init__(self): self._formatters = [] @@ -55,7 +55,7 @@ def format_url(self, mobj, userdata, maxlen=0): """Return a 2-tuple containing: - the text represented by MatchObject MOBJ, formatted as -@@ -1281,6 +1298,10 @@ +@@ -1289,6 +1306,10 @@ escape=1) lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref, revision_to_url) @@ -66,11 +66,11 @@ if cfg.options.mangle_email_addresses == 2: lf.add_formatter(_re_rewrite_email, lf.format_email_truncated) elif cfg.options.mangle_email_addresses == 1: ---- lib/config.py.orig 2010-09-12 03:48:44.000000000 +0200 -+++ lib/config.py 2010-09-12 03:49:08.000000000 +0200 -@@ -437,6 +437,7 @@ - self.options.dir_pagesize = 0 +--- lib/config.py.orig 2011-02-18 20:27:50.000000000 +0100 ++++ lib/config.py 2011-02-19 01:46:56.000000000 +0100 +@@ -436,6 +436,7 @@ self.options.log_pagesize = 0 + self.options.log_pagesextra = 3 self.options.limit_changes = 100 + self.options.buglink_base = None ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Remember to have fun... -- To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org For additional commands, e-mail: opensuse-commit+help@opensuse.org