<?php
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright (C) 2004-2006 Tim Armes
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA     02111-1307      USA
//
// --
//
// configclass.php
//
// General class for handling configuration options

require_once 'include/command.php';
require_once 'include/authz.php';
require_once 'include/version.php';

// Auxillary functions used to sort repositories by name/group

// {{{ cmpReps($a, $b)

function cmpReps($a, $b) {
        // First, sort by group
        $g = strcasecmp((string) $a->group, (string) $b->group);
        if ($g) return $g;

        // Same group? Sort by name
        return strcasecmp($a->name, $b->name);
}

// }}}

// {{{ cmpGroups($a, $b)

function cmpGroups($a, $b) {
        $g = strcasecmp((string) $a->group, (string) $b->group);
        if ($g) return $g;

        return 0;
}

// }}}

// {{{ mergesort(&$array, [$cmp_function])

function mergesort(&$array, $cmp_function = 'strcmp') {
        // Arrays of size < 2 require no action

        if (count($array) < 2) return;

        // Split the array in half
        $halfway = count($array) / 2;
        $array1 = array_slice($array, 0, $halfway);
        $array2 = array_slice($array, $halfway);

        // Recurse to sort the two halves
        mergesort($array1, $cmp_function);
        mergesort($array2, $cmp_function);

        // If all of $array1 is <= all of $array2, just append them.
        if (call_user_func($cmp_function, end($array1), $array2[0]) < 1) {
                $array = array_merge($array1, $array2);
                return;
        }

        // Merge the two sorted arrays into a single sorted array
        $array = array();
        $array1count = count($array1);
        $array2count = count($array2);
        $ptr1 = 0;
        $ptr2 = 0;
        while ($ptr1 < $array1count && $ptr2 < $array2count) {
                if (call_user_func($cmp_function, $array1[$ptr1], $array2[$ptr2]) < 1) {
                        $array[] = $array1[$ptr1++];
                } else {
                        $array[] = $array2[$ptr2++];
                }
        }

        // Merge the remainder
        while ($ptr1 < $array1count) $array[] = $array1[$ptr1++];
        while ($ptr2 < $array2count) $array[] = $array2[$ptr2++];

        return;
}

// }}}

// A Repository parent path configuration class

class ParentPath {
        // {{{ Properties

        var $path;
        var $group;
        var $pattern;
        var $skipAlreadyAdded;
        var $clientRootURL;

        // }}}

        // {{{ __construct($path [, $group [, $pattern [, $skipAlreadyAdded [, $clientRootURL]]]])
        function __construct($path, $group = null, $pattern = false, $skipAlreadyAdded = true, $clientRootURL = '') {
                $this->path = $path;
                $this->group = $group;
                $this->pattern = $pattern;
                $this->skipAlreadyAdded = $skipAlreadyAdded;
                $this->clientRootURL = rtrim($clientRootURL, '/');
        }
        // }}}

        // {{{ findRepository($name)
        // look for a repository with $name
        function &findRepository($name) {
                global $config;
                if ($this->group != null) {
                        $prefix = $this->group.'.';
                        if (substr($name, 0, strlen($prefix)) == $prefix) {
                                $name = substr($name, strlen($prefix));
                        } else {
                                $null = null;
                                return $null;
                        }
                }
                // is there a directory named $name?
                $fullpath = $this->path.DIRECTORY_SEPARATOR.$name;
                if (is_dir($fullpath) && is_readable($fullpath)) {
                        // And that contains a db directory (in an attempt to not include non svn repositories.
                        $dbfullpath = $fullpath.DIRECTORY_SEPARATOR.'db';
                        if (is_dir($dbfullpath) && is_readable($dbfullpath)) {
                                // And matches the pattern if specified
                                if ($this->pattern === false || preg_match($this->pattern, $name)) {
                                        $url = $config->fileUrlPrefix.$fullpath;
                                        $url = str_replace(DIRECTORY_SEPARATOR, '/', $url);
                                        if ($url[ strlen($url) - 1 ] == '/') {
                                                $url = substr($url, 0, -1);
                                        }

                                        if (!in_array($url, $config->_excluded, true)) {
                                                $clientRootURL = ($this->clientRootURL) ? $this->clientRootURL.'/'.$name : '';
                                                $rep = new Repository($name, $name, $url, $this->group, null, null, null, $clientRootURL);
                                                return $rep;
                                        }
                                }
                        }
                }
                $null = null;
                return $null;
        }
        // }}}

        // {{{ getRepositories()
        // return all repositories in the parent path matching pattern
        function &getRepositories() {
                global $config;
                $repos = array();
                $handle = @opendir($this->path);

                if (!$handle) return $repos;

                // For each file...
                while (false !== ($name = readdir($handle))) {
                        $fullpath = $this->path.DIRECTORY_SEPARATOR.$name;
                        if ($name[0] != '.' && is_dir($fullpath) && is_readable($fullpath)) {
                                // And that contains a db directory (in an attempt to not include non svn repositories.
                                $dbfullpath = $fullpath.DIRECTORY_SEPARATOR.'db';
                                if (is_dir($dbfullpath) && is_readable($dbfullpath)) {
                                        // And matches the pattern if specified
                                        if ($this->pattern === false || preg_match($this->pattern, $name)) {
                                                $url = $config->fileUrlPrefix.$fullpath;
                                                $url = str_replace(DIRECTORY_SEPARATOR, '/', $url);
                                                if ($url[strlen($url) - 1] == '/') {
                                                        $url = substr($url, 0, -1);
                                                }
                                                $clientRootURL = ($this->clientRootURL) ? $this->clientRootURL.'/'.$name : '';
                                                $repos[] = new Repository($name, $name, $url, $this->group, null, null, null, $clientRootURL);
                                        }
                                }
                        }
                }
                closedir($handle);

                // Sort the repositories into alphabetical order
                if (!empty($repos)) {
                        usort($repos, 'cmpReps');
                }

                return $repos;
        }
        // }}}

        // {{{ getSkipAlreadyAdded()
        // Return if we should skip already added repos for this parent path.
        function getSkipAlreadyAdded() {
                return $this->skipAlreadyAdded;
        }
        // }}}
}

// A Repository configuration class

class Repository {
        // {{{ Properties

        var $name;
        var $svnName;
        var $path;
        var $subpath;
        var $group;
        var $username = null;
        var $password = null;
        var $clientRootURL;

        // Local configuration options must start off unset

        var $allowDownload;
        var $minDownloadLevel;
        var $allowedExceptions = array();
        var $disallowedExceptions = array();
        var $logsShowChanges;
        var $rss;
        var $rssCaching;
        var $rssMaxEntries;
        var $spaces;
        var $ignoreSvnMimeTypes;
        var $ignoreWebSVNContentTypes;
        var $bugtraq;
        var $bugtraqProperties;
        var $authz = null;
        var $templatePath = false;

        // }}}

        // {{{ __construct($name, $svnName, $serverRootURL [, $group [, $username [, $password [, $clientRootURL]]]])

        function __construct($name, $svnName, $serverRootURL, $group = null, $username = null, $password = null, $subpath = null, $clientRootURL = null) {
                $this->name = $name;
                $this->svnName = $svnName;
                $this->path = $serverRootURL;
                $this->subpath = $subpath;
                $this->group = $group;
                $this->username = $username;
                $this->password = $password;
                $this->clientRootURL = rtrim($clientRootURL, '/');
        }

        // }}}

        // {{{ getDisplayName()

        function getDisplayName() {
                if (!empty($this->group)) {
                        return $this->group.'.'.$this->name;
                }

                return $this->name;
        }

        // }}}

        // {{{ svnCredentials

        function svnCredentials() {
                $params = '';
                if ($this->username !== null && $this->username !== '') {
                        $params .= ' --username '.quote($this->username);
                }
                if ($this->password !== null) {
                        $params .= ' --password '.quote($this->password);
                }
                return $params;
        }

        // }}}

        // Local configuration accessors

        function setLogsShowChanges($enabled = true) {
                $this->logsShowChanges = $enabled;
        }

        function logsShowChanges() {
                global $config;

                if (isset($this->logsShowChanges))
                        return $this->logsShowChanges;
                else
                        return $config->logsShowChanges();
        }

        // {{{ RSS Feed

        function setRssEnabled($enabled) {
                $this->rss = $enabled;
        }

        function isRssEnabled() {
                global $config;

                if (isset($this->rss))
                        return $this->rss;
                else
                        return $config->isRssEnabled();
        }

        function setRssCachingEnabled($enabled = true) {
                $this->rssCaching = $enabled;
        }

        function isRssCachingEnabled() {
                global $config;

                if (isset($this->rssCaching))
                        return $this->rssCaching;
                else
                        return $config->isRssCachingEnabled();
        }

        function setRssMaxEntries($max) {
                $this->rssMaxEntries = $max;
        }

        function getRssMaxEntries() {
                global $config;

                if (isset($this->rssMaxEntries))
                        return $this->rssMaxEntries;
                else
                        return $config->getRssMaxEntries();
        }

        // }}}

        // {{{ Download

        function allowDownload() {
                $this->allowDownload = true;
        }

        function disallowDownload() {
                $this->allowDownload = false;
        }

        function getAllowDownload() {
                global $config;

                if (isset($this->allowDownload)) {
                        return $this->allowDownload;
                }

                return $config->getAllowDownload();
        }

        function setMinDownloadLevel($level) {
                $this->minDownloadLevel = $level;
        }

        function getMinDownloadLevel() {
                global $config;

                if (isset($this->minDownloadLevel)) {
                        return $this->minDownloadLevel;
                }

                return $config->getMinDownloadLevel();
        }

        function addAllowedDownloadException($path) {
                if ($path[strlen($path) - 1] != '/') $path .= '/';

                $this->allowedExceptions[] = $path;
        }

        function addDisallowedDownloadException($path) {
                if ($path[strlen($path) - 1] != '/') $path .= '/';

                $this->disallowedExceptions[] = $path;
        }

        function isDownloadAllowed($path) {
                global $config;

                // Check global download option
                if (!$this->getAllowDownload()) {
                        return false;
                }

                // Check with access module
                if (!$this->hasUnrestrictedReadAccess($path)) {
                        return false;
                }

                $subs = explode('/', $path);
                $level = count($subs) - 2;
                if ($level >= $this->getMinDownloadLevel()) {
                        // Level OK, search for disallowed exceptions

                        if ($config->findException($path, $this->disallowedExceptions)) {
                                return false;
                        }

                        if ($config->findException($path, $config->disallowedExceptions)) {
                                return false;
                        }

                        return true;

                } else {
                        // Level not OK, search for disallowed exceptions

                        if ($config->findException($path, $this->allowedExceptions)) {
                                return true;
                        }

                        if ($config->findException($path, $config->allowedExceptions)) {
                                return true;
                        }

                        return false;
                }
        }

        // }}}

        // {{{ Templates

        function setTemplatePath($path) {
                $this->templatePath = $path;
        }

        function getTemplatePath() {
                global $config;
                if (!empty($this->templatePath)) {
                        return $this->templatePath;
                }

                return $config->getTemplatePath();
        }

        // }}}

        // {{{ Tab expansion

        function expandTabsBy($sp) {
                $this->spaces = $sp;
        }

        function getExpandTabsBy() {
                global $config;

                if (isset($this->spaces)) {
                        return $this->spaces;
                }

                return $config->getExpandTabsBy();
        }

        // }}}

        // {{{ MIME-Type Handing

        function ignoreSvnMimeTypes() {
                $this->ignoreSvnMimeTypes = true;
        }

        function useSvnMimeTypes() {
                $this->ignoreSvnMimeTypes = false;
        }

        function getIgnoreSvnMimeTypes() {
                global $config;

                if (isset($this->ignoreSvnMimeTypes)) {
                        return $this->ignoreSvnMimeTypes;
                }

                return $config->getIgnoreSvnMimeTypes();
        }

        function ignoreWebSVNContentTypes() {
                $this->ignoreWebSVNContentTypes = true;
        }

        function useWebSVNContentTypes() {
                $this->ignoreWebSVNContentTypes = false;
        }

        function getIgnoreWebSVNContentTypes() {
                global $config;

                if (isset($this->ignoreWebSVNContentTypes)) {
                        return $this->ignoreWebSVNContentTypes;
                }

                return $config->getIgnoreWebSVNContentTypes();
        }

        // }}}

        // {{{ Bugtraq issue tracking

        function setBugtraqEnabled($enabled) {
                $this->bugtraq = $enabled;
        }

        function isBugtraqEnabled() {
                global $config;

                if (isset($this->bugtraq))
                        return $this->bugtraq;
                else
                        return $config->isBugtraqEnabled();
        }

        function setBugtraqProperties($properties) {
                $this->bugtraqProperties = $properties;
        }

        function getBugtraqProperties() {
                global $config;

                if (isset($this->bugtraqProperties))
                        return $this->bugtraqProperties;
                else
                        return $config->getBugtraqProperties();
        }

        // }}}

        // {{{ Authorization

        function useAccessFile($file) {
                if (is_readable($file)) {
                        if ($this->authz === null) {
                                $this->authz = new Authorization();
                        }
                        $this->authz->addAccessFile($file);
                } else {
                        die('Unable to read access file "'.$file.'"');
                }
        }

        function &getAuthz() {
                global $config;

                $a = null;
                if ($this->authz !== null) {
                        $a =& $this->authz;
                } else {
                        $a =& $config->getAuthz();
                }
                return $a;
        }

        function _getPathWithSubIf($pathWoSub) {
                if (!$this->subpath) {
                        return $pathWoSub;
                }

                return '/' . $this->subpath . $pathWoSub;
        }

        function hasReadAccess($pathWoSub, $checkSubDirs = false) {
                $path   =  $this->_getPathWithSubIf($pathWoSub);
                $a              =& $this->getAuthz();

                if (!empty($a)) {
                        return $a->hasReadAccess($this->svnName, $path, $checkSubDirs);
                }

                // No access file - free access...
                return true;
        }

        function hasLogReadAccess($pathWithSub) {
                $path   =  $pathWithSub;
                $a              =& $this->getAuthz();

                if (!empty($a)) {
                        return $a->hasReadAccess($this->svnName, $path, false);
                }

                // No access file - free access...
                return true;
        }

        function hasUnrestrictedReadAccess($pathWoSub) {
                $path   =  $this->_getPathWithSubIf($pathWoSub);
                $a              =& $this->getAuthz();

                if (!empty($a)) {
                        return $a->hasUnrestrictedReadAccess($this->svnName, $path);
                }

                // No access file - free access...
                return true;
        }

        // }}}

}

// The general configuration class

class WebSvnConfig {
        // {{{ Properties

        // Tool path locations

        var $_svnCommandPath = '';
        var $_svnConfigDir = '/tmp/websvn';
        var $_svnTrustServerCert = false;
        var $svn = 'svn --non-interactive --config-dir /tmp/websvn';
        var $svnAuthz = 'svnauthz accessof';
        var $diff = 'diff';
        var $enscript = 'enscript -q';
        var $sed = 'sed';
        var $gzip = 'gzip';
        var $tar = 'tar';
        var $zip = 'zip';
        var $locale = '';

        // different modes for file and directory download

        var $defaultFileDlMode = 'plain';
        var $defaultDirectoryDlMode = 'gzip';

        var $validFileDlModes = array( 'gzip', 'zip', 'plain' );
        var $validDirectoryDlModes = array( 'gzip', 'zip' );

        // Other configuration items

        var $treeView = true;
        var $flatIndex = true;
        var $openTree = false;
        var $alphabetic = false;
        var $showLastModInIndex = true;
        var $showLastModInListing = true;
        var $showAgeInsteadOfDate = true;
        var $_showRepositorySelectionForm = true;
        var $_ignoreWhitespacesInDiff = false;
        var $serverIsWindows = false;
        var $multiViews = false;
        var $multiViewsIndex = 'browse';
        var $useEnscript = false;
        var $useEnscriptBefore_1_6_3 = false;
        var $useGeshi = false;
        var $geshiScript = 'geshi.php';
        var $useParsedown = false;
        var $parsedownScript = 'Parsedown.php';
        var $inlineMimeTypes = array();
        var $allowDownload = false;
        var $tempDir = '';
        var $minDownloadLevel = 0;
        var $allowedExceptions = array();
        var $disallowedExceptions = array();
        var $logsShowChanges = false;
        var $rss = true;
        var $rssCaching = false;
        var $rssMaxEntries = 40;
        var $spaces = 8;
        var $bugtraq = false;
        var $bugtraqProperties = null;
        var $authz = null;
        var $blockRobots = false;

        var $loadAllRepos = false;

        var $templatePaths = array();
        var $userTemplate = false;

        var $ignoreSvnMimeTypes = false;
        var $ignoreWebSVNContentTypes = false;

        var $subversionVersion = '';
        var $subversionMajorVersion = '';
        var $subversionMinorVersion = '';

        var $defaultLanguage = 'en';
        var $ignoreAcceptedLanguages = false;

        var $quote = "'";
        var $pathSeparator = ':';
        var $fileUrlPrefix = 'file://';
        var $breadcrumbRepoRootAsRepo = false;

        var $_repositories = array();

        var $_parentPaths = array();    // parent paths to load

        var $_parentPathsLoaded = false;

        var $_excluded = array();

        // }}}

        // {{{ __construct()

        function __construct() {
        }

        // }}}

        // {{{ Repository configuration

        function addRepository($name, $serverRootURL, $group = null, $username = null, $password = null, $clientRootURL = null) {
                $this->addRepositorySubpath($name, $serverRootURL, null, $group, $username, $password, $clientRootURL);
        }

        function addRepositorySubpath($name, $serverRootURL, $subpath, $group = null, $username = null, $password = null, $clientRootURL = null) {
                if (DIRECTORY_SEPARATOR != '/') {
                        $serverRootURL = str_replace(DIRECTORY_SEPARATOR, '/', $serverRootURL);
                        if ($subpath !== null) {
                                $subpath = str_replace(DIRECTORY_SEPARATOR, '/', $subpath);
                        }
                }
                $serverRootURL = trim($serverRootURL, '/');
                $svnName = substr($serverRootURL, strrpos($serverRootURL, '/') + 1);
                $this->_repositories[] = new Repository($name, $svnName, $serverRootURL, $group, $username, $password, $subpath, $clientRootURL);
        }

        // Automatically set up the repositories based on a parent path

        function parentPath($path, $group = null, $pattern = false, $skipAlreadyAdded = true, $clientRootURL = '') {
                $this->_parentPaths[] = new ParentPath($path, $group, $pattern, $skipAlreadyAdded, $clientRootURL);
        }

        function addExcludedPath($path) {
                $url = $this->fileUrlPrefix.$path;
                $url = str_replace(DIRECTORY_SEPARATOR, '/', $url);
                if ($url[strlen($url) - 1] == '/') {
                        $url = substr($url, 0, -1);
                }
                $this->_excluded[] = $url;
        }

        function getRepositories() {
                // lazily load parent paths
                if ($this->_parentPathsLoaded) return $this->_repositories;

                $this->_parentPathsLoaded = true;

                foreach ($this->_parentPaths as $parentPath) {
                        $parentRepos = $parentPath->getRepositories();
                        foreach ($parentRepos as $repo) {
                                if (!$parentPath->getSkipAlreadyAdded()) {
                                        $this->_repositories[] = $repo;
                                } else {
                                        // we have to check if we already have a repo with the same svn name
                                        $duplicate = false;
                                        foreach ($this->_repositories as $knownRepos) {
                                                if ($knownRepos->path == $repo->path && $knownRepos->subpath == $repo->subpath) {
                                                        $duplicate = true;
                                                        break;
                                                }
                                        }

                                        if (!$duplicate && !in_array($repo->path, $this->_excluded, true)) {
                                                $this->_repositories[] = $repo;
                                        }
                                }
                        }
                }

                return $this->_repositories;
        }

        function &findRepository($name) {
                // first look in the "normal repositories"
                foreach ($this->_repositories as $index => $rep) {
                        if (strcmp($rep->getDisplayName(), $name) == 0) {
                                $repref =& $this->_repositories[$index];
                                return $repref;
                        }
                }

                // now if the parent repos have not already been loaded
                // check them
                if (!$this->_parentPathsLoaded) {
                        foreach ($this->_parentPaths as $parentPath) {
                                $repref =& $parentPath->findRepository($name);
                                if ($repref != null) {
                                        $this->_repositories[] = $repref;
                                        return $repref;
                                }
                        }
                }

                // Hack to return a string by reference; value retrieved at setup.php:414
                $str = 'Unable to find repository "'.escape($name).'".';
                $error =& $str;
                return $error;
        }

        // }}}

        // {{{ setServerIsWindows
        //
        // The server is running on Windows

        function setServerIsWindows() {
                $this->serverIsWindows = true;

                // On Windows machines, use double quotes around command line parameters
                $this->quote = '"';

                // On Windows, semicolon separates path entries in a list rather than colon.
                $this->pathSeparator = ';';

                $this->fileUrlPrefix = 'file:///';
        }

        // }}}

        // {{{ MultiViews

        // useMultiViews
        //
        // Use MultiViews to access the repository

        function useMultiViews($multiViewsIndex = 'browse') {
                $this->multiViews = true;
                $this->multiViewsIndex = $multiViewsIndex;
        }

        function getUseMultiViews() {
                return $this->multiViews;
        }

        function getMultiViewsIndex() {
                return $this->multiViewsIndex;
        }

        // }}}

        // {{{ Enscript

        // useEnscript
        //
        // Use Enscript to colourise listings

        function useEnscript($before_1_6_3 = false) {
                $this->useEnscript = true;
                $this->useEnscriptBefore_1_6_3 = $before_1_6_3;
        }

        function getUseEnscript() {
                return $this->useEnscript;
        }

        function getUseEnscriptBefore_1_6_3() {
                return $this->useEnscriptBefore_1_6_3;
        }

        // }}}

        // {{{ GeSHi

        function setGeshiPath($path) {
                $this->_setPath($this->geshiScript, $path, 'geshi.php');
        }

        function getGeshiScript() {
                return $this->geshiScript;
        }

        // useGeshi
        //
        // Use GeSHi to colourise listings
        function useGeshi() {
                $this->useGeshi = true;
        }

        function getUseGeshi() {
                return $this->useGeshi;
        }

        // }}}

        // {{{ Parsedown

        function setParsedownPath($path) {
                $this->_setPath($this->parsedownScript, $path, 'Parsedown.php');
        }

        function getParsedownScript() {
                return $this->parsedownScript;
        }

        // useParsedown
        //
        // Use Parsedown to render README.md
        function useParsedown() {
                $this->useParsedown = true;
        }

        function getUseParsedown() {
                return $this->useParsedown;
        }

        // }}}

        // {{{ Inline MIME Types

        // inlineMimeTypes
        //
        // Specify MIME types to display inline in WebSVN pages
        function addInlineMimeType($type) {
                if (!in_array($type, $this->inlineMimeTypes)) {
                        $this->inlineMimeTypes[] = $type;
                }
        }

        // }}}

        // {{{ Show changed files by default on log.php

        function setLogsShowChanges($enabled = true, $myrep = 0) {
                if (empty($myrep)) {
                        $this->logsShowChanges = $enabled;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->logsShowChanges = $enabled;
                }
        }

        function logsShowChanges() {
                return $this->logsShowChanges;
        }

        // }}}

        // {{{ RSS

        function setRssEnabled($enabled = true, $myrep = 0) {
                if (empty($myrep)) {
                        $this->rss = $enabled;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->setRssEnabled($enabled);
                }
        }

        function isRssEnabled() {
                return $this->rss;
        }

        function setRssCachingEnabled($enabled = true, $myrep = 0) {
                if (empty($myrep)) {
                        $this->rssCaching = true;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->setRssCachingEnabled($enabled);
                }
        }

        function isRssCachingEnabled() {
                return $this->rssCaching;
        }

        // Maximum number of entries in RSS feed

        function setRssMaxEntries($max, $myrep = 0) {
                if (empty($myrep)) {
                        $this->rssMaxEntries = $max;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->setRssMaxEntries($max);
                }
        }

        function getRssMaxEntries() {
                return $this->rssMaxEntries;
        }

        function getHideRSS() {
                return $this->rss;
        }

        // cacheRSS
        //
        // Enable caching of RSS feeds

        function enableRSSCaching($myrep = 0) {
                if (empty($myrep)) {
                        $this->rssCaching = true;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->enableRSSCaching();
                }
        }

        function getRSSCaching() {
                return $this->rssCaching;
        }

        // }}}

        // {{{ Downloads

        // allowDownload
        //
        // Allow download of tarballs

        function allowDownload($myrep = 0) {
                if (empty($myrep)) {
                        $this->allowDownload = true;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->allowDownload();
                }
        }

        function disallowDownload($myrep = 0) {
                if (empty($myrep)) {
                        $this->allowDownload = false;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->disallowDownload();
                }
        }

        function getAllowDownload() {
                return $this->allowDownload;
        }

        function setTempDir($tempDir) {
                $this->tempDir = $tempDir;
        }

        function getTempDir() {
                if (empty($this->tempDir)) {
                        $this->tempDir = sys_get_temp_dir();
                }
                return $this->tempDir;
        }

        function setMinDownloadLevel($level, $myrep = 0) {
                if (empty($myrep)) {
                        $this->minDownloadLevel = $level;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->setMinDownloadLevel($level);
                }
        }

        function getMinDownloadLevel() {
                return $this->minDownloadLevel;
        }

        function addAllowedDownloadException($path, $myrep = 0) {
                if ($path[strlen($path) - 1] != '/') {
                        $path .= '/';
                }

                if (empty($myrep)) {
                        $this->allowedExceptions[] = $path;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->addAllowedDownloadException($path);
                }
        }

        function addDisallowedDownloadException($path, $myrep = 0) {
                if ($path[strlen($path) - 1] != '/') {
                        $path .= '/';
                }

                if (empty($myrep)) {
                        $this->disallowedExceptions[] = $path;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->addDisallowedDownloadException($path);
                }
        }

        function findException($path, $exceptions) {
                foreach ($exceptions as $key => $exc) {
                        if (strncmp($exc, $path, strlen($exc)) == 0) {
                                return true;
                        }
                }

                return false;
        }

        // }}}

        // {{{ getURL
        //
        // Get the URL to a path name based on the current config

        function getURL($rep, $path, $op) {
                list($base, $params) = $this->getUrlParts($rep, $path, $op);
                $url = $base.'?';
                foreach ($params as $k => $v) {
                        $url .= $k.'='.rawurlencode($v).'&amp;';
                }
                return $url;
        }

        // }}}

        // {{{ getUrlParts
        //
        // Get the URL and parameters for a path name based on the current config

        function getUrlParts($rep, $path, $op) {
                $params = array();

                if ($this->multiViews) {
                        $url = $_SERVER['SCRIPT_NAME'];
                        if (preg_match('|\.php$|i', $url)) {
                                // remove the .php extension
                                $url = substr($url, 0, -4);
                        }

                        if ($path && $path[0] != '/') {
                                $path = '/'.$path;
                        }

                        if (substr($url, -5) == 'index') {
                                $url = substr($url, 0, -5).$this->multiViewsIndex;
                        }

                        if ($op == 'index') {
                                $url .= '/';
                        } else if (is_object($rep)) {
                                $url .= '/'.$rep->getDisplayName().str_replace('%2F', '/', rawurlencode($path ?? ''));

                                if ($op && $op != 'dir' && $op != 'file') {
                                        $params['op'] = $op;
                                }
                        }

                } else {
                        switch ($op) {
                                case 'index':
                                        $url = '.';
                                        break;

                                case 'dir':
                                        $url = 'listing.php';
                                        break;

                                case 'revision':
                                        $url = 'revision.php';
                                        break;

                                case 'file':
                                        $url = 'filedetails.php';
                                        break;

                                case 'log':
                                        $url = 'log.php';
                                        break;

                                case 'diff':
                                        $url = 'diff.php';
                                        break;

                                case 'blame':
                                        $url = 'blame.php';
                                        break;

                                case 'form':
                                        $url = 'form.php';
                                        break;

                                case 'rss':
                                        $url = 'rss.php';
                                        break;

                                case 'dl':
                                        $url = 'dl.php';
                                        break;

                                case 'comp':
                                        $url = 'comp.php';
                                        break;

                                case 'search':
                                        $url = 'search.php';
                                        break;
                        }

                        if (is_object($rep) && $op != 'index') {
                                $params['repname'] = $rep->getDisplayName();
                        }
                        if (!empty($path)) {
                                $params['path'] = $path;
                        }
                }

                return array($url, $params);
        }

        // }}}

        // {{{ Paths and Commands

        // setPath
        //
        // Set the location of the given path

        function _setPath(&$var, $path, $name, $params = '') {
                if ($path == '') {
                        // Search in system search path. No check for existence possible
                        $var = $name;
                } else {
                        $lastchar = substr($path, -1, 1);
                        $isDir = ($lastchar == DIRECTORY_SEPARATOR || $lastchar == '/' || $lastchar == '\\');

                        if (!$isDir) $path .= DIRECTORY_SEPARATOR;

                        if (($this->serverIsWindows && !file_exists($path.$name.'.exe')) || (!$this->serverIsWindows && !file_exists($path.$name))) {
                                echo 'Unable to find "'.$name.'" tool at location "'.$path.$name.'"';
                                exit;
                        }

                        // On a windows machine we need to put quotes around the
                        // entire command to allow for spaces in the path
                        if ($this->serverIsWindows) {
                                $var = '"'.$path.$name.'"';
                        } else {
                                $var = $path.$name;
                        }
                }

                // Append parameters
                if ($params != '') $var .= ' '.$params;
        }

        // Define directory path to use for --config-dir parameter
        function setSvnConfigDir($path) {
                $this->_svnConfigDir = $path;
                $this->_updateSvnCommand();
        }

        // Define flag to use --trust-server-cert parameter
        function setTrustServerCert() {
                $this->_svnTrustServerCert = true;
                $this->_updateSvnCommand();
        }

        // Define the location of the svn command (e.g. '/usr/bin')
        function setSvnCommandPath($path) {
                $this->_svnCommandPath = $path;
                $this->_updateSvnCommand();
        }

        // Define the location of the svnauthz command (e.g. '/usr/bin')
        function setSvnAuthzCommandPath($path) {
                $this->_setPath($this->svnAuthz, $path, 'svnauthz', 'accessof');
        }

        function _updateSvnCommand() {
                $this->_setPath($this->svn, $this->_svnCommandPath, 'svn', '--non-interactive --config-dir '.$this->_svnConfigDir.($this->_svnTrustServerCert ? ' --trust-server-cert' : ''));
        }

        function getSvnCommand() {
                return $this->svn;
        }

        function getSvnAuthzCommand() {
                return $this->svnAuthz;
        }

        // setDiffPath
        //
        // Define the location of the diff command

        function setDiffPath($path) {
                $this->_setPath($this->diff, $path, 'diff');
        }

        function getDiffCommand() {
                return $this->diff;
        }

        // setEnscriptPath
        //
        // Define the location of the enscript command

        function setEnscriptPath($path) {
                $this->_setPath($this->enscript, $path, 'enscript', '-q');
        }

        function getEnscriptCommand() {
                return $this->enscript;
        }

        // setSedPath
        //
        // Define the location of the sed command

        function setSedPath($path) {
                $this->_setPath($this->sed, $path, 'sed');
        }

        function getSedCommand() {
                return $this->sed;
        }

        // setTarPath
        //
        // Define the location of the tar command

        function setTarPath($path) {
                $this->_setPath($this->tar, $path, 'tar');
        }

        function getTarCommand() {
                return $this->tar;
        }

        // setGzipPath
        //
        // Define the location of the GZip command

        function setGzipPath($path) {
                $this->_setPath($this->gzip, $path, 'gzip');
        }

        function getGzipCommand() {
                return $this->gzip;
        }

        // setZipPath
        //
        // Define the location of the zip command
        function setZipPath($path) {
                $this->_setPath($this->zip, $path, 'zip');
        }

        function getZipPath() {
                return $this->zip;
        }

        // setLocale
        //
        // Set the locale for PHP and all spawned processes

        function setLocale($locale) {
                $this->locale = $locale;
        }

        function getLocale() {
                return $this->locale;
        }

        // setDefaultFileDlMode
        //
        // Define the default file download mode - one of [gzip, zip, plain]
        function setDefaultFileDlMode($dlmode) {
                if (in_array($dlmode, $this->validFileDlModes)) {
                        $this->defaultFileDlMode = $dlmode;
                } else {
                        echo 'Setting default file download mode to an invalid value "'.$dlmode.'"';
                        exit;
                }
        }

        function getDefaultFileDlMode() {
                return $this->defaultFileDlMode;
        }

        // setDefaultDirectoryDlMode
        //
        // Define the default directory download mode - one of [gzip, zip]
        function setDefaultDirectoryDlMode($dlmode) {
                if (in_array($dlmode, $this->validDirectoryDlModes)) {
                        $this->defaultDirectoryDlMode = $dlmode;
                } else {
                        echo 'Setting default directory download mode to an invalid value "'.$dlmode.'"';
                        exit;
                }
        }

        function setDefaultFolderDlMode($dlmode) {
                $this->setDefaultDirectoryDlMode($dlmode);
        }

        function getDefaultDirectoryDlMode() {
                return $this->defaultDirectoryDlMode;
        }

        function getDefaultFolderDlMode() {
                return $this->getDefaultDirectoryDlMode();
        }

        // Templates

        function addTemplatePath($path) {
                $lastchar = substr($path, -1, 1);
                if ($lastchar != '/' && $lastchar != '\\') {
                        $path .= DIRECTORY_SEPARATOR;
                }

                if (!in_array($path, $this->templatePaths)) {
                        $this->templatePaths[] = $path;
                }
        }

        function setTemplatePath($path, $myrep = null) {
                $lastchar = substr($path, -1, 1);
                if ($lastchar != '/' && $lastchar != '\\') {
                        $path .= DIRECTORY_SEPARATOR;
                }

                if ($myrep !== null) {
                        // fixed template for specific repository
                        $repo =& $this->findRepository($myrep);
                        $repo->setTemplatePath($path);
                } else {
                        // for backward compatibility
                        if (in_array($path, $this->templatePaths)) {
                                array_splice($this->templatePaths, array_search($path, $this->templatePaths), 1);
                        }
                        array_unshift($this->templatePaths, $path);
                }
        }

        function getTemplatePath() {
                if (count($this->templatePaths) == 0) {
                        echo 'No template path added in config file';
                        exit;
                }
                if ($this->userTemplate !== false)
                        return $this->userTemplate;
                else
                        return $this->templatePaths[0];
        }

        // }}}

        function setDefaultLanguage($language) {
                $this->defaultLanguage = $language;
        }

        function getDefaultLanguage() {
                return $this->defaultLanguage;
        }

        function ignoreUserAcceptedLanguages() {
                $this->ignoreAcceptedLanguages = true;
        }

        function useAcceptedLanguages() {
                return !$this->ignoreAcceptedLanguages;
        }

        // {{{ Tab expansion functions

        function expandTabsBy($sp, $myrep = 0) {
                if (empty($myrep)) {
                        $this->spaces = $sp;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->expandTabsBy($sp);
                }
        }

        function getExpandTabsBy() {
                return $this->spaces;
        }

        // }}}

        // {{{ Bugtraq issue tracking

        function setBugtraqEnabled($enabled, $myrep = 0) {
                if (empty($myrep)) {
                        $this->bugtraq = $enabled;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->setBugtraqEnabled($enabled);
                }
        }

        function isBugtraqEnabled() {
                return $this->bugtraq;
        }

        function setBugtraqProperties($message, $logregex, $url, $append = true, $myrep = null) {
                $properties = array();
                $properties['bugtraq:message'] = $message;
                $properties['bugtraq:logregex'] = $logregex;
                $properties['bugtraq:url'] = $url;
                $properties['bugtraq:append'] = (bool)$append;
                if ($myrep === null) {
                        $this->bugtraqProperties = $properties;
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->setBugtraqProperties($properties);
                }
        }

        function getBugtraqProperties() {
                return $this->bugtraqProperties;
        }

        // }}}

        // {{{ Misc settings

        function ignoreSvnMimeTypes() {
                $this->ignoreSvnMimeTypes = true;
        }

        function getIgnoreSvnMimeTypes() {
                return $this->ignoreSvnMimeTypes;
        }

        function ignoreWebSVNContentTypes() {
                $this->ignoreWebSVNContentTypes = true;
        }

        function getIgnoreWebSVNContentTypes() {
                return $this->ignoreWebSVNContentTypes;
        }

        function useAccessFile($file, $myrep = 0) {
                if (empty($myrep)) {
                        if (is_readable($file)) {
                                if ($this->authz === null) {
                                        $this->authz = new Authorization();
                                }
                                $this->authz->addAccessFile($file);
                        } else {
                                echo 'Unable to read access file "'.$file.'"';
                                exit;
                        }
                } else {
                        $repo =& $this->findRepository($myrep);
                        $repo->useAccessFile($file);
                }
        }

        function &getAuthz() {
                return $this->authz;
        }

        function areRobotsBlocked() {
                return $this->blockRobots;
        }

        function setBlockRobots($value = true) {
                $this->blockRobots = $value;
        }

        function useTreeView() {
                $this->treeView = true;
        }

        function getUseTreeView() {
                return $this->treeView;
        }

        function useFlatView() {
                $this->treeView = false;
        }

        function useTreeIndex($open) {
                $this->flatIndex = false;
                $this->openTree = $open;
        }

        function getUseFlatIndex() {
                return $this->flatIndex;
        }

        function getOpenTree() {
                return $this->openTree;
        }

        function setLoadAllRepos($flag) {
                $this->loadAllRepos = $flag;
        }

        function showLoadAllRepos() {
                return $this->loadAllRepos;
        }

        function setAlphabeticOrder($flag) {
                $this->alphabetic = $flag;
        }

        function isAlphabeticOrder() {
                return $this->alphabetic;
        }

        function showLastModInIndex() {
                return $this->showLastModInIndex;
        }

        function setShowLastModInIndex($show) {
                $this->showLastModInIndex = $show;
        }

        function showLastModInListing() {
                return $this->showLastModInListing;
        }

        function setShowLastModInListing($show) {
                $this->showLastModInListing = $show;
        }

        function showAgeInsteadOfDate() {
                return $this->showAgeInsteadOfDate;
        }

        function setShowAgeInsteadOfDate($show) {
                $this->showAgeInsteadOfDate = $show;
        }

        function showRepositorySelectionForm() {
                return $this->_showRepositorySelectionForm;
        }

        function setShowRepositorySelectionForm($show) {
                $this->_showRepositorySelectionForm = $show;
        }

        function getIgnoreWhitespacesInDiff() {
                return $this->_ignoreWhitespacesInDiff;
        }

        function setIgnoreWhitespacesInDiff($ignore) {
                $this->_ignoreWhitespacesInDiff = $ignore;
        }

        // Methods for storing version information for the command-line svn tool

        function setSubversionVersion($subversionVersion) {
                $this->subversionVersion = $subversionVersion;
        }

        function getSubversionVersion() {
                return $this->subversionVersion;
        }

        function setSubversionMajorVersion($subversionMajorVersion) {
                $this->subversionMajorVersion = $subversionMajorVersion;
        }

        function getSubversionMajorVersion() {
                return $this->subversionMajorVersion;
        }

        function setSubversionMinorVersion($subversionMinorVersion) {
                $this->subversionMinorVersion = $subversionMinorVersion;
        }

        function getSubversionMinorVersion() {
                return $this->subversionMinorVersion;
        }

        // }}}

        // {{{ Sort the repostories
        //
        // This function sorts the repositories by group name.  The contents of the
        // group are left in there original order, which will either be sorted if the
        // group was added using the parentPath function, or defined for the order in
        // which the repositories were included in the user's config file.
        //
        // Note that as of PHP 4.0.6 the usort command no longer preserves the order
        // of items that are considered equal (in our case, part of the same group).
        // The mergesort function preserves this order.

        function sortByGroup() {
                if (!empty($this->_repositories)) {
                        mergesort($this->_repositories, 'cmpGroups');
                }
        }

        // }}}

        // {{{ Change the name of the breadcrumb root-phrase to that of the current repo?

        function setBreadcrumbRepoRootAsRepo($newValue) {
                $this->breadcrumbRepoRootAsRepo = $newValue;
        }

        function getBreadcrumbRepoRootAsRepo() {
                return $this->breadcrumbRepoRootAsRepo;
        }

        // }}}
}