No changes between revisions
/WebSVN/include/auth.inc
1,242 → 1,242
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// auth.inc
//
// Handle reading and interpretation of an SVN auth file
 
require_once("include/accessfile.inc");
 
define("UNDEFINED", 0);
define("ALLOW", 1);
define("DENY", 2);
 
class Authentication
{
var $rights;
var $user;
var $usersGroups = array();
// {{{ __construct
 
function Authentication($accessfile)
{
$this->rights = new IniFile();
$this->rights->readIniFile($accessfile);
$this->setUsername("");
$this->identifyGroups();
}
 
// }}}
// {{{ setUsername($username)
//
// Set the username if it is given, or
// get the user of the current http session
 
function setUsername($username)
{
if ($username == "") // Use the current user
{
$this->user = @$_SERVER["REMOTE_USER"];
}
else
{
$this->user = $username;
}
}
 
// }}}
 
// {{{ identifyGroups()
//
// Checks to see which groups the user belongs to
 
function identifyGroups()
{
$this->usersGroups[] = "*";
 
if (is_array($this->rights->getValues("groups")))
{
foreach ($this->rights->getValues("groups") as $group => $names)
{
if (in_array(strtolower($this->user), preg_split('/\s*,\s*/', $names)))
$this->usersGroups[] = "@" . $group;
}
}
}
 
// }}}
 
// {{{ inList
//
// Check if the user is in the given list and return their read status
// if they are (UNDEFINED, ALLOW or DENY)
function inList($accessors, $user)
{
$output = UNDEFINED;
foreach($accessors As $key => $rights)
{
$keymatch = false;
if (in_array($key, $this->usersGroups) || !strcmp($key, strtolower($user)))
$keymatch = true;
if ($keymatch)
{
if (strpos($rights, "r") !== false)
return ALLOW;
else
$output = DENY;
}
}
return $output;
}
 
// }}}
// {{{ hasReadAccess
//
// Returns true if the user has read access to the given path
function hasReadAccess($repos, $path, $checkSubFolders = false)
{
$access = UNDEFINED;
if ($path{0} != "/")
$path = "/$path";
// If were told to, we should check sub folders of the path to see if there's
// a read access below this level. This is used to display the folders needed
// to get to the folder to which read access is granted.
if ($checkSubFolders)
{
$sections = $this->rights->getSections();
foreach($sections As $section => $accessers)
{
$qualified = $repos.":".$path;
$len = strlen($qualified);
if ($len < strlen($section) && strncmp($section, $qualified, strlen($qualified)) == 0)
{
$access = $this->inList($accessers, $this->user);
}
 
if ($access != ALLOW)
{
$len = strlen($path);
if ($len < strlen($section) && strncmp($section, $path, strlen($path)) == 0)
{
$access = $this->inList($accessers, $this->user);
}
}
if ($access == ALLOW)
break;
}
}
// If we still don't have access, check each subpath of the path until we find an
// access level...
if ($access != ALLOW)
{
$access = UNDEFINED;
do
{
$accessers = $this->rights->getValues($repos.":".$path);
if (!empty($accessers))
$access = $this->inList($accessers, $this->user);
if ($access == UNDEFINED)
{
$accessers = $this->rights->getValues($path);
if (!empty($accessers))
$access = $this->inList($accessers, $this->user);
}
// If we've not got a match, remove the sub directory and start again
if ($access == UNDEFINED)
{
if ($path == "/") break;
$path = substr($path, 0, strrpos(substr($path, 0, -1), "/") + 1);
}
} while ($access == UNDEFINED && $path != "");
}
return $access == ALLOW;
}
 
// }}}
// {{{ hasUnrestrictedReadAccess
//
// Returns true if the user has read access to the given path and too
// all subfolders
function hasUnrestrictedReadAccess($repos, $path)
{
// First make sure that we have full read access at this level
if (!$this->hasReadAccess($repos, $path, false))
return false;
// Now check to see if there is a sub folder that's protected
$sections = $this->rights->getSections();
foreach($sections As $section => $accessers)
{
$qualified = $repos.":".$path;
$len = strlen($qualified);
$access = UNDEFINED;
if ($len < strlen($section) && strncmp($section, $qualified, strlen($qualified)) == 0)
{
$access = $this->inList($accessers, $this->user);
}
 
if ($access != DENY)
{
$len = strlen($path);
if ($len < strlen($section) && strncmp($section, $path, strlen($qualified)) == 0)
{
$access = $this->inList($accessers, $this->user);
}
}
if ($access == DENY)
return false;
}
return true;
}
 
// }}}
 
}
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// auth.inc
//
// Handle reading and interpretation of an SVN auth file
 
require_once("include/accessfile.inc");
 
define("UNDEFINED", 0);
define("ALLOW", 1);
define("DENY", 2);
 
class Authentication
{
var $rights;
var $user;
var $usersGroups = array();
// {{{ __construct
 
function Authentication($accessfile)
{
$this->rights = new IniFile();
$this->rights->readIniFile($accessfile);
$this->setUsername("");
$this->identifyGroups();
}
 
// }}}
// {{{ setUsername($username)
//
// Set the username if it is given, or
// get the user of the current http session
 
function setUsername($username)
{
if ($username == "") // Use the current user
{
$this->user = @$_SERVER["REMOTE_USER"];
}
else
{
$this->user = $username;
}
}
 
// }}}
 
// {{{ identifyGroups()
//
// Checks to see which groups the user belongs to
 
function identifyGroups()
{
$this->usersGroups[] = "*";
 
if (is_array($this->rights->getValues("groups")))
{
foreach ($this->rights->getValues("groups") as $group => $names)
{
if (in_array(strtolower($this->user), preg_split('/\s*,\s*/', $names)))
$this->usersGroups[] = "@" . $group;
}
}
}
 
// }}}
 
// {{{ inList
//
// Check if the user is in the given list and return their read status
// if they are (UNDEFINED, ALLOW or DENY)
function inList($accessors, $user)
{
$output = UNDEFINED;
foreach($accessors As $key => $rights)
{
$keymatch = false;
if (in_array($key, $this->usersGroups) || !strcmp($key, strtolower($user)))
$keymatch = true;
if ($keymatch)
{
if (strpos($rights, "r") !== false)
return ALLOW;
else
$output = DENY;
}
}
return $output;
}
 
// }}}
// {{{ hasReadAccess
//
// Returns true if the user has read access to the given path
function hasReadAccess($repos, $path, $checkSubFolders = false)
{
$access = UNDEFINED;
if ($path{0} != "/")
$path = "/$path";
// If were told to, we should check sub folders of the path to see if there's
// a read access below this level. This is used to display the folders needed
// to get to the folder to which read access is granted.
if ($checkSubFolders)
{
$sections = $this->rights->getSections();
foreach($sections As $section => $accessers)
{
$qualified = $repos.":".$path;
$len = strlen($qualified);
if ($len < strlen($section) && strncmp($section, $qualified, strlen($qualified)) == 0)
{
$access = $this->inList($accessers, $this->user);
}
 
if ($access != ALLOW)
{
$len = strlen($path);
if ($len < strlen($section) && strncmp($section, $path, strlen($path)) == 0)
{
$access = $this->inList($accessers, $this->user);
}
}
if ($access == ALLOW)
break;
}
}
// If we still don't have access, check each subpath of the path until we find an
// access level...
if ($access != ALLOW)
{
$access = UNDEFINED;
do
{
$accessers = $this->rights->getValues($repos.":".$path);
if (!empty($accessers))
$access = $this->inList($accessers, $this->user);
if ($access == UNDEFINED)
{
$accessers = $this->rights->getValues($path);
if (!empty($accessers))
$access = $this->inList($accessers, $this->user);
}
// If we've not got a match, remove the sub directory and start again
if ($access == UNDEFINED)
{
if ($path == "/") break;
$path = substr($path, 0, strrpos(substr($path, 0, -1), "/") + 1);
}
} while ($access == UNDEFINED && $path != "");
}
return $access == ALLOW;
}
 
// }}}
// {{{ hasUnrestrictedReadAccess
//
// Returns true if the user has read access to the given path and too
// all subfolders
function hasUnrestrictedReadAccess($repos, $path)
{
// First make sure that we have full read access at this level
if (!$this->hasReadAccess($repos, $path, false))
return false;
// Now check to see if there is a sub folder that's protected
$sections = $this->rights->getSections();
foreach($sections As $section => $accessers)
{
$qualified = $repos.":".$path;
$len = strlen($qualified);
$access = UNDEFINED;
if ($len < strlen($section) && strncmp($section, $qualified, strlen($qualified)) == 0)
{
$access = $this->inList($accessers, $this->user);
}
 
if ($access != DENY)
{
$len = strlen($path);
if ($len < strlen($section) && strncmp($section, $path, strlen($qualified)) == 0)
{
$access = $this->inList($accessers, $this->user);
}
}
if ($access == DENY)
return false;
}
return true;
}
 
// }}}
 
}
?>
/WebSVN/include/bugtraq.inc
1,390 → 1,390
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// bugtraq.inc
//
// Functions for accessing the bugtraq properties and replacing issue IDs
// with URLs.
//
// For more information about bugtraq, see
// http://svn.collab.net/repos/tortoisesvn/trunk/doc/issuetrackers.txt
 
class Bugtraq
{
// {{{ Properties
 
var $msgstring;
var $urlstring;
var $logregex;
var $append;
 
var $firstPart;
var $firstPartLen;
var $lastPart;
var $lastPartLen;
 
var $propsfound = false;
 
// }}}
 
// {{{ __construct($rep, $svnrep, $path)
 
function Bugtraq($rep, $svnrep, $path)
{
global $config;
if ($rep->getBugtraq())
{
$pos = strrpos($path, "/");
$parent = substr($path, 0, $pos + 1);
$this->append = true;
$enoughdata = false;
while(!$enoughdata && (strpos($parent, "/") !== false))
{
if (empty($this->msgstring)) $this->msgstring = $svnrep->getProperty($parent, 'bugtraq:message');
if (empty($this->logregex)) $this->logregex = $svnrep->getProperty($parent, 'bugtraq:logregex');
if (empty($this->urlstring)) $this->urlstring = $svnrep->getProperty($parent, 'bugtraq:url');
if (empty($this->append)) $this->append = ($svnrep->getProperty($parent, 'bugtraq:append') == "true");
$parent = substr($parent, 0, -1); // Remove the trailing slash
$pos = strrpos($parent, "/"); // Find the last trailing slash
$parent = substr($parent, 0, $pos + 1); // Find the previous parent directory
$enoughdata = ((!empty($this->msgstring) || !empty($this->logregex)) && !empty($this->urlstring));
}
$this->msgstring = trim(@$this->msgstring);
$this->urlstring = trim(@$this->urlstring);
if ($enoughdata && !empty($this->msgstring))
$this->initPartInfo();
if ($enoughdata)
$this->propsfound = true;
}
}
 
// }}}
 
// {{{ initPartInfo()
function initPartInfo()
{
if (($bugidpos = strpos($this->msgstring, "%BUGID%")) !== false && strpos($this->urlstring, "%BUGID%") !== false)
{
// Get the textual parts of the message string for comparison purposes
$this->firstPart = substr($this->msgstring, 0, $bugidpos);
$this->firstPartLen = strlen($this->firstPart);
$this->lastPart = substr($this->msgstring, $bugidpos + 7);
$this->lastPartLen = strlen($this->lastPart);
}
}
 
// }}}
 
// {{{ replaceIDs($message)
 
function replaceIDs($message)
{
if ($this->propsfound)
{
// First we search for the message string
$logmsg = "";
$message = rtrim($message);
if ($this->append)
{
// Just compare the last line
if (($offset = strrpos($message, "\n")) !== false)
{
$logmsg = substr($message, 0, $offset + 1);
$bugLine = substr($message, $offset + 1);
}
else
$bugLine = $message;
}
else
{
if (($offset = strpos($message, "\n")) !== false)
{
$bugLine = substr($message, 0, $offset);
$logmsg = substr($message, $offset);
}
else
$bugLine = $message;
}
 
// Make sure that our line really is an issue tracker message
 
if (((strncmp($bugLine, $this->firstPart, $this->firstPartLen) == 0)) &&
strcmp(substr($bugLine, -$this->lastPartLen, $this->lastPartLen), $this->lastPart) == 0)
{
// Get the issues list
if ($this->lastPartLen > 0)
$issues = substr($bugLine, $this->firstPartLen, -$this->lastPartLen);
else
$issues = substr($bugLine, $this->firstPartLen);
// Add each reference to the first part of the line
$line = $this->firstPart;
while ($pos = strpos($issues, ","))
{
$issue = trim(substr($issues, 0, $pos));
$issues = substr($issues, $pos + 1);
$line .= "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">$issue</a>, ";
}
$line .= "<a href=\"".str_replace("%BUGID%", trim($issues), $this->urlstring)."\">".trim($issues)."</a>".$this->lastPart;
if ($this->append)
$message = $logmsg.$line;
else
$message = $line.$logmsg;
}
// Now replace all other instances of bug IDs that match the regex
 
if ($this->logregex)
{
$message = rtrim($message);
$line = "";
$allissues = "";
$lines = split("\n", $this->logregex);
$regex_all = "~".$lines[0]."~";
$regex_single = @$lines[1];
if (empty($regex_single))
{
// If the property only contains one line, then the pattern is only designed
// to find one issue number at a time. e.g. [Ii]ssue #?(\d+). In this case
// we need to replace the matched issue ID with the link.
if ($numMatches = preg_match_all($regex_all, $message, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
{
$addedOffset = 0;
for ($match = 0; $match < $numMatches; $match++)
{
$issue = $matches[$match][1][0];
$issueOffset = $matches[$match][1][1];
$issueLink = "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">".$issue."</a>";
$message = substr_replace($message, $issueLink, $issueOffset + $addedOffset, strlen($issue));
$addedOffset += strlen($issueLink) - strlen($issue);
}
}
}
else
{
// It the property contains two lines, then the first is a pattern for extracting
// multiple issue numbers, and the second is a pattern extracting each issue
// number from the multiple match. e.g. [Ii]ssue #?(\d+)(,? ?#?(\d+))+ and (\d+)
while (preg_match($regex_all, $message, $matches, PREG_OFFSET_CAPTURE))
{
$completeMatch = $matches[0][0];
$completeMatchOffset = $matches[0][1];
$replacement = $completeMatch;
if ($numMatches = preg_match_all("~".$regex_single."~", $replacement, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
{
$addedOffset = 0;
for ($match = 0; $match < $numMatches; $match++)
{
$issue = $matches[$match][1][0];
$issueOffset = $matches[$match][1][1];
 
$issueLink = "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">".$issue."</a>";
$replacement = substr_replace($replacement, $issueLink, $issueOffset + $addedOffset, strlen($issue));
$addedOffset += strlen($issueLink) - strlen($issue);
}
}
$message = substr_replace($message, $replacement, $completeMatchOffset, strlen($completeMatch));
}
}
}
}
 
return $message;
}
 
// }}}
 
}
 
// The BugtraqTestable class is a derived class that is used to test the matching
// abilities of the Bugtraq class. In particular, it allows for the initialisation of the
// class without the need for a repository.
 
class BugtraqTestable extends Bugtraq
{
// {{{ __construct()
 
function BugtraqTestable()
{
// This constructor serves to assure that the parent constructor is not
// called.
}
 
// }}}
 
// {{{ setUpVars($message, $url, $regex, $append)
 
function setUpVars($message, $url, $regex, $append)
{
$this->msgstring = $message;
$this->urlstring = $url;
$this->logregex = $regex;
$this->append = $append;
$this->propsfound = true;
 
$this->initPartInfo();
}
 
// }}}
 
// {{{ setMessage($message)
function setMessage($message)
{
$this->msgstring = $message;
}
 
// }}}
 
// {{{ setUrl($url)
 
function setUrl($url)
{
$this->urlstring = $url;
}
 
// }}}
 
// {{{ setRegex($regex)
 
function setRegEx($regex)
{
$this->logregex = $regex;
}
 
// }}}
 
// {{{ setAppend($append)
 
function setAppend($append)
{
$this->append = $append;
}
 
// }}}
 
// {{{ printVars()
function printVars()
{
echo "msgstring = ".$this->msgstring."\n";
echo "urlstring = ".$this->urlstring."\n";
echo "logregex = ".$this->logregex."\n";
echo "append = ".$this->append."\n";
echo "firstPart = ".$this->firstPart."\n";
echo "firstPartLen = ".$this->firstPartLen."\n";
echo "lastPart = ".$this->lastPart."\n";
echo "lastPartLen = ".$this->lastPartLen."\n";
}
 
// }}}
}
 
// {{{ test_bugtraq()
 
function test_bugtraq()
{
$tester = new BugtraqTestable;
$tester->setUpVars("BugID: %BUGID%",
"http://bugtracker/?id=%BUGID%",
"[Ii]ssue #?(\d+)",
true);
//$tester->printVars();
$res = $tester->replaceIDs("BugID: 789\n".
"This is a test message that refers to issue #123 and\n".
"issue #456.\n".
"BugID: 789");
echo nl2br($res)."<p>";
$res = $tester->replaceIDs("BugID: 789, 101112\n".
"This is a test message that refers to issue #123 and\n".
"issue #456.\n".
"BugID: 789, 101112");
 
echo nl2br($res)."<p>";
$tester->setAppend(false);
$res = $tester->replaceIDs("BugID: 789\n".
"This is a test message that refers to issue #123 and\n".
"issue #456.\n".
"BugID: 789");
echo nl2br($res)."<p>";
$res = $tester->replaceIDs("BugID: 789, 101112\n".
"This is a test message that refers to issue #123 and\n".
"issue #456.\n".
"BugID: 789, 101112");
 
echo nl2br($res)."<p>";
$tester->setUpVars("BugID: %BUGID%",
"http://bugtracker/?id=%BUGID%",
"[Ii]ssues?:?(\s*(,|and)?\s*#\d+)+\n(\d+)",
true);
$res = $tester->replaceIDs("BugID: 789, 101112\n".
"This is a test message that refers to issue #123 and\n".
"issues #456, #654 and #321.\n".
"BugID: 789, 101112");
 
echo nl2br($res)."<p>";
 
$tester->setUpVars("Test: %BUGID%",
"http://bugtracker/?id=%BUGID%",
"\s*[Cc]ases*\s*[IDs]*\s*[#: ]+((\d+[ ,:;#]*)+)\n(\d+)",
true);
$res = $tester->replaceIDs("Cosmetic change\n".
"CaseIDs: 48");
 
echo nl2br($res)."<p>";
}
 
// }}}
 
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// bugtraq.inc
//
// Functions for accessing the bugtraq properties and replacing issue IDs
// with URLs.
//
// For more information about bugtraq, see
// http://svn.collab.net/repos/tortoisesvn/trunk/doc/issuetrackers.txt
 
class Bugtraq
{
// {{{ Properties
 
var $msgstring;
var $urlstring;
var $logregex;
var $append;
 
var $firstPart;
var $firstPartLen;
var $lastPart;
var $lastPartLen;
 
var $propsfound = false;
 
// }}}
 
// {{{ __construct($rep, $svnrep, $path)
 
function Bugtraq($rep, $svnrep, $path)
{
global $config;
if ($rep->getBugtraq())
{
$pos = strrpos($path, "/");
$parent = substr($path, 0, $pos + 1);
$this->append = true;
$enoughdata = false;
while(!$enoughdata && (strpos($parent, "/") !== false))
{
if (empty($this->msgstring)) $this->msgstring = $svnrep->getProperty($parent, 'bugtraq:message');
if (empty($this->logregex)) $this->logregex = $svnrep->getProperty($parent, 'bugtraq:logregex');
if (empty($this->urlstring)) $this->urlstring = $svnrep->getProperty($parent, 'bugtraq:url');
if (empty($this->append)) $this->append = ($svnrep->getProperty($parent, 'bugtraq:append') == "true");
$parent = substr($parent, 0, -1); // Remove the trailing slash
$pos = strrpos($parent, "/"); // Find the last trailing slash
$parent = substr($parent, 0, $pos + 1); // Find the previous parent directory
$enoughdata = ((!empty($this->msgstring) || !empty($this->logregex)) && !empty($this->urlstring));
}
$this->msgstring = trim(@$this->msgstring);
$this->urlstring = trim(@$this->urlstring);
if ($enoughdata && !empty($this->msgstring))
$this->initPartInfo();
if ($enoughdata)
$this->propsfound = true;
}
}
 
// }}}
 
// {{{ initPartInfo()
function initPartInfo()
{
if (($bugidpos = strpos($this->msgstring, "%BUGID%")) !== false && strpos($this->urlstring, "%BUGID%") !== false)
{
// Get the textual parts of the message string for comparison purposes
$this->firstPart = substr($this->msgstring, 0, $bugidpos);
$this->firstPartLen = strlen($this->firstPart);
$this->lastPart = substr($this->msgstring, $bugidpos + 7);
$this->lastPartLen = strlen($this->lastPart);
}
}
 
// }}}
 
// {{{ replaceIDs($message)
 
function replaceIDs($message)
{
if ($this->propsfound)
{
// First we search for the message string
$logmsg = "";
$message = rtrim($message);
if ($this->append)
{
// Just compare the last line
if (($offset = strrpos($message, "\n")) !== false)
{
$logmsg = substr($message, 0, $offset + 1);
$bugLine = substr($message, $offset + 1);
}
else
$bugLine = $message;
}
else
{
if (($offset = strpos($message, "\n")) !== false)
{
$bugLine = substr($message, 0, $offset);
$logmsg = substr($message, $offset);
}
else
$bugLine = $message;
}
 
// Make sure that our line really is an issue tracker message
 
if (((strncmp($bugLine, $this->firstPart, $this->firstPartLen) == 0)) &&
strcmp(substr($bugLine, -$this->lastPartLen, $this->lastPartLen), $this->lastPart) == 0)
{
// Get the issues list
if ($this->lastPartLen > 0)
$issues = substr($bugLine, $this->firstPartLen, -$this->lastPartLen);
else
$issues = substr($bugLine, $this->firstPartLen);
// Add each reference to the first part of the line
$line = $this->firstPart;
while ($pos = strpos($issues, ","))
{
$issue = trim(substr($issues, 0, $pos));
$issues = substr($issues, $pos + 1);
$line .= "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">$issue</a>, ";
}
$line .= "<a href=\"".str_replace("%BUGID%", trim($issues), $this->urlstring)."\">".trim($issues)."</a>".$this->lastPart;
if ($this->append)
$message = $logmsg.$line;
else
$message = $line.$logmsg;
}
// Now replace all other instances of bug IDs that match the regex
 
if ($this->logregex)
{
$message = rtrim($message);
$line = "";
$allissues = "";
$lines = split("\n", $this->logregex);
$regex_all = "~".$lines[0]."~";
$regex_single = @$lines[1];
if (empty($regex_single))
{
// If the property only contains one line, then the pattern is only designed
// to find one issue number at a time. e.g. [Ii]ssue #?(\d+). In this case
// we need to replace the matched issue ID with the link.
if ($numMatches = preg_match_all($regex_all, $message, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
{
$addedOffset = 0;
for ($match = 0; $match < $numMatches; $match++)
{
$issue = $matches[$match][1][0];
$issueOffset = $matches[$match][1][1];
$issueLink = "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">".$issue."</a>";
$message = substr_replace($message, $issueLink, $issueOffset + $addedOffset, strlen($issue));
$addedOffset += strlen($issueLink) - strlen($issue);
}
}
}
else
{
// It the property contains two lines, then the first is a pattern for extracting
// multiple issue numbers, and the second is a pattern extracting each issue
// number from the multiple match. e.g. [Ii]ssue #?(\d+)(,? ?#?(\d+))+ and (\d+)
while (preg_match($regex_all, $message, $matches, PREG_OFFSET_CAPTURE))
{
$completeMatch = $matches[0][0];
$completeMatchOffset = $matches[0][1];
$replacement = $completeMatch;
if ($numMatches = preg_match_all("~".$regex_single."~", $replacement, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE))
{
$addedOffset = 0;
for ($match = 0; $match < $numMatches; $match++)
{
$issue = $matches[$match][1][0];
$issueOffset = $matches[$match][1][1];
 
$issueLink = "<a href=\"".str_replace("%BUGID%", $issue, $this->urlstring)."\">".$issue."</a>";
$replacement = substr_replace($replacement, $issueLink, $issueOffset + $addedOffset, strlen($issue));
$addedOffset += strlen($issueLink) - strlen($issue);
}
}
$message = substr_replace($message, $replacement, $completeMatchOffset, strlen($completeMatch));
}
}
}
}
 
return $message;
}
 
// }}}
 
}
 
// The BugtraqTestable class is a derived class that is used to test the matching
// abilities of the Bugtraq class. In particular, it allows for the initialisation of the
// class without the need for a repository.
 
class BugtraqTestable extends Bugtraq
{
// {{{ __construct()
 
function BugtraqTestable()
{
// This constructor serves to assure that the parent constructor is not
// called.
}
 
// }}}
 
// {{{ setUpVars($message, $url, $regex, $append)
 
function setUpVars($message, $url, $regex, $append)
{
$this->msgstring = $message;
$this->urlstring = $url;
$this->logregex = $regex;
$this->append = $append;
$this->propsfound = true;
 
$this->initPartInfo();
}
 
// }}}
 
// {{{ setMessage($message)
function setMessage($message)
{
$this->msgstring = $message;
}
 
// }}}
 
// {{{ setUrl($url)
 
function setUrl($url)
{
$this->urlstring = $url;
}
 
// }}}
 
// {{{ setRegex($regex)
 
function setRegEx($regex)
{
$this->logregex = $regex;
}
 
// }}}
 
// {{{ setAppend($append)
 
function setAppend($append)
{
$this->append = $append;
}
 
// }}}
 
// {{{ printVars()
function printVars()
{
echo "msgstring = ".$this->msgstring."\n";
echo "urlstring = ".$this->urlstring."\n";
echo "logregex = ".$this->logregex."\n";
echo "append = ".$this->append."\n";
echo "firstPart = ".$this->firstPart."\n";
echo "firstPartLen = ".$this->firstPartLen."\n";
echo "lastPart = ".$this->lastPart."\n";
echo "lastPartLen = ".$this->lastPartLen."\n";
}
 
// }}}
}
 
// {{{ test_bugtraq()
 
function test_bugtraq()
{
$tester = new BugtraqTestable;
$tester->setUpVars("BugID: %BUGID%",
"http://bugtracker/?id=%BUGID%",
"[Ii]ssue #?(\d+)",
true);
//$tester->printVars();
$res = $tester->replaceIDs("BugID: 789\n".
"This is a test message that refers to issue #123 and\n".
"issue #456.\n".
"BugID: 789");
echo nl2br($res)."<p>";
$res = $tester->replaceIDs("BugID: 789, 101112\n".
"This is a test message that refers to issue #123 and\n".
"issue #456.\n".
"BugID: 789, 101112");
 
echo nl2br($res)."<p>";
$tester->setAppend(false);
$res = $tester->replaceIDs("BugID: 789\n".
"This is a test message that refers to issue #123 and\n".
"issue #456.\n".
"BugID: 789");
echo nl2br($res)."<p>";
$res = $tester->replaceIDs("BugID: 789, 101112\n".
"This is a test message that refers to issue #123 and\n".
"issue #456.\n".
"BugID: 789, 101112");
 
echo nl2br($res)."<p>";
$tester->setUpVars("BugID: %BUGID%",
"http://bugtracker/?id=%BUGID%",
"[Ii]ssues?:?(\s*(,|and)?\s*#\d+)+\n(\d+)",
true);
$res = $tester->replaceIDs("BugID: 789, 101112\n".
"This is a test message that refers to issue #123 and\n".
"issues #456, #654 and #321.\n".
"BugID: 789, 101112");
 
echo nl2br($res)."<p>";
 
$tester->setUpVars("Test: %BUGID%",
"http://bugtracker/?id=%BUGID%",
"\s*[Cc]ases*\s*[IDs]*\s*[#: ]+((\d+[ ,:;#]*)+)\n(\d+)",
true);
$res = $tester->replaceIDs("Cosmetic change\n".
"CaseIDs: 48");
 
echo nl2br($res)."<p>";
}
 
// }}}
 
?>
/WebSVN/include/command.inc
1,171 → 1,171
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// command.inc
//
// External command handling
 
// {{{ replaceEntities
//
// Replace character codes with HTML entities for display purposes.
// This routine assumes that the character encoding of the string is
// that of the local system (i.e., it's a string returned from a command
// line command).
 
function replaceEntities($str, $rep)
{
global $config;
// Ideally, we'd do this:
//
// $str = htmlentities($str, ENT_COMPAT, $config->inputEnc);
//
// However, htmlentities is very limited in it's ability to process
// character encodings. We have to rely on something more powerful.
if (version_compare(phpversion(), "4.1.0", "<"))
{
// In this case, we can't do any better than assume that the
// input encoding is ISO-8859-1.
$str = htmlentities($str);
}
else
{
$str = toOutputEncoding($str, $rep->getContentEncoding());
 
// $str is now encoded as UTF-8.
$str = htmlentities($str, ENT_COMPAT, $config->outputEnc);
}
return $str;
}
 
// }}}
 
// {{{ toOutputEncoding
 
function toOutputEncoding($str, $inputEncoding = "")
{
global $config;
if (empty($inputEncoding))
$inputEncoding = $config->inputEnc;
// Try to convert the messages based on the locale information
if ($config->inputEnc && $config->outputEnc)
{
if (function_exists("iconv"))
{
$output = @iconv($inputEncoding, $config->outputEnc, $str);
if (!empty($output))
$str = $output;
}
}
 
return $str;
}
 
// }}}
 
// {{{ quoteCommand
 
function quoteCommand($cmd, $redirecterr)
{
global $config;
if ($redirecterr)
$cmd .= " 2>&1";
// On Windows machines, the whole line needs quotes round it so that it's
// passed to cmd.exe correctly
 
if ($config->serverIsWindows)
$cmd = "\"$cmd\"";
return $cmd;
}
 
// }}}
 
// {{{ runCommand
 
function runCommand($cmd, $mayReturnNothing = false)
{
global $lang;
$output = array ();
$err = false;
 
$c = quoteCommand($cmd, false);
// Try to run the command normally
if ($handle = popen($c, 'r'))
{
$firstline = true;
while (!feof($handle))
{
$line = fgets($handle);
if ($firstline && empty($line) && !$mayReturnNothing)
{
$err = true;
}
$firstline = false;
$output[] = toOutputEncoding(rtrim($line));
}
pclose($handle);
if (!$err)
return $output;
}
 
echo '<p>',$lang['BADCMD'],': <code>',$cmd,'</code></p>';
// Rerun the command, this time grabbing the error information
 
$c = quoteCommand($cmd, true);
 
$output = toOutputEncoding(shell_exec($c));
if (!empty($output))
echo '<p>',nl2br($output),'</p>';
exit;
}
 
// }}}
 
// {{{ quote
//
// Quote a string to send to the command line
 
function quote($str)
{
global $config;
 
if ($config->serverIsWindows)
return "\"$str\"";
else
return escapeshellarg($str);
}
 
// }}}
 
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// command.inc
//
// External command handling
 
// {{{ replaceEntities
//
// Replace character codes with HTML entities for display purposes.
// This routine assumes that the character encoding of the string is
// that of the local system (i.e., it's a string returned from a command
// line command).
 
function replaceEntities($str, $rep)
{
global $config;
// Ideally, we'd do this:
//
// $str = htmlentities($str, ENT_COMPAT, $config->inputEnc);
//
// However, htmlentities is very limited in it's ability to process
// character encodings. We have to rely on something more powerful.
if (version_compare(phpversion(), "4.1.0", "<"))
{
// In this case, we can't do any better than assume that the
// input encoding is ISO-8859-1.
$str = htmlentities($str);
}
else
{
$str = toOutputEncoding($str, $rep->getContentEncoding());
 
// $str is now encoded as UTF-8.
$str = htmlentities($str, ENT_COMPAT, $config->outputEnc);
}
return $str;
}
 
// }}}
 
// {{{ toOutputEncoding
 
function toOutputEncoding($str, $inputEncoding = "")
{
global $config;
if (empty($inputEncoding))
$inputEncoding = $config->inputEnc;
// Try to convert the messages based on the locale information
if ($config->inputEnc && $config->outputEnc)
{
if (function_exists("iconv"))
{
$output = @iconv($inputEncoding, $config->outputEnc, $str);
if (!empty($output))
$str = $output;
}
}
 
return $str;
}
 
// }}}
 
// {{{ quoteCommand
 
function quoteCommand($cmd, $redirecterr)
{
global $config;
if ($redirecterr)
$cmd .= " 2>&1";
// On Windows machines, the whole line needs quotes round it so that it's
// passed to cmd.exe correctly
 
if ($config->serverIsWindows)
$cmd = "\"$cmd\"";
return $cmd;
}
 
// }}}
 
// {{{ runCommand
 
function runCommand($cmd, $mayReturnNothing = false)
{
global $lang;
$output = array ();
$err = false;
 
$c = quoteCommand($cmd, false);
// Try to run the command normally
if ($handle = popen($c, 'r'))
{
$firstline = true;
while (!feof($handle))
{
$line = fgets($handle);
if ($firstline && empty($line) && !$mayReturnNothing)
{
$err = true;
}
$firstline = false;
$output[] = toOutputEncoding(rtrim($line));
}
pclose($handle);
if (!$err)
return $output;
}
 
echo '<p>',$lang['BADCMD'],': <code>',$cmd,'</code></p>';
// Rerun the command, this time grabbing the error information
 
$c = quoteCommand($cmd, true);
 
$output = toOutputEncoding(shell_exec($c));
if (!empty($output))
echo '<p>',nl2br($output),'</p>';
exit;
}
 
// }}}
 
// {{{ quote
//
// Quote a string to send to the command line
 
function quote($str)
{
global $config;
 
if ($config->serverIsWindows)
return "\"$str\"";
else
return escapeshellarg($str);
}
 
// }}}
 
?>
/WebSVN/include/configclass.inc
1,1251 → 1,1269
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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.inc4
//
// General class for handling configuration options
 
require_once("include/command.inc");
require_once("include/auth.inc");
require_once("include/version.inc");
 
// Auxillary functions used to sort repositories by name/group
 
// {{{ cmpReps($a, $b)
 
function cmpReps($a, $b)
{
// First, sort by group
$g = strcasecmp($a->group, $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($a->group, $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();
$ptr1 = $ptr2 = 0;
while ($ptr1 < count($array1) && $ptr2 < count($array2))
{
if (call_user_func($cmp_function, $array1[$ptr1], $array2[$ptr2]) < 1)
{
$array[] = $array1[$ptr1++];
}
else
{
$array[] = $array2[$ptr2++];
}
}
// Merge the remainder
while ($ptr1 < count($array1)) $array[] = $array1[$ptr1++];
while ($ptr2 < count($array2)) $array[] = $array2[$ptr2++];
return;
}
 
// }}}
 
// A Repository configuration class
 
Class Repository
{
// {{{ Properties
 
var $name;
var $svnName;
var $path;
var $group;
var $username;
var $password;
// Local configuration options must start off unset
var $allowDownload;
var $minDownloadLevel;
var $allowedExceptions = array();
var $disallowedExceptions = array();
var $rss;
var $spaces;
var $ignoreSvnMimeTypes;
var $ignoreWebSVNContentTypes;
var $bugtraq;
var $auth;
var $contentEnc;
var $templatePath;
 
// }}}
 
// {{{ __construct($name, $svnName, $path, [$group, [$username, [$password]]])
 
function Repository($name, $svnName, $path, $group = NULL, $username = NULL, $password = NULL)
{
$this->name = $name;
$this->svnName = $svnName;
$this->path = $path;
$this->group = $group;
$this->username = $username;
$this->password = $password;
}
 
// }}}
 
// {{{ getDisplayName()
 
function getDisplayName()
{
if(!empty($this->group))
return $this->group.".".$this->name;
else
return $this->name;
}
 
// }}}
 
// {{{ svnParams
 
function svnParams()
{
if (!empty($this->username))
return " --username ".$this->username." --password ".$this->password." ";
else
return " ";
}
 
// }}}
 
// Local configuration accessors
// {{{ RSS Feed
function hideRSS()
{
$this->rss = false;
}
 
function showRSS()
{
$this->rss = true;
}
 
function getHideRSS()
{
global $config;
 
if (isset($this->rss))
return $this->rss;
return $config->getHideRSS();
}
 
// }}}
// {{{ 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)
{
$lastchar = substr($path, -1, 1);
if (!($lastchar == DIRECTORY_SEPARATOR ||
$lastchar == '/' ||
$lastchar == '\\'))
$path .= DIRECTORY_SEPARATOR;
 
$this->templatePath = $path;
}
 
function getTemplatePath()
{
global $config;
if (!empty($this->templatePath))
return $this->templatePath;
else
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();
}
 
// }}}
// {{{ Issue Tracking
function useBugtraqProperties()
{
$this->bugtraq = true;
}
 
function ignoreBugtraqProperties()
{
$this->bugtraq = false;
}
 
function getBugtraq()
{
global $config;
 
if (isset($this->bugtraq))
return $this->bugtraq;
return $config->getBugtraq();
}
 
// }}}
// {{{ Encodings
function setContentEncoding($contentEnc)
{
$this->contentEnc = $contentEnc;
}
 
function getContentEncoding()
{
global $config;
 
if (isset($this->contentEnc))
return $this->contentEnc;
return $config->getContentEncoding();
}
 
// }}}
 
// {{{ Authentication
function useAuthenticationFile($file)
{
if (is_readable($file))
$this->auth =& new Authentication($file);
else
die('Unable to read authentication file "'.$file.'"');
}
 
function hasReadAccess($path, $checkSubFolders = false)
{
global $config;
 
$a = null;
if (isset($this->auth))
$a =& $this->auth;
else
$a =& $config->getAuth();
if (!empty($a))
return $a->hasReadAccess($this->svnName, $path, $checkSubFolders);
// No auth file - free access...
return true;
}
 
function hasUnrestrictedReadAccess($path)
{
global $config;
 
$a = null;
if (isset($this->auth))
$a =& $this->auth;
else
$a =& $config->getAuth();
if (!empty($a))
return $a->hasUnrestrictedReadAccess($this->svnName, $path);
// No auth file - free access...
return true;
}
 
// }}}
 
}
 
// The general configuration class
 
Class Config
{
// {{{ Properties
 
// Tool path locations
 
var $svnlook = "svnlook";
var $svn = "svn --non-interactive --config-dir /tmp";
var $svn_noparams = "svn --config-dir /tmp";
var $diff = "diff";
var $enscript ="enscript";
var $sed = "sed";
var $gzip = "gzip";
var $tar = "tar";
// Other configuration items
var $treeView = true;
var $flatIndex = true;
var $openTree = false;
var $serverIsWindows = false;
var $cacheResults = false;
var $multiViews = false;
var $useEnscript = false;
var $allowDownload = false;
var $minDownloadLevel = 0;
var $allowedExceptions = array();
var $disallowedExceptions = array();
var $rss = true;
var $spaces = 8;
var $bugtraq = false;
var $auth = "";
var $templatePath = "./templates/Standard/";
var $phpCompatPath = './include/PHP/Compat.php';
 
var $ignoreSvnMimeTypes = false;
var $ignoreWebSVNContentTypes = false;
 
var $subversionMajorVersion = "";
var $subversionMinorVersion = "";
 
// Default character encodings
var $inputEnc = ""; // Encoding of output returned from command line
var $contentEnc = ""; // Encoding of repository content
var $outputEnc = "UTF-8"; // Encoding of web page. Now forced to UTF-8
 
var $quote = "'";
 
var $_repositories;
 
// }}}
 
// {{{ __construct()
 
function Config()
{
}
 
// }}}
 
// {{{ Repository configuration
function addRepository($name, $url, $group = NULL, $username = NULL, $password = NULL)
{
$url = str_replace(DIRECTORY_SEPARATOR, "/", $url);
if ($url{strlen($url) - 1} == "/")
$url = substr($url, 0, -1);
$svnName = substr($url, strrpos($url, "/") + 1);
$this->_repositories[] = new Repository($name, $svnName, $url, $group, $username, $password);
}
 
function getRepositories()
{
return $this->_repositories;
}
 
function &findRepository($name)
{
foreach ($this->_repositories as $index => $rep)
{
if (strcmp($rep->getDisplayName(), $name) == 0)
{
$repref =& $this->_repositories[$index];
return $repref;
}
}
print "ERROR: Unable to find repository '$name'";
exit;
}
 
// }}}
 
// {{{ setServerIsWindows
//
// The server is running on Windows
function setServerIsWindows()
{
$this->serverIsWindows = true;
// Try to set the input encoding intelligently
$cp = 0;
if ($cp = @shell_exec("CHCP"))
{
$cp = trim(substr($cp, strpos($cp, ":") + 1));
settype($cp, "integer");
}
// Use the most sensible default value if that failed
if ($cp == 0) $cp = 850;
// We assume, as a default, that the encoding of the repository contents is
// in iso-8859-1, to be compatible with compilers and the like.
$this->setInputEncoding("CP$cp", "iso-8859-1");
// On Windows machines, use double quotes around command line parameters
$this->quote = '"';
}
 
// }}}
 
// {{{ Caching
 
// setCachingOn
//
// Set result caching on
function setCachingOn()
{
$this->cacheResults = true;
}
 
function isCachingOn()
{
return $this->cacheResults;
}
 
// }}}
 
// {{{ MultiViews
 
// useMultiViews
//
// Use MultiViews to access the repository
function useMultiViews()
{
$this->multiViews = true;
}
 
function getUseMultiViews()
{
return $this->multiViews;
}
 
// }}}
 
// {{{ Enscript
 
// useEnscript
//
// Use Enscript to colourise listings
function useEnscript()
{
$this->useEnscript = true;
}
 
function getUseEnscript()
{
return $this->useEnscript;
}
 
// }}}
 
// {{{ RSS
 
// offerRSS
//
// Use Enscript to colourise listings
function hideRSS($myrep = 0)
{
if (empty($myrep))
$this->rss = false;
else
{
$repo =& $this->findRepository($myrep);
$repo->hideRSS();
}
}
 
function getHideRSS()
{
return $this->rss;
}
 
// }}}
 
// {{{ 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 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)
{
$base = $_SERVER["SCRIPT_NAME"];
if ($this->multiViews)
{
// Remove the .php
if (eregi(".php$", $base))
{
// Remove the .php
$base = substr($base, 0, -4);
}
if ($path && $path{0} != "/") $path = "/".$path;
$url = $base;
if (is_object($rep))
{
$url .= "/".$rep->getDisplayName().str_replace('%2F', '/', urlencode($path));
if ($op != "dir" && $op != "file")
$url .= "?op=$op&amp;";
else
$url .= "?";
}
return $url;
}
else
{
switch ($op)
{
case "dir":
$fname = "listing.php";
break;
case "file":
$fname = "filedetails.php";
break;
 
case "log":
$fname = "log.php";
break;
 
case "diff":
$fname = "diff.php";
break;
 
case "blame":
$fname = "blame.php";
break;
 
case "form":
$fname = "form.php";
break;
 
case "rss":
$fname = "rss.php";
break;
 
case "dl":
$fname = "dl.php";
break;
 
case "comp":
$fname = "comp.php";
break;
}
 
if ($rep == -1)
return $fname."?path=".urlencode($path)."&amp;";
else
return $fname."?repname=".urlencode($rep->getDisplayName())."&amp;path=".urlencode($path)."&amp;";
}
}
 
// }}}
 
// {{{ Paths and Commands
 
// setPath
//
// Set the location of the given path
function setPath(&$var, $path, $name, $params = "")
{
$lastchar = substr($path, -1, 1);
$isDir = ($lastchar == DIRECTORY_SEPARATOR ||
$lastchar == "/" ||
$lastchar == "\\");
if (!$isDir)
{
$path .= DIRECTORY_SEPARATOR;
}
 
// On a windows machine we need to put spaces around the entire command
// to allow for spaces in the path
if ($this->serverIsWindows)
$var = "\"$path$name\"";
else
$var = "$path$name";
$var .= " ".$params;
}
 
// setSVNCommandPath
//
// Define the location of the svn and svnlook commands
function setSVNCommandPath($path)
{
$this->setPath($this->svn, $path, "svn", "--non-interactive --config-dir /tmp");
$this->setPath($this->svn_noparams, $path, "svn", " --config-dir /tmp");
$this->setPath($this->svnlook, $path, "svnlook");
}
function getSvnCommand()
{
return $this->svn;
}
 
function getCleanSvnCommand()
{
return $this->svn_noparams;
}
 
function getSvnlookCommand()
{
return $this->svnlook;
}
// 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");
}
 
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;
}
 
// Templates
function setTemplatePath($path, $myrep = 0)
{
if (empty($myrep))
{
$lastchar = substr($path, -1, 1);
if (!($lastchar == DIRECTORY_SEPARATOR ||
$lastchar == '/' ||
$lastchar == '\\'))
$path .= DIRECTORY_SEPARATOR;
 
$this->templatePath = $path;
}
else
{
$repo =& $this->findRepository($myrep);
$repo->setTemplatePath($path);
}
}
 
function getTemplatePath()
{
return $this->templatePath;
}
 
// PHP Compat file (from PEAR)
 
function setPHPCompatPath($path)
{
$this->setPath($this->phpCompatPath, $path, 'Compat.php');
}
 
function getPHPCompatFile()
{
return trim($this->phpCompatPath);
}
 
// }}}
 
// {{{ parentPath
//
// Automatically set up the repositories based on a parent path
function parentPath($path, $group = NULL)
{
if ($handle = @opendir($path))
{
// For each file...
while (false !== ($file = readdir($handle)))
{
// That's also a non hidden directory
if (is_dir($path.DIRECTORY_SEPARATOR.$file) && $file{0} != ".")
{
// And that contains a db directory (in an attempt to not include
// non svn repositories.
if (is_dir($path.DIRECTORY_SEPARATOR.$file.DIRECTORY_SEPARATOR."db"))
{
// We add the repository to the list
$this->addRepository($file, "file:///".$path.DIRECTORY_SEPARATOR.$file, $group);
}
}
}
closedir($handle);
}
 
// Sort the repositories into alphabetical order
if (!empty($this->_repositories))
usort($this->_repositories, "cmpReps");
}
 
// }}}
// {{{ Encoding functions
function setInputEncoding($systemEnc)
{
$this->inputEnc = $systemEnc;
if (!isset($this->contentEnc))
$this->contentEnc = $systemEnc;
}
function getInputEncoding()
{
return $this->inputEnc;
}
 
function setContentEncoding($contentEnc, $myrep = 0)
{
if (empty($myrep))
$this->contentEnc = $contentEnc;
else
{
$repo =& $this->findRepository($myrep);
$repo->setContentEncoding($contentEnc);
}
}
function getContentEncoding()
{
return $this->contentEnc;
}
 
// }}}
 
// {{{ 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;
}
 
// }}}
 
// {{{ Misc settings
function ignoreSvnMimeTypes()
{
$this->ignoreSvnMimeTypes = true;
}
 
function getIgnoreSvnMimeTypes()
{
return $this->ignoreSvnMimeTypes;
}
 
function ignoreWebSVNContentTypes()
{
$this->ignoreWebSVNContentTypes = true;
}
 
function getIgnoreWebSVNContentTypes()
{
return $this->ignoreWebSVNContentTypes;
}
 
function useBugtraqProperties($myrep = 0)
{
if (empty($myrep))
$this->bugtraq = true;
else
{
$repo =& $this->findRepository($myrep);
$repo->useBugtraqProperties();
}
}
function getBugtraq()
{
return $this->bugtraq;
}
 
function useAuthenticationFile($file, $myrep = 0)
{
if (empty($myrep))
{
if (is_readable($file))
$this->auth = new Authentication($file);
else
{
echo "Unable to read authentication file '$file'";
exit;
}
}
else
{
$repo =& $this->findRepository($myrep);
$repo->useAuthenticationFile($file);
}
}
function &getAuth()
{
return $this->auth;
}
 
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;
}
 
// setSubversionMajorVersion
//
// Set subversion major version
function setSubversionMajorVersion($subversionMajorVersion)
{
$this->subversionMajorVersion = $subversionMajorVersion;
}
 
function getSubversionMajorVersion()
{
return $this->subversionMajorVersion;
}
 
// setSubversionMinorVersion
//
// Set subversion minor version
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");
}
 
// }}}
}
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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.inc4
//
// General class for handling configuration options
 
require_once("include/command.inc");
require_once("include/auth.inc");
require_once("include/version.inc");
 
// Auxillary functions used to sort repositories by name/group
 
// {{{ cmpReps($a, $b)
 
function cmpReps($a, $b)
{
// First, sort by group
$g = strcasecmp($a->group, $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($a->group, $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();
$ptr1 = $ptr2 = 0;
while ($ptr1 < count($array1) && $ptr2 < count($array2))
{
if (call_user_func($cmp_function, $array1[$ptr1], $array2[$ptr2]) < 1)
{
$array[] = $array1[$ptr1++];
}
else
{
$array[] = $array2[$ptr2++];
}
}
// Merge the remainder
while ($ptr1 < count($array1)) $array[] = $array1[$ptr1++];
while ($ptr2 < count($array2)) $array[] = $array2[$ptr2++];
return;
}
 
// }}}
 
// A Repository configuration class
 
Class Repository
{
// {{{ Properties
 
var $name;
var $svnName;
var $path;
var $group;
var $username;
var $password;
// Local configuration options must start off unset
var $allowDownload;
var $minDownloadLevel;
var $allowedExceptions = array();
var $disallowedExceptions = array();
var $rss;
var $spaces;
var $ignoreSvnMimeTypes;
var $ignoreWebSVNContentTypes;
var $bugtraq;
var $auth;
var $contentEnc;
var $templatePath;
 
// }}}
 
// {{{ __construct($name, $svnName, $path, [$group, [$username, [$password]]])
 
function Repository($name, $svnName, $path, $group = NULL, $username = NULL, $password = NULL)
{
$this->name = $name;
$this->svnName = $svnName;
$this->path = $path;
$this->group = $group;
$this->username = $username;
$this->password = $password;
}
 
// }}}
 
// {{{ getDisplayName()
 
function getDisplayName()
{
if(!empty($this->group))
return $this->group.".".$this->name;
else
return $this->name;
}
 
// }}}
 
// {{{ svnParams
 
function svnParams()
{
if (!empty($this->username))
return " --username ".$this->username." --password ".$this->password." ";
else
return " ";
}
 
// }}}
 
// Local configuration accessors
// {{{ RSS Feed
function hideRSS()
{
$this->rss = false;
}
 
function showRSS()
{
$this->rss = true;
}
 
function getHideRSS()
{
global $config;
 
if (isset($this->rss))
return $this->rss;
return $config->getHideRSS();
}
 
// }}}
// {{{ 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)
{
$lastchar = substr($path, -1, 1);
if (!($lastchar == DIRECTORY_SEPARATOR ||
$lastchar == '/' ||
$lastchar == '\\'))
$path .= DIRECTORY_SEPARATOR;
 
$this->templatePath = $path;
}
 
function getTemplatePath()
{
global $config;
if (!empty($this->templatePath))
return $this->templatePath;
else
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();
}
 
// }}}
// {{{ Issue Tracking
function useBugtraqProperties()
{
$this->bugtraq = true;
}
 
function ignoreBugtraqProperties()
{
$this->bugtraq = false;
}
 
function getBugtraq()
{
global $config;
 
if (isset($this->bugtraq))
return $this->bugtraq;
return $config->getBugtraq();
}
 
// }}}
// {{{ Encodings
function setContentEncoding($contentEnc)
{
$this->contentEnc = $contentEnc;
}
 
function getContentEncoding()
{
global $config;
 
if (isset($this->contentEnc))
return $this->contentEnc;
return $config->getContentEncoding();
}
 
// }}}
 
// {{{ Authentication
function useAuthenticationFile($file)
{
if (is_readable($file))
$this->auth =& new Authentication($file);
else
die('Unable to read authentication file "'.$file.'"');
}
 
function hasReadAccess($path, $checkSubFolders = false)
{
global $config;
 
$a = null;
if (isset($this->auth))
$a =& $this->auth;
else
$a =& $config->getAuth();
if (!empty($a))
return $a->hasReadAccess($this->svnName, $path, $checkSubFolders);
// No auth file - free access...
return true;
}
 
function hasUnrestrictedReadAccess($path)
{
global $config;
 
$a = null;
if (isset($this->auth))
$a =& $this->auth;
else
$a =& $config->getAuth();
if (!empty($a))
return $a->hasUnrestrictedReadAccess($this->svnName, $path);
// No auth file - free access...
return true;
}
 
// }}}
 
}
 
// The general configuration class
 
Class Config
{
// {{{ Properties
 
// Tool path locations
 
var $svnlook = "svnlook";
var $svn = "svn --non-interactive --config-dir /tmp";
var $svn_noparams = "svn --config-dir /tmp";
var $diff = "diff";
var $enscript ="enscript";
var $sed = "sed";
var $gzip = "gzip";
var $tar = "tar";
// Other configuration items
var $treeView = true;
var $flatIndex = true;
var $openTree = false;
var $serverIsWindows = false;
var $phpCompatEnabled = false;
var $cacheResults = false;
var $multiViews = false;
var $useEnscript = false;
var $allowDownload = false;
var $minDownloadLevel = 0;
var $allowedExceptions = array();
var $disallowedExceptions = array();
var $rss = true;
var $spaces = 8;
var $bugtraq = false;
var $auth = "";
var $templatePath = "./templates/Standard/";
var $phpCompatPath = './include/PHP/Compat.php';
 
var $ignoreSvnMimeTypes = false;
var $ignoreWebSVNContentTypes = false;
 
var $subversionMajorVersion = "";
var $subversionMinorVersion = "";
 
// Default character encodings
var $inputEnc = ""; // Encoding of output returned from command line
var $contentEnc = ""; // Encoding of repository content
var $outputEnc = "UTF-8"; // Encoding of web page. Now forced to UTF-8
 
var $quote = "'";
var $pathSeparator = ":";
 
var $_repositories;
 
// }}}
 
// {{{ __construct()
 
function Config()
{
}
 
// }}}
 
// {{{ Repository configuration
function addRepository($name, $url, $group = NULL, $username = NULL, $password = NULL)
{
$url = str_replace(DIRECTORY_SEPARATOR, "/", $url);
if ($url{strlen($url) - 1} == "/")
$url = substr($url, 0, -1);
$svnName = substr($url, strrpos($url, "/") + 1);
$this->_repositories[] = new Repository($name, $svnName, $url, $group, $username, $password);
}
 
function getRepositories()
{
return $this->_repositories;
}
 
function &findRepository($name)
{
foreach ($this->_repositories as $index => $rep)
{
if (strcmp($rep->getDisplayName(), $name) == 0)
{
$repref =& $this->_repositories[$index];
return $repref;
}
}
print "ERROR: Unable to find repository '$name'";
exit;
}
 
// }}}
 
// {{{ setServerIsWindows
//
// The server is running on Windows
function setServerIsWindows()
{
$this->serverIsWindows = true;
// Try to set the input encoding intelligently
$cp = 0;
if ($cp = @shell_exec("CHCP"))
{
$cp = trim(substr($cp, strpos($cp, ":") + 1));
settype($cp, "integer");
}
// Use the most sensible default value if that failed
if ($cp == 0) $cp = 850;
// We assume, as a default, that the encoding of the repository contents is
// in iso-8859-1, to be compatible with compilers and the like.
$this->setInputEncoding("CP$cp", "iso-8859-1");
// 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 = ";";
}
// }}}
// {{{ setPHPCompatEnabled
//
// Used for PHP4
 
function setPHPCompatEnabled() {
$this->phpCompatEnabled = true;
}
function isPHPCompatEnabled() {
return $this->phpCompatEnabled;
}
 
// }}}
 
// {{{ Caching
 
// setCachingOn
//
// Set result caching on
function setCachingOn()
{
$this->cacheResults = true;
}
 
function isCachingOn()
{
return $this->cacheResults;
}
 
// }}}
 
// {{{ MultiViews
 
// useMultiViews
//
// Use MultiViews to access the repository
function useMultiViews()
{
$this->multiViews = true;
}
 
function getUseMultiViews()
{
return $this->multiViews;
}
 
// }}}
 
// {{{ Enscript
 
// useEnscript
//
// Use Enscript to colourise listings
function useEnscript()
{
$this->useEnscript = true;
}
 
function getUseEnscript()
{
return $this->useEnscript;
}
 
// }}}
 
// {{{ RSS
 
// offerRSS
//
// Use Enscript to colourise listings
function hideRSS($myrep = 0)
{
if (empty($myrep))
$this->rss = false;
else
{
$repo =& $this->findRepository($myrep);
$repo->hideRSS();
}
}
 
function getHideRSS()
{
return $this->rss;
}
 
// }}}
 
// {{{ 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 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)
{
$base = $_SERVER["SCRIPT_NAME"];
if ($this->multiViews)
{
// Remove the .php
if (eregi(".php$", $base))
{
// Remove the .php
$base = substr($base, 0, -4);
}
if ($path && $path{0} != "/") $path = "/".$path;
$url = $base;
if (is_object($rep))
{
$url .= "/".$rep->getDisplayName().str_replace('%2F', '/', urlencode($path));
if ($op != "dir" && $op != "file")
$url .= "?op=$op&amp;";
else
$url .= "?";
}
return $url;
}
else
{
switch ($op)
{
case "dir":
$fname = "listing.php";
break;
case "file":
$fname = "filedetails.php";
break;
 
case "log":
$fname = "log.php";
break;
 
case "diff":
$fname = "diff.php";
break;
 
case "blame":
$fname = "blame.php";
break;
 
case "form":
$fname = "form.php";
break;
 
case "rss":
$fname = "rss.php";
break;
 
case "dl":
$fname = "dl.php";
break;
 
case "comp":
$fname = "comp.php";
break;
}
 
if ($rep == -1)
return $fname."?path=".urlencode($path)."&amp;";
else
return $fname."?repname=".urlencode($rep->getDisplayName())."&amp;path=".urlencode($path)."&amp;";
}
}
 
// }}}
 
// {{{ Paths and Commands
 
// setPath
//
// Set the location of the given path
function setPath(&$var, $path, $name, $params = "")
{
$lastchar = substr($path, -1, 1);
$isDir = ($lastchar == DIRECTORY_SEPARATOR ||
$lastchar == "/" ||
$lastchar == "\\");
if (!$isDir)
{
$path .= DIRECTORY_SEPARATOR;
}
 
// On a windows machine we need to put spaces around the entire command
// to allow for spaces in the path
if ($this->serverIsWindows)
$var = "\"$path$name\"";
else
$var = "$path$name";
$var .= " ".$params;
}
 
// setSVNCommandPath
//
// Define the location of the svn and svnlook commands
function setSVNCommandPath($path)
{
$this->setPath($this->svn, $path, "svn", "--non-interactive --config-dir /tmp");
$this->setPath($this->svn_noparams, $path, "svn", " --config-dir /tmp");
$this->setPath($this->svnlook, $path, "svnlook");
}
function getSvnCommand()
{
return $this->svn;
}
 
function getCleanSvnCommand()
{
return $this->svn_noparams;
}
 
function getSvnlookCommand()
{
return $this->svnlook;
}
// 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");
}
 
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;
}
 
// Templates
function setTemplatePath($path, $myrep = 0)
{
if (empty($myrep))
{
$lastchar = substr($path, -1, 1);
if (!($lastchar == DIRECTORY_SEPARATOR ||
$lastchar == '/' ||
$lastchar == '\\'))
$path .= DIRECTORY_SEPARATOR;
 
$this->templatePath = $path;
}
else
{
$repo =& $this->findRepository($myrep);
$repo->setTemplatePath($path);
}
}
 
function getTemplatePath()
{
return $this->templatePath;
}
 
// PHP Compat file (from PEAR)
 
function setPHPCompatPath($path)
{
$this->setPath($this->phpCompatPath, $path, 'Compat.php');
}
 
function getPHPCompatFile()
{
return trim($this->phpCompatPath);
}
 
// }}}
 
// {{{ parentPath
//
// Automatically set up the repositories based on a parent path
function parentPath($path, $group = NULL)
{
if ($handle = @opendir($path))
{
// For each file...
while (false !== ($file = readdir($handle)))
{
// That's also a non hidden directory
if (is_dir($path.DIRECTORY_SEPARATOR.$file) && $file{0} != ".")
{
// And that contains a db directory (in an attempt to not include
// non svn repositories.
if (is_dir($path.DIRECTORY_SEPARATOR.$file.DIRECTORY_SEPARATOR."db"))
{
// We add the repository to the list
$this->addRepository($file, "file:///".$path.DIRECTORY_SEPARATOR.$file, $group);
}
}
}
closedir($handle);
}
 
// Sort the repositories into alphabetical order
if (!empty($this->_repositories))
usort($this->_repositories, "cmpReps");
}
 
// }}}
// {{{ Encoding functions
function setInputEncoding($systemEnc)
{
$this->inputEnc = $systemEnc;
if (!isset($this->contentEnc))
$this->contentEnc = $systemEnc;
}
function getInputEncoding()
{
return $this->inputEnc;
}
 
function setContentEncoding($contentEnc, $myrep = 0)
{
if (empty($myrep))
$this->contentEnc = $contentEnc;
else
{
$repo =& $this->findRepository($myrep);
$repo->setContentEncoding($contentEnc);
}
}
function getContentEncoding()
{
return $this->contentEnc;
}
 
// }}}
 
// {{{ 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;
}
 
// }}}
 
// {{{ Misc settings
function ignoreSvnMimeTypes()
{
$this->ignoreSvnMimeTypes = true;
}
 
function getIgnoreSvnMimeTypes()
{
return $this->ignoreSvnMimeTypes;
}
 
function ignoreWebSVNContentTypes()
{
$this->ignoreWebSVNContentTypes = true;
}
 
function getIgnoreWebSVNContentTypes()
{
return $this->ignoreWebSVNContentTypes;
}
 
function useBugtraqProperties($myrep = 0)
{
if (empty($myrep))
$this->bugtraq = true;
else
{
$repo =& $this->findRepository($myrep);
$repo->useBugtraqProperties();
}
}
function getBugtraq()
{
return $this->bugtraq;
}
 
function useAuthenticationFile($file, $myrep = 0)
{
if (empty($myrep))
{
if (is_readable($file))
$this->auth = new Authentication($file);
else
{
echo "Unable to read authentication file '$file'";
exit;
}
}
else
{
$repo =& $this->findRepository($myrep);
$repo->useAuthenticationFile($file);
}
}
function &getAuth()
{
return $this->auth;
}
 
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;
}
 
// setSubversionMajorVersion
//
// Set subversion major version
function setSubversionMajorVersion($subversionMajorVersion)
{
$this->subversionMajorVersion = $subversionMajorVersion;
}
 
function getSubversionMajorVersion()
{
return $this->subversionMajorVersion;
}
 
// setSubversionMinorVersion
//
// Set subversion minor version
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");
}
 
// }}}
}
?>
/WebSVN/include/distconfig.inc
1,375 → 1,376
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// config.inc
//
// Configuration parameters
 
// --- FOLLOW THE INSTRUCTIONS BELOW TO CONFIGURE YOUR SETUP ---
 
// {{{ PLATFORM CONFIGURATION ---
 
// Uncomment the next line if your running a windows server
//
// $config->setServerIsWindows();
 
// Configure these lines if your commands aren't on your path.
//
// $config->setSVNCommandPath('Path/to/svn and svnlook/ e.g. c:\\program files\\subversion\\bin');
// $config->setDiffPath('Path/to/diff/command/');
 
// For syntax colouring, if option enabled...
// $config->setEnscriptPath('Path/to/enscript/command/');
// $config->setSedPath('Path/to/sed/command/');
 
// For delivered tarballs, if option enabled...
// $config->setTarPath('Path/to/tar/command/');
 
// For delivered GZIP'd files and tarballs, if option enabled...
// $config->setGZipPath('Path/to/gzip/command/');
 
// }}}
 
// {{{ REPOSITORY SETUP ---
 
// There are 2 methods for defining the repositiories available on the system. Either you list
// them by hand, in which case you can give each one the name of your choice, or you use the
// parent path function, in which case the name of the directory is used as the repository name.
//
// In all cases, you may optionally supply a group name to the repositories. This is useful in the
// case that you need to separate your projects. Grouped Repositories are referred to using the
// convention GroupName.RepositoryName
//
// Performance is much better on local repositories (e.g. accessed by file:///). However, you
// can also provide an interface onto a remote repository. In this case you should supply the
// username and password needed to access it.
//
// To configure the repositories by hand, copy the appropriate line below, uncomment it and
// replace the name and URL of your repository.
 
// Local repositories (without and with optional group):
//
// $config->addRepository('NameToDisplay', 'URL to repository (e.g. file:///c:/svn/proj)');
// $config->addRepository('NameToDisplay', 'URL to repository (e.g. file:///c:/svn/proj)', 'group');
//
// Remote repositories (without and with optional group):
//
// $config->addRepository('NameToDisplay', 'URL (e.g. http://path/to/rep)', NULL, 'username', 'password');
// $config->addRepository('NameToDisplay', 'URL (e.g. http://path/to/rep)', 'group', 'username', 'password');
//
// To use the parent path method, uncomment the newt line and and replace the path with your one. You
// can call the function several times if you have several parent paths. Note that in this case the
// path is a filesystem path
//
// $config->parentPath('Path/to/parent (e.g. c:\\svn)');
 
// }}}
 
// {{{ LOOK AND FEEL ---
//
// Uncomment ONLY the template file that you want.
 
$config->setTemplatePath("$locwebsvnreal/templates/Standard/");
// $config->setTemplatePath("$locwebsvnreal/templates/BlueGrey/");
// $config->setTemplatePath("$locwebsvnreal/templates/Zinn/");
 
// You may also specify a per repository template file by uncommenting and changing the following
// line as necessary. Use the convention "groupname.myrep" if your repository is in a group.
 
// $config->setTemplatePath('$locwebsvnreal/templates/Standard/', 'myrep'); // Access file for myrep
 
// The index page containing the projects may either be displayed as a flat view (the default),
// where grouped repositories are displayed as "GroupName.RepName" or as a tree view.
// In the case of a tree view, you may choose whether the entire tree is open by default.
 
// $config->useTreeIndex(false); // Tree index, closed by default
// $config->useTreeIndex(true); // Tree index, open by default
 
// By default, WebSVN displays a tree view onto the current directory. You can however
// choose to display a flat view of the current directory only, which may make the display
// load faster. Uncomment this line if you want that.
 
// $config->useFlatView();
 
// }}}
 
// {{{ LANGUAGE SETUP ---
 
// WebSVN uses the iconv module to convert messages from your system's character set to the
// UTF-8 output encoding. If you find that your log messages aren't displayed correctly then
// you'll need to change the value here.
//
// You may also specify the character encoding of the repository contents if different from
// the system encoding. This is typically the case for windows users, whereby the command
// line returns, for example, CP850 encoded strings, whereas the source files are encoded
// as iso-8859-1 by Windows based text editors. When display text file, WebSVN will convert
// them from the content encoding to the output encoding (UTF-8).
//
// WebSVN does its best to automate all this, so only use the following if it doesn't work
// "out of the box". Uncomment and change one of the examples below.
//
// $config->setInputEncoding('CP850'); // Encoding of result returned by svn command line, etc.
// $config->setContentEncoding('iso-8859-1'); // Content encoding of all your repositories // repositories
 
// You may also specify a content encoding on a per repository basis. Uncomment and copy this
// line as necessary.
//
// $config->setContentEncoding('iso-8859-1', 'MyEnc');
 
// Note for Windows users: To enable iconv you'll need to enable the extension in your php.ini file
// AND copy iconv.dll (not php_iconv.dll) to your Windows system folder. In most cases the correct
// encoding is set when you call $config->setServerIsWindows();.
 
// Note for *nix users. You'll need to have iconv compiled into your binary. The default input and
// output encodings are taken from your locale informations. Override these if they aren't correct.
 
// Uncomment the default language. If you want English then don't do anything here.
//
// include 'languages/catalan.inc';
// include 'languages/danish.inc';
// include 'languages/dutch.inc';
// include 'languages/finnish.inc';
// include 'languages/french.inc';
// include 'languages/german.inc';
// include 'languages/japanese.inc';
// include 'languages/korean.inc';
// include 'languages/norwegian.inc';
// include 'languages/polish.inc';
// include 'languages/portuguese.inc';
// include 'languages/russian.inc';
// include 'languages/schinese.inc';
// include 'languages/slovenian.inc';
// include 'languages/spanish.inc';
// include 'languages/swedish.inc';
// include 'languages/tchinese.inc';
// include 'languages/turkish.inc';
 
// }}}
 
// {{{ MULTIVIEWS ---
 
// Uncomment this line if you want to use MultiView to access the repository by, for example:
//
// http://servername/wsvn/repname/path/in/repository
//
// Note: The websvn directory will need to have Multiviews turned on in Apache, and you'll need to configure
// wsvn.php
 
// $config->useMultiViews();
 
// }}}
 
// {{{ ACCESS RIGHTS ---
 
// Uncomment this line if you want to use your Subversion access file to control access
// rights via WebSVN. For this to work, you'll need to set up the same Apache based authentication
// to the WebSVN (or wsvn) directory as you have for Subversion itself. More information can be
// found in install.txt
 
// $config->useAuthenticationFile('/path/to/accessfile'); // Global access file
 
// You may also specify a per repository access file by uncommenting and copying the following
// line as necessary. Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->useAuthenticationFile('/path/to/accessfile', 'myrep'); // Access file for myrep
 
// }}}
 
// {{{ FILE CONTENT ---
//
// You may wish certain file types to be GZIP'd and delieved to the user when clicked apon.
// This is useful for binary files and the like that don't display well in a browser window!
// Copy, uncomment and modify this line for each extension to which this rule should apply.
// (Don't forget the . before the extension. You don't need an index between the []'s).
// If you'd rather that the files were delivered uncompressed with the associated MIME type,
// then read below.
//
// $zipped[] = '.dll';
 
// Subversion controlled files have an svn:mime-type property that can
// be set on a file indicating its mime type. By default binary files
// are set to the generic appcliation/octet-stream, and other files
// don't have it set at all. WebSVN also has a built-in list of
// associations from file extension to MIME content type. (You can
// view this list in setup.inc).
//
// Determining the content-type: By default, if the svn:mime-type
// property exists and is different from application/octet-stream, it
// is used. Otherwise, if the built-in list has a contentType entry
// for the extension of the file, that is used. Otherwise, if the
// svn:mime-type property exists has the generic binary value of
// application/octet-stream, the file will be served as a binary
// file. Otherwise, the file will be brought up as ASCII text in the
// browser window (although this text may optionally be colourised.
// See below).
//
// Uncomment this if you want to ignore any svn:mime-type property on your
// files.
//
// $config->ignoreSvnMimeTypes();
//
// Uncomment this if you want skip WebSVN's custom mime-type handling
//
// $config->ignoreWebSVNContentTypes();
//
// Following the examples below, you can add new associations, modify
// the default ones or even delete them entirely (to show them in
// ASCII via WebSVN).
 
// $contentType['.c'] = 'plain/text'; // Create a new association
// $contentType['.doc'] = 'plain/text'; // Modify an existing one
// unset($contentType['.m'] // Remove a default association
 
// }}}
 
// {{{ TARBALLS ---
 
// You need tar and gzip installed on your system. Set the paths above if necessary
//
// Uncomment the line below to offer a tarball download option across all your
// repositories.
//
// $config->allowDownload();
//
// To change the global option for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->allowDownload('myrep'); // Specifically allow downloading for 'myrep'
// $config->disallowDownload('myrep'); // Specifically disallow downloading for 'myrep'
 
// You can also choose the minimum directory level from which you'll allow downloading.
// A value of zero will allow downloading from the root. 1 will allow downloding of directories
// in the root, etc.
//
// If your project is arranged with trunk, tags and branches at the root level, then a value of 2
// would allow the downloading of directories within branches/tags while disallowing the download
// of the entire branches or tags directories. This would also stop downloading of the trunk, but
// see after for path exceptions.
//
// Change the line below to set the download level across all your repositories.
 
$config->setMinDownloadLevel(2);
 
// To change the level for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->setMinDownloadLevel(2, 'myrep');
 
// Finally, you may add or remove certain directories (and their contents) either globally
// or on a per repository basis. Uncomment and copy the following lines as necessary. Note
// that the these are searched in the order than you give them until a match is made (with the
// exception that all the per repository exceptions are tested before the global ones). This means
// that you must disallow /a/b/c/ before you allow /a/b/ otherwise the allowed match on /a/b/ will
// stop any further searching, thereby allowing downloads on /a/b/c/.
 
// Global exceptions possibilties:
//
// $config->addAllowedDownloadException('/path/to/allowed/directory/');
// $config->addDisAllowedDownloadException('/path/to/disallowed/directory/');
//
// Per repository exception possibilties:
// Use the convention 'groupname.myrep' if your repository is in a group.
//
// $config->addAllowedDownloadException('/path/to/allowed/directory/', 'myrep');
// $config->addDisAllowedDownloadException('/path/to/disallowed/directory/', 'myrep');
 
// }}}
 
// {{{ COLOURISATION ---
 
// Uncomment this line if you want to use Enscript to colourise your file listings
//
// You'll need Enscript version 1.6 or higher AND Sed installed to use this feature.
// Set the path above.
//
// $config->useEnscript();
 
// Enscript need to be told what the contents of a file are so that it can be colourised
// correctly. WebSVN includes a predefined list of mappings from file extension to Enscript
// file type (viewable in setup.inc).
//
// Here you should add and other extensions not already listed or redefine the default ones. eg:
//
// $extEnscript['.pas'] = 'pascal';
//
// Note that extensions are case sensitive.
 
// }}}
 
// {{{ RSSFEED ---
 
// Uncomment this line if you wish to hide the RSS feed links across all repositories
//
// $config->hideRSS();
//
// To change the global option for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->hideRSS('myrep'); // Specifically hide RSS links for 'myrep'
// $config->showRSS('myrep'); // Specifically show RSS links for 'myrep'
 
// }}}
 
// {{{ BUGTRAQ ---
 
// Uncomment this line if you wish to use bugtraq: properties to show links to your BugTracker
// from the log messages.
//
// $config->useBugtraqProperties();
//
// To change the global option for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->useBugtraqProperties('myrep'); // Specifically use bugtraq properties for 'myrep'
// $config->ignoreBugtraqProperties('myrep'); // Specifically ignore bugtraq properties for 'myrep'
 
// }}}
 
// {{{ MISCELLANEOUS ---
 
// Comment out this if you don't have the right to use it. Be warned that you may need it however!
set_time_limit(0);
 
// Comment this line to turn off caching of repo information. This will slow down your browsing.
$config->setCachingOn();
 
// Number of spaces to expand tabs to in diff/listing view across all repositories
 
$config->expandTabsBy(8);
 
// To change the global option for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->expandTabsBy(3, 'myrep'); // Expand Tabs by 3 for repository 'myrep'
 
// For installations without PHP5, a copy of PEAR's PHP Compat library is included.
// If you have your own version of Compat you wish to use, go ahead and specify here.
 
// $config->setPHPCompatPath('/usr/share/php/PHP/');
 
// }}}
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// config.inc
//
// Configuration parameters
 
// --- FOLLOW THE INSTRUCTIONS BELOW TO CONFIGURE YOUR SETUP ---
 
// {{{ PLATFORM CONFIGURATION ---
 
// Uncomment the next line if your running a windows server
//
// $config->setServerIsWindows();
 
// Configure these lines if your commands aren't on your path.
//
// $config->setSVNCommandPath('Path/to/svn and svnlook/ e.g. c:\\program files\\subversion\\bin');
// $config->setDiffPath('Path/to/diff/command/');
 
// For syntax colouring, if option enabled...
// $config->setEnscriptPath('Path/to/enscript/command/');
// $config->setSedPath('Path/to/sed/command/');
 
// For delivered tarballs, if option enabled...
// $config->setTarPath('Path/to/tar/command/');
 
// For delivered GZIP'd files and tarballs, if option enabled...
// $config->setGZipPath('Path/to/gzip/command/');
 
// }}}
 
// {{{ REPOSITORY SETUP ---
 
// There are 2 methods for defining the repositiories available on the system. Either you list
// them by hand, in which case you can give each one the name of your choice, or you use the
// parent path function, in which case the name of the directory is used as the repository name.
//
// In all cases, you may optionally supply a group name to the repositories. This is useful in the
// case that you need to separate your projects. Grouped Repositories are referred to using the
// convention GroupName.RepositoryName
//
// Performance is much better on local repositories (e.g. accessed by file:///). However, you
// can also provide an interface onto a remote repository. In this case you should supply the
// username and password needed to access it.
//
// To configure the repositories by hand, copy the appropriate line below, uncomment it and
// replace the name and URL of your repository.
 
// Local repositories (without and with optional group):
//
// $config->addRepository('NameToDisplay', 'URL to repository (e.g. file:///c:/svn/proj)');
// $config->addRepository('NameToDisplay', 'URL to repository (e.g. file:///c:/svn/proj)', 'group');
//
// Remote repositories (without and with optional group):
//
// $config->addRepository('NameToDisplay', 'URL (e.g. http://path/to/rep)', NULL, 'username', 'password');
// $config->addRepository('NameToDisplay', 'URL (e.g. http://path/to/rep)', 'group', 'username', 'password');
//
// To use the parent path method, uncomment the newt line and and replace the path with your one. You
// can call the function several times if you have several parent paths. Note that in this case the
// path is a filesystem path
//
// $config->parentPath('Path/to/parent (e.g. c:\\svn)');
 
// }}}
 
// {{{ LOOK AND FEEL ---
//
// Uncomment ONLY the template file that you want.
 
$config->setTemplatePath("$locwebsvnreal/templates/Standard/");
// $config->setTemplatePath("$locwebsvnreal/templates/BlueGrey/");
// $config->setTemplatePath("$locwebsvnreal/templates/Zinn/");
 
// You may also specify a per repository template file by uncommenting and changing the following
// line as necessary. Use the convention "groupname.myrep" if your repository is in a group.
 
// $config->setTemplatePath('$locwebsvnreal/templates/Standard/', 'myrep'); // Access file for myrep
 
// The index page containing the projects may either be displayed as a flat view (the default),
// where grouped repositories are displayed as "GroupName.RepName" or as a tree view.
// In the case of a tree view, you may choose whether the entire tree is open by default.
 
// $config->useTreeIndex(false); // Tree index, closed by default
// $config->useTreeIndex(true); // Tree index, open by default
 
// By default, WebSVN displays a tree view onto the current directory. You can however
// choose to display a flat view of the current directory only, which may make the display
// load faster. Uncomment this line if you want that.
 
// $config->useFlatView();
 
// }}}
 
// {{{ LANGUAGE SETUP ---
 
// WebSVN uses the iconv module to convert messages from your system's character set to the
// UTF-8 output encoding. If you find that your log messages aren't displayed correctly then
// you'll need to change the value here.
//
// You may also specify the character encoding of the repository contents if different from
// the system encoding. This is typically the case for windows users, whereby the command
// line returns, for example, CP850 encoded strings, whereas the source files are encoded
// as iso-8859-1 by Windows based text editors. When display text file, WebSVN will convert
// them from the content encoding to the output encoding (UTF-8).
//
// WebSVN does its best to automate all this, so only use the following if it doesn't work
// "out of the box". Uncomment and change one of the examples below.
//
// $config->setInputEncoding('CP850'); // Encoding of result returned by svn command line, etc.
// $config->setContentEncoding('iso-8859-1'); // Content encoding of all your repositories // repositories
 
// You may also specify a content encoding on a per repository basis. Uncomment and copy this
// line as necessary.
//
// $config->setContentEncoding('iso-8859-1', 'MyEnc');
 
// Note for Windows users: To enable iconv you'll need to enable the extension in your php.ini file
// AND copy iconv.dll (not php_iconv.dll) to your Windows system folder. In most cases the correct
// encoding is set when you call $config->setServerIsWindows();.
 
// Note for *nix users. You'll need to have iconv compiled into your binary. The default input and
// output encodings are taken from your locale informations. Override these if they aren't correct.
 
// Uncomment the default language. If you want English then don't do anything here.
//
// include 'languages/catalan.inc';
// include 'languages/czech.inc';
// include 'languages/danish.inc';
// include 'languages/dutch.inc';
// include 'languages/finnish.inc';
// include 'languages/french.inc';
// include 'languages/german.inc';
// include 'languages/japanese.inc';
// include 'languages/korean.inc';
// include 'languages/norwegian.inc';
// include 'languages/polish.inc';
// include 'languages/portuguese.inc';
// include 'languages/russian.inc';
// include 'languages/schinese.inc';
// include 'languages/slovenian.inc';
// include 'languages/spanish.inc';
// include 'languages/swedish.inc';
// include 'languages/tchinese.inc';
// include 'languages/turkish.inc';
 
// }}}
 
// {{{ MULTIVIEWS ---
 
// Uncomment this line if you want to use MultiView to access the repository by, for example:
//
// http://servername/wsvn/repname/path/in/repository
//
// Note: The websvn directory will need to have Multiviews turned on in Apache, and you'll need to configure
// wsvn.php
 
// $config->useMultiViews();
 
// }}}
 
// {{{ ACCESS RIGHTS ---
 
// Uncomment this line if you want to use your Subversion access file to control access
// rights via WebSVN. For this to work, you'll need to set up the same Apache based authentication
// to the WebSVN (or wsvn) directory as you have for Subversion itself. More information can be
// found in install.txt
 
// $config->useAuthenticationFile('/path/to/accessfile'); // Global access file
 
// You may also specify a per repository access file by uncommenting and copying the following
// line as necessary. Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->useAuthenticationFile('/path/to/accessfile', 'myrep'); // Access file for myrep
 
// }}}
 
// {{{ FILE CONTENT ---
//
// You may wish certain file types to be GZIP'd and delieved to the user when clicked apon.
// This is useful for binary files and the like that don't display well in a browser window!
// Copy, uncomment and modify this line for each extension to which this rule should apply.
// (Don't forget the . before the extension. You don't need an index between the []'s).
// If you'd rather that the files were delivered uncompressed with the associated MIME type,
// then read below.
//
// $zipped[] = '.dll';
 
// Subversion controlled files have an svn:mime-type property that can
// be set on a file indicating its mime type. By default binary files
// are set to the generic appcliation/octet-stream, and other files
// don't have it set at all. WebSVN also has a built-in list of
// associations from file extension to MIME content type. (You can
// view this list in setup.inc).
//
// Determining the content-type: By default, if the svn:mime-type
// property exists and is different from application/octet-stream, it
// is used. Otherwise, if the built-in list has a contentType entry
// for the extension of the file, that is used. Otherwise, if the
// svn:mime-type property exists has the generic binary value of
// application/octet-stream, the file will be served as a binary
// file. Otherwise, the file will be brought up as ASCII text in the
// browser window (although this text may optionally be colourised.
// See below).
//
// Uncomment this if you want to ignore any svn:mime-type property on your
// files.
//
// $config->ignoreSvnMimeTypes();
//
// Uncomment this if you want skip WebSVN's custom mime-type handling
//
// $config->ignoreWebSVNContentTypes();
//
// Following the examples below, you can add new associations, modify
// the default ones or even delete them entirely (to show them in
// ASCII via WebSVN).
 
// $contentType['.c'] = 'plain/text'; // Create a new association
// $contentType['.doc'] = 'plain/text'; // Modify an existing one
// unset($contentType['.m'] // Remove a default association
 
// }}}
 
// {{{ TARBALLS ---
 
// You need tar and gzip installed on your system. Set the paths above if necessary
//
// Uncomment the line below to offer a tarball download option across all your
// repositories.
//
// $config->allowDownload();
//
// To change the global option for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->allowDownload('myrep'); // Specifically allow downloading for 'myrep'
// $config->disallowDownload('myrep'); // Specifically disallow downloading for 'myrep'
 
// You can also choose the minimum directory level from which you'll allow downloading.
// A value of zero will allow downloading from the root. 1 will allow downloding of directories
// in the root, etc.
//
// If your project is arranged with trunk, tags and branches at the root level, then a value of 2
// would allow the downloading of directories within branches/tags while disallowing the download
// of the entire branches or tags directories. This would also stop downloading of the trunk, but
// see after for path exceptions.
//
// Change the line below to set the download level across all your repositories.
 
$config->setMinDownloadLevel(2);
 
// To change the level for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->setMinDownloadLevel(2, 'myrep');
 
// Finally, you may add or remove certain directories (and their contents) either globally
// or on a per repository basis. Uncomment and copy the following lines as necessary. Note
// that the these are searched in the order than you give them until a match is made (with the
// exception that all the per repository exceptions are tested before the global ones). This means
// that you must disallow /a/b/c/ before you allow /a/b/ otherwise the allowed match on /a/b/ will
// stop any further searching, thereby allowing downloads on /a/b/c/.
 
// Global exceptions possibilties:
//
// $config->addAllowedDownloadException('/path/to/allowed/directory/');
// $config->addDisAllowedDownloadException('/path/to/disallowed/directory/');
//
// Per repository exception possibilties:
// Use the convention 'groupname.myrep' if your repository is in a group.
//
// $config->addAllowedDownloadException('/path/to/allowed/directory/', 'myrep');
// $config->addDisAllowedDownloadException('/path/to/disallowed/directory/', 'myrep');
 
// }}}
 
// {{{ COLOURISATION ---
 
// Uncomment this line if you want to use Enscript to colourise your file listings
//
// You'll need Enscript version 1.6 or higher AND Sed installed to use this feature.
// Set the path above.
//
// $config->useEnscript();
 
// Enscript need to be told what the contents of a file are so that it can be colourised
// correctly. WebSVN includes a predefined list of mappings from file extension to Enscript
// file type (viewable in setup.inc).
//
// Here you should add and other extensions not already listed or redefine the default ones. eg:
//
// $extEnscript['.pas'] = 'pascal';
//
// Note that extensions are case sensitive.
 
// }}}
 
// {{{ RSSFEED ---
 
// Uncomment this line if you wish to hide the RSS feed links across all repositories
//
// $config->hideRSS();
//
// To change the global option for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->hideRSS('myrep'); // Specifically hide RSS links for 'myrep'
// $config->showRSS('myrep'); // Specifically show RSS links for 'myrep'
 
// }}}
 
// {{{ BUGTRAQ ---
 
// Uncomment this line if you wish to use bugtraq: properties to show links to your BugTracker
// from the log messages.
//
// $config->useBugtraqProperties();
//
// To change the global option for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->useBugtraqProperties('myrep'); // Specifically use bugtraq properties for 'myrep'
// $config->ignoreBugtraqProperties('myrep'); // Specifically ignore bugtraq properties for 'myrep'
 
// }}}
 
// {{{ MISCELLANEOUS ---
 
// Comment out this if you don't have the right to use it. Be warned that you may need it however!
set_time_limit(0);
 
// Comment this line to turn off caching of repo information. This will slow down your browsing.
$config->setCachingOn();
 
// Number of spaces to expand tabs to in diff/listing view across all repositories
 
$config->expandTabsBy(8);
 
// To change the global option for individual repositories, uncomment and replicate
// the required line below (replacing 'myrep' for the name of the repository to be changed).
// Use the convention 'groupname.myrep' if your repository is in a group.
 
// $config->expandTabsBy(3, 'myrep'); // Expand Tabs by 3 for repository 'myrep'
 
// For installations without PHP5, a copy of PEAR's PHP Compat library is included.
// If you have your own version of Compat you wish to use, go ahead and specify here.
 
// $config->setPHPCompatPath('/usr/share/php/PHP/');
 
// }}}
?>
/WebSVN/include/feedcreator.class.php
1,1153 → 1,1153
<?php
# vim:et:ts=4:sts=4:sw=4:fdm=marker:
# {{{ Info
/***************************************************************************
 
FeedCreator class v1.6
originally (c) Kai Blankenhorn
www.bitfolge.de
kaib@bitfolge.de
v1.3 work by Scott Reynen (scott@randomchaos.com) and Kai Blankenhorn
v1.5 OPML support by Dirk Clemens
 
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: <http://www.gnu.org/licenses/gpl.txt>
 
****************************************************************************
 
 
Changelog:
 
Modifications for WebSVN:
The main description link wasn't put through htmlspecialcharacters
Output encoding now defined by $config
Remove hardcoded time zone
 
v1.6 05-10-04
added stylesheet to RSS 1.0 feeds
fixed generator comment (thanks Kevin L. Papendick and Tanguy Pruvot)
fixed RFC822 date bug (thanks Tanguy Pruvot)
added TimeZone customization for RFC8601 (thanks Tanguy Pruvot)
fixed Content-type could be empty (thanks Tanguy Pruvot)
fixed author/creator in RSS1.0 (thanks Tanguy Pruvot)
 
 
v1.6 beta 02-28-04
added Atom 0.3 support (not all features, though)
improved OPML 1.0 support (hopefully - added more elements)
added support for arbitrary additional elements (use with caution)
code beautification :-)
considered beta due to some internal changes
 
v1.5.1 01-27-04
fixed some RSS 1.0 glitches (thanks to Stéphane Vanpoperynghe)
fixed some inconsistencies between documentation and code (thanks to Timothy Martin)
 
v1.5 01-06-04
added support for OPML 1.0
added more documentation
 
v1.4 11-11-03
optional feed saving and caching
improved documentation
minor improvements
 
v1.3 10-02-03
renamed to FeedCreator, as it not only creates RSS anymore
added support for mbox
tentative support for echo/necho/atom/pie/???
v1.2 07-20-03
intelligent auto-truncating of RSS 0.91 attributes
don't create some attributes when they're not set
documentation improved
fixed a real and a possible bug with date conversions
code cleanup
 
v1.1 06-29-03
added images to feeds
now includes most RSS 0.91 attributes
added RSS 2.0 feeds
 
v1.0 06-24-03
initial release
 
 
 
***************************************************************************/
 
/*** GENERAL USAGE *********************************************************
 
include("feedcreator.class.php");
 
$rss = new UniversalFeedCreator();
$rss->useCached(); // use cached version if age<1 hour
$rss->title = "PHP news";
$rss->description = "daily news from the PHP scripting world";
$rss->link = "http://www.dailyphp.net/news";
$rss->syndicationURL = "http://www.dailyphp.net/".$_SERVER["PHP_SELF"];
 
$image = new FeedImage();
$image->title = "dailyphp.net logo";
$image->url = "http://www.dailyphp.net/images/logo.gif";
$image->link = "http://www.dailyphp.net";
$image->description = "Feed provided by dailyphp.net. Click to visit.";
$rss->image = $image;
 
// get your news items from somewhere, e.g. your database:
mysql_select_db($dbHost, $dbUser, $dbPass);
$res = mysql_query("SELECT * FROM news ORDER BY newsdate DESC");
while ($data = mysql_fetch_object($res)) {
$item = new FeedItem();
$item->title = $data->title;
$item->link = $data->url;
$item->description = $data->short;
$item->date = $data->newsdate;
$item->source = "http://www.dailyphp.net";
$item->author = "John Doe";
$rss->addItem($item);
}
 
// valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1 (deprecated),
// MBOX, OPML, ATOM0.3
echo $rss->saveFeed("RSS1.0", "news/feed.xml");
 
# }}}
 
***************************************************************************
* A little setup *
**************************************************************************/
 
/**
* Version string.
**/
define("FEEDCREATOR_VERSION", "FeedCreator 1.6");
 
 
 
/**
* A FeedItem is a part of a FeedCreator feed.
*
* @author Kai Blankenhorn <kaib@bitfolge.de>
* @since 1.3
*/
class FeedItem {
# {{{ Properties
 
/**
* Mandatory attributes of an item.
*/
var $title, $description, $link;
/**
* Optional attributes of an item.
*/
var $author, $authorEmail, $image, $category, $comments, $guid, $source, $creator;
/**
* Publishing date of an item. May be in one of the following formats:
*
* RFC 822:
* "Mon, 20 Jan 03 18:05:41 +0400"
* "20 Jan 03 18:05:41 +0000"
*
* ISO 8601:
* "2003-01-20T18:05:41+04:00"
*
* Unix:
* 1043082341
*/
var $date;
/**
* Any additional elements to include as an assiciated array. All $key => $value pairs
* will be included unencoded in the feed item in the form
* <$key>$value</$key>
* Again: No encoding will be used! This means you can invalidate or enhance the feed
* if $value contains markup. This may be abused to embed tags not implemented by
* the FeedCreator class used.
*/
var $additionalElements = Array();
 
// on hold
// var $source;
 
# }}}
}
 
 
 
/**
* An FeedImage may be added to a FeedCreator feed.
* @author Kai Blankenhorn <kaib@bitfolge.de>
* @since 1.3
*/
class FeedImage {
# {{{ Properties
 
/**
* Mandatory attributes of an image.
*/
var $title, $url, $link;
/**
* Optional attributes of an image.
*/
var $width, $height, $description;
 
# }}}
}
 
 
/**
* UniversalFeedCreator lets you choose during runtime which
* format to build.
* For general usage of a feed class, see the FeedCreator class
* below or the example above.
*
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class UniversalFeedCreator extends FeedCreator {
var $_feed;
# {{{ _setFormat
function _setFormat($format) {
switch (strtoupper($format)) {
case "2.0":
// fall through
case "RSS2.0":
$this->_feed = new RSSCreator20();
break;
case "1.0":
// fall through
case "RSS1.0":
$this->_feed = new RSSCreator10();
break;
case "0.91":
// fall through
case "RSS0.91":
$this->_feed = new RSSCreator091();
break;
case "PIE0.1":
$this->_feed = new PIECreator01();
break;
case "MBOX":
$this->_feed = new MBOXCreator();
break;
case "OPML":
$this->_feed = new OPMLCreator();
break;
case "ATOM0.3":
$this->_feed = new AtomCreator03();
break;
default:
$this->_feed = new RSSCreator091();
break;
}
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if ($key!="feed") {
$this->_feed->{$key} = $this->{$key};
}
}
}
# }}}
# {{{ createFeed
/**
* Creates a syndication feed based on the items previously added.
*
* @see FeedCreator::addItem()
* @param string format format the feed should comply to. Valid values are:
* "PIE0.1", "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML".
* @return string the contents of the feed.
*/
function createFeed($format = "RSS0.91") {
$this->_setFormat($format);
return $this->_feed->createFeed();
}
# }}}
 
# {{{ saveFeed
/**
* Saves this feed as a file on the local disk. After the file is saved, an HTTP redirect
* header may be sent to redirect the use to the newly created file.
* @since 1.4
*
* @param string format format the feed should comply to. Valid values are:
* "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3".
* @param string filename optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
* @param boolean displayContents optional send the content of the file or not. If true, the file will be sent in the body of the response.
*/
function saveFeed($format="RSS0.91", $filename="", $displayContents=true) {
$this->_setFormat($format);
$this->_feed->saveFeed($filename, $displayContents);
}
# }}}
 
}
 
 
/**
* FeedCreator is the abstract base implementation for concrete
* implementations that implement a specific format of syndication.
*
* @abstract
* @author Kai Blankenhorn <kaib@bitfolge.de>
* @since 1.4
*/
class FeedCreator {
# {{{ Properties
 
/**
* Mandatory attributes of a feed.
*/
var $title, $description, $link;
 
 
/**
* Optional attributes of a feed.
*/
var $syndicationURL, $image, $language, $copyright, $pubDate, $lastBuildDate, $editor, $editorEmail, $webmaster, $category, $docs, $ttl, $rating, $skipHours, $skipDays;
 
 
/**
* @access private
*/
var $items = Array();
 
 
/**
* This feed's MIME content type.
* @since 1.4
* @access private
*/
var $contentType = "text/xml";
 
 
/**
* Any additional elements to include as an assiciated array. All $key => $value pairs
* will be included unencoded in the feed in the form
* <$key>$value</$key>
* Again: No encoding will be used! This means you can invalidate or enhance the feed
* if $value contains markup. This may be abused to embed tags not implemented by
* the FeedCreator class used.
*/
var $additionalElements = Array();
 
# }}}
# {{{ addItem
/**
* Adds an FeedItem to the feed.
*
* @param object FeedItem $item The FeedItem to add to the feed.
* @access public
*/
function addItem($item) {
$this->items[] = $item;
}
# }}}
 
# {{{ iTrunc
/**
* Truncates a string to a certain length at the most sensible point.
* First, if there's a '.' character near the end of the string, the string is truncated after this character.
* If there is no '.', the string is truncated after the last ' ' character.
* If the string is truncated, " ..." is appended.
* If the string is already shorter than $length, it is returned unchanged.
*
* @static
* @param string string A string to be truncated.
* @param int length the maximum length the string should be truncated to
* @return string the truncated string
*/
function iTrunc($string, $length) {
if (strlen($string)<=$length) {
return $string;
}
$pos = strrpos($string,".");
if ($pos>=$length-4) {
$string = substr($string,0,$length-4);
$pos = strrpos($string,".");
}
if ($pos>=$length*0.4) {
return substr($string,0,$pos+1)." ...";
}
$pos = strrpos($string," ");
if ($pos>=$length-4) {
$string = substr($string,0,$length-4);
$pos = strrpos($string," ");
}
if ($pos>=$length*0.4) {
return substr($string,0,$pos)." ...";
}
return substr($string,0,$length-4)." ...";
}
# }}}
 
# {{{ _createGeneratorComment
/**
* Creates a comment indicating the generator of this feed.
* The format of this comment seems to be recognized by
* Syndic8.com.
*/
function _createGeneratorComment() {
return "<!-- generator=\"".FEEDCREATOR_VERSION."\" -->\n";
}
# }}}
 
# {{{ _createAdditionalElements
/**
* Creates a string containing all additional elements specified in
* $additionalElements.
* @param elements array an associative array containing key => value pairs
* @param indentString string a string that will be inserted before every generated line
* @return string the XML tags corresponding to $additionalElements
*/
function _createAdditionalElements($elements, $indentString="") {
$ae = "";
if (is_array($elements)) {
foreach($elements AS $key => $value) {
$ae.= $indentString."<$key>$value</$key>\n";
}
}
return $ae;
}
# }}}
 
# {{{ createFeed
/**
* Builds the feed's text.
* @abstract
* @return string the feed's complete text
*/
function createFeed() {
}
# }}}
 
# {{{ _generateFilename
/**
* Generate a filename for the feed cache file. The result will be $_SERVER["PHP_SELF"] with the extension changed to .xml.
* For example:
*
* echo $_SERVER["PHP_SELF"]."\n";
* echo FeedCreator::_generateFilename();
*
* would produce:
*
* /rss/latestnews.php
* latestnews.xml
*
* @return string the feed cache filename
* @since 1.4
* @access private
*/
function _generateFilename() {
$fileInfo = pathinfo($_SERVER["PHP_SELF"]);
return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".xml";
}
# }}}
 
# {{{ _redirect
/**
* @since 1.4
* @access private
*/
function _redirect($filename) {
// attention, heavily-commented-out-area
// maybe use this in addition to file time checking
//Header("Expires: ".date("r",time()+$this->_timeout));
/* no caching at all, doesn't seem to work as good:
Header("Cache-Control: no-cache");
Header("Pragma: no-cache");
*/
// HTTP redirect, some feed readers' simple HTTP implementations don't follow it
//Header("Location: ".$filename);
 
Header("Content-Type: ".$this->contentType."; filename=".basename($filename));
Header("Content-Disposition: inline; filename=".basename($filename));
readfile($filename, "r");
die();
}
# }}}
 
# {{{ useCached
/**
* Turns on caching and checks if there is a recent version of this feed in the cache.
* If there is, an HTTP redirect header is sent.
* To effectively use caching, you should create the FeedCreator object and call this method
* before anything else, especially before you do the time consuming task to build the feed
* (web fetching, for example).
* @since 1.4
* @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
* @param timeout int optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour)
*/
function useCached($filename="", $timeout=3600) {
$this->_timeout = $timeout;
if ($filename=="") {
$filename = $this->_generateFilename();
}
if (file_exists($filename) AND (time()-filemtime($filename) < $timeout)) {
$this->_redirect($filename);
}
}
# }}}
 
# {{{ saveFeed
/**
* Saves this feed as a file on the local disk. After the file is saved, a redirect
* header may be sent to redirect the user to the newly created file.
* @since 1.4
*
* @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
* @param redirect boolean optional send an HTTP redirect header or not. If true, the user will be automatically redirected to the created file.
*/
function saveFeed($filename="", $displayContents=true) {
if ($filename=="") {
$filename = $this->_generateFilename();
}
$feedFile = fopen($filename, "w+");
if ($feedFile) {
fputs($feedFile,$this->createFeed());
fclose($feedFile);
if ($displayContents) {
$this->_redirect($filename);
}
} else {
echo "<br /><b>Error creating feed file, please check write permissions.</b><br />";
}
}
# }}}
}
 
 
/**
* FeedDate is an internal class that stores a date for a feed or feed item.
* Usually, you won't need to use this.
*/
class FeedDate {
var $unix;
 
# {{{ __construct
/**
* Creates a new instance of FeedDate representing a given date.
* Accepts RFC 822, ISO 8601 date formats as well as unix time stamps.
* @param mixed $dateString optional the date this FeedDate will represent. If not specified, the current date and time is used.
*/
function FeedDate($dateString="") {
if ($dateString=="") $dateString = date("r");
if (is_integer($dateString)) {
$this->unix = $dateString;
return;
}
if (preg_match("~(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?(\\d{1,2})\\s+([a-zA-Z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+(.*)~",$dateString,$matches)) {
$months = Array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12);
$this->unix = mktime($matches[4],$matches[5],$matches[6],$months[$matches[2]],$matches[1],$matches[3]);
if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') {
$tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60;
} else {
if (strlen($matches[7])==1) {
$oneHour = 3600;
$ord = ord($matches[7]);
if ($ord < ord("M")) {
$tzOffset = (ord("A") - $ord - 1) * $oneHour;
} elseif ($ord >= ord("M") AND $matches[7]!="Z") {
$tzOffset = ($ord - ord("M")) * $oneHour;
} elseif ($matches[7]=="Z") {
$tzOffset = 0;
}
}
switch ($matches[7]) {
case "UT":
case "GMT": $tzOffset = 0;
}
}
$this->unix += $tzOffset;
return;
}
if (preg_match("~(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(.*)~",$dateString,$matches)) {
$this->unix = mktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]);
if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') {
$tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60;
} else {
if ($matches[7]=="Z") {
$tzOffset = 0;
}
}
$this->unix += $tzOffset;
return;
}
$this->unix = 0;
}
# }}}
 
# {{{ rfc822
/**
* Gets the date stored in this FeedDate as an RFC 822 date.
*
* @return a date in RFC 822 format
*/
function rfc822() {
return gmdate("r",$this->unix);
}
# }}}
 
# {{{ iso8601
/**
* Gets the date stored in this FeedDate as an ISO 8601 date.
*
* @return a date in ISO 8601 format
*/
function iso8601() {
$date = gmdate("Y-m-d\TH:i:sO",$this->unix);
$date = substr($date,0,22) . ':' . substr($date,-2);
return $date;
}
# }}}
 
# {{{ unix
/**
* Gets the date stored in this FeedDate as unix time stamp.
*
* @return a date as a unix time stamp
*/
function unix() {
return $this->unix;
}
# }}}
}
 
 
/**
* RSSCreator10 is a FeedCreator that implements RDF Site Summary (RSS) 1.0.
*
* @see http://www.purl.org/rss/1.0/
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class RSSCreator10 extends FeedCreator {
 
# {{{ createFeed
/**
* Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0.
* The feed will contain all items previously added in the same order.
* @return string the feed's complete text
*/
function createFeed() {
global $config;
$feed = "<?xml version=\"1.0\" encoding=\"".$config->outputEnc."\"?>\n";
$feed.= "<?xml-stylesheet href=\"http://www.w3.org/2000/08/w3c-synd/style.css\" type=\"text/css\"?>\n";
$feed.= $this->_createGeneratorComment();
$feed.= "<rdf:RDF\n";
$feed.= " xmlns=\"http://purl.org/rss/1.0/\"\n";
$feed.= " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n";
$feed.= " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n";
$feed.= " <channel rdf:about=\"".htmlspecialchars($this->syndicationURL)."\">\n";
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
$feed.= " <description>".htmlspecialchars($this->description)."</description>\n";
$feed.= " <link>".htmlspecialchars($this->link)."</link>\n";
if ($this->image!=null) {
$feed.= " <image rdf:resource=\"".$this->image->url."\" />\n";
}
$now = new FeedDate();
$feed.= " <dc:date>".htmlspecialchars($now->iso8601())."</dc:date>\n";
$feed.= " <items>\n";
$feed.= " <rdf:Seq>\n";
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <rdf:li rdf:resource=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
}
$feed.= " </rdf:Seq>\n";
$feed.= " </items>\n";
$feed.= " </channel>\n";
if ($this->image!=null) {
$feed.= " <image rdf:about=\"".$this->image->url."\">\n";
$feed.= " <title>".$this->image->title."</title>\n";
$feed.= " <link>".$this->image->link."</link>\n";
$feed.= " <url>".$this->image->url."</url>\n";
$feed.= " </image>\n";
}
//$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <item rdf:about=\"".htmlspecialchars($this->items[$i]->link)."\">\n";
//$feed.= " <dc:type>Posting</dc:type>\n";
$feed.= " <dc:format>text/html</dc:format>\n";
if ($this->items[$i]->date!=null) {
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= " <dc:date>".htmlspecialchars($itemDate->iso8601())."</dc:date>\n";
}
if ($this->items[$i]->source!="") {
$feed.= " <dc:source>".htmlspecialchars($this->items[$i]->source)."</dc:source>\n";
}
if ($this->items[$i]->author!="") {
$feed.= " <dc:creator>".htmlspecialchars($this->items[$i]->author)."</dc:creator>\n";
}
$feed.= " <title>".htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")))."</title>\n";
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
$feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n";
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
$feed.= " </item>\n";
}
$feed.= "</rdf:RDF>\n";
return $feed;
}
# }}}
}
 
 
 
/**
* RSSCreator091 is a FeedCreator that implements RSS 0.91 Spec, revision 3.
*
* @see http://my.netscape.com/publish/formats/rss-spec-0.91.html
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class RSSCreator091 extends FeedCreator {
 
/**
* Stores this RSS feed's version number.
* @access private
*/
var $RSSVersion;
 
# {{{ __construct
function RSSCreator091() {
$this->_setRSSVersion("0.91");
$this->contentType = "application/rss+xml";
}
# }}}
 
# {{{ _setRSSVersion
/**
* Sets this RSS feed's version number.
* @access private
*/
function _setRSSVersion($version) {
$this->RSSVersion = $version;
}
# }}}
 
# {{{ createFeed
/**
* Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0.
* The feed will contain all items previously added in the same order.
* @return string the feed's complete text
*/
function createFeed() {
global $config;
$feed = "<?xml version=\"1.0\" encoding=\"".$config->outputEnc."\"?>\n";
$feed.= $this->_createGeneratorComment();
$feed.= "<rss version=\"".$this->RSSVersion."\">\n";
$feed.= " <channel>\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n";
$feed.= " <description>".FeedCreator::iTrunc(htmlspecialchars($this->description),500)."</description>\n";
$feed.= " <link>".htmlspecialchars($this->link)."</link>\n";
$now = new FeedDate();
$feed.= " <lastBuildDate>".htmlspecialchars($now->rfc822())."</lastBuildDate>\n";
$feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n";
 
if ($this->image!=null) {
$feed.= " <image>\n";
$feed.= " <url>".$this->image->url."</url>\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->image->title),100)."</title>\n";
$feed.= " <link>".$this->image->link."</link>\n";
if ($this->image->width!="") {
$feed.= " <width>".$this->image->width."</width>\n";
}
if ($this->image->height!="") {
$feed.= " <height>".$this->image->height."</height>\n";
}
if ($this->image->description!="") {
$feed.= " <description>".htmlspecialchars($this->image->description)."</description>\n";
}
$feed.= " </image>\n";
}
if ($this->language!="") {
$feed.= " <language>".$this->language."</language>\n";
}
if ($this->copyright!="") {
$feed.= " <copyright>".FeedCreator::iTrunc(htmlspecialchars($this->copyright),100)."</copyright>\n";
}
if ($this->editor!="") {
$feed.= " <managingEditor>".FeedCreator::iTrunc(htmlspecialchars($this->editor),100)."</managingEditor>\n";
}
if ($this->webmaster!="") {
$feed.= " <webMaster>".FeedCreator::iTrunc(htmlspecialchars($this->webmaster),100)."</webMaster>\n";
}
if ($this->pubDate!="") {
$pubDate = new FeedDate($this->pubDate);
$feed.= " <pubDate>".htmlspecialchars($pubDate->rfc822())."</pubDate>\n";
}
if ($this->category!="") {
$feed.= " <category>".htmlspecialchars($this->category)."</category>\n";
}
if ($this->docs!="") {
$feed.= " <docs>".FeedCreator::iTrunc(htmlspecialchars($this->docs),500)."</docs>\n";
}
if ($this->ttl!="") {
$feed.= " <ttl>".htmlspecialchars($this->ttl)."</ttl>\n";
}
if ($this->rating!="") {
$feed.= " <rating>".FeedCreator::iTrunc(htmlspecialchars($this->rating),500)."</rating>\n";
}
if ($this->skipHours!="") {
$feed.= " <skipHours>".htmlspecialchars($this->skipHours)."</skipHours>\n";
}
if ($this->skipDays!="") {
$feed.= " <skipDays>".htmlspecialchars($this->skipDays)."</skipDays>\n";
}
$feed.= $this->_createAdditionalElements($this->additionalElements, " ");
 
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <item>\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n";
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
$feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n";
if ($this->items[$i]->author!="") {
$feed.= " <author>".htmlspecialchars($this->items[$i]->author)."</author>\n";
}
/*
// on hold
if ($this->items[$i]->source!="") {
$feed.= " <source>".htmlspecialchars($this->items[$i]->source)."</source>\n";
}
*/
if ($this->items[$i]->category!="") {
$feed.= " <category>".htmlspecialchars($this->items[$i]->category)."</category>\n";
}
if ($this->items[$i]->comments!="") {
$feed.= " <comments>".$this->items[$i]->comments."</comments>\n";
}
if ($this->items[$i]->date!="") {
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= " <pubDate>".htmlspecialchars($itemDate->rfc822())."</pubDate>\n";
}
if ($this->items[$i]->guid!="") {
$feed.= " <guid>".$this->items[$i]->guid."</guid>\n";
}
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
$feed.= " </item>\n";
}
$feed.= " </channel>\n";
$feed.= "</rss>\n";
return $feed;
}
# }}}
}
 
 
 
/**
* RSSCreator20 is a FeedCreator that implements RDF Site Summary (RSS) 2.0.
*
* @see http://backend.userland.com/rss
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class RSSCreator20 extends RSSCreator091 {
 
# {{{ __construct
function RSSCreator20() {
parent::_setRSSVersion("2.0");
}
# }}}
 
}
 
 
/**
* PIECreator01 is a FeedCreator that implements the emerging PIE specification,
* as in http://intertwingly.net/wiki/pie/Syntax.
*
* @deprecated
* @since 1.3
* @author Scott Reynen <scott@randomchaos.com> and Kai Blankenhorn <kaib@bitfolge.de>
*/
class PIECreator01 extends FeedCreator {
 
# {{{ createFeed
function createFeed() {
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$feed.= "<feed version=\"0.1\" xmlns=\"http://example.com/newformat#\">\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n";
$feed.= " <subtitle>".FeedCreator::iTrunc(htmlspecialchars($this->description),500)."</subtitle>\n";
$feed.= " <link>".$this->link."</link>\n";
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <entry>\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n";
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n";
$feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n";
$feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n";
$feed.= " <id>".$this->items[$i]->guid."</id>\n";
if ($this->items[$i]->author!="") {
$feed.= " <author>\n";
$feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
if ($this->items[$i]->authorEmail!="") {
$feed.= " <email>".$this->items[$i]->authorEmail."</email>\n";
}
$feed.=" </author>\n";
}
$feed.= " <content type=\"text/html\" xml:lang=\"en-us\">\n";
$feed.= " <div xmlns=\"http://www.w3.org/1999/xhtml\">".$this->items[$i]->description."</div>\n";
$feed.= " </content>\n";
$feed.= " </entry>\n";
}
$feed.= "</feed>\n";
return $feed;
}
# }}}
}
 
 
/**
* AtomCreator03 is a FeedCreator that implements the atom specification,
* as in http://www.intertwingly.net/wiki/pie/FrontPage.
* Please note that just by using AtomCreator03 you won't automatically
* produce valid atom files. For example, you have to specify either an editor
* for the feed or an author for every single feed item.
*
* Some elements have not been implemented yet. These are (incomplete list):
* author URL, item author's email and URL, item contents, alternate links,
* other link content types than text/html. Some of them may be created with
* AtomCreator03::additionalElements.
*
* @see FeedCreator#additionalElements
* @since 1.6
* @author Kai Blankenhorn <kaib@bitfolge.de>, Scott Reynen <scott@randomchaos.com>
*/
class AtomCreator03 extends FeedCreator {
 
# {{{ __construct
function AtomCreator03() {
$this->contentType = "application/atom+xml";
}
# }}}
 
# {{{ createFeed
function createFeed() {
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$feed.= $this->_createGeneratorComment();
$feed.= "<feed version=\"0.3\" xmlns=\"http://purl.org/atom/ns#\"";
if ($this->language!="") {
$feed.= " xml:lang:\"".$this->language."\"";
}
$feed.= ">\n";
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
$feed.= " <tagline>".htmlspecialchars($this->description)."</tagline>\n";
$feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->link)."\"/>\n";
$feed.= " <id>".$this->link."</id>\n";
$now = new FeedDate();
$feed.= " <modified>".htmlspecialchars($now->iso8601())."</modified>\n";
if ($this->editor!="") {
$feed.= " <author>\n";
$feed.= " <name>".$this->editor."</name>\n";
if ($this->editorEmail!="") {
$feed.= " <email>".$this->editorEmail."</email>\n";
}
$feed.= " </author>\n";
}
$feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n";
$feed.= $this->_createAdditionalElements($this->additionalElements, " ");
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <entry>\n";
$feed.= " <title>".htmlspecialchars(strip_tags($this->items[$i]->title))."</title>\n";
$feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
if ($this->items[$i]->date=="") {
$this->items[$i]->date = time();
}
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n";
$feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n";
$feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n";
$feed.= " <id>".$this->items[$i]->link."</id>\n";
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
if ($this->items[$i]->author!="") {
$feed.= " <author>\n";
$feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
$feed.= " </author>\n";
}
if ($this->items[$i]->description!="") {
$feed.= " <summary>".htmlspecialchars($this->items[$i]->description)."</summary>\n";
}
$feed.= " </entry>\n";
}
$feed.= "</feed>\n";
return $feed;
}
# }}}
}
 
 
/**
* MBOXCreator is a FeedCreator that implements the mbox format
* as described in http://www.qmail.org/man/man5/mbox.html
*
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class MBOXCreator extends FeedCreator {
 
# {{{ __construct
function MBOXCreator() {
$this->contentType = "text/plain";
}
# }}}
 
# {{{ qp_enc
function qp_enc($input = "", $line_max = 76) {
$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
$lines = preg_split("/(?:\r\n|\r|\n)/", $input);
$eol = "\r\n";
$escape = "=";
$output = "";
while( list(, $line) = each($lines) ) {
//$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
$linlen = strlen($line);
$newline = "";
for($i = 0; $i < $linlen; $i++) {
$c = substr($line, $i, 1);
$dec = ord($c);
if ( ($dec == 32) && ($i == ($linlen - 1)) ) { // convert space at eol only
$c = "=20";
} elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
$h2 = floor($dec/16); $h1 = floor($dec%16);
$c = $escape.$hex["$h2"].$hex["$h1"];
}
if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
$output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
$newline = "";
}
$newline .= $c;
} // end of for
$output .= $newline.$eol;
}
return trim($output);
}
# }}}
 
# {{{ createFeed
/**
* Builds the MBOX contents.
* @return string the feed's complete text
*/
function createFeed() {
global $config;
for ($i=0;$i<count($this->items);$i++) {
if ($this->items[$i]->author!="") {
$from = $this->items[$i]->author;
} else {
$from = $this->title;
}
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= "From ".strtr(MBOXCreator::qp_enc($from)," ","_")." ".date("D M d H:i:s Y",$itemDate->unix())."\n";
$feed.= "Content-Type: text/plain;\n";
$feed.= " charset=\"".$config->outputEnc."\"\n";
$feed.= "Content-Transfer-Encoding: quoted-printable\n";
$feed.= "Content-Type: text/plain\n";
$feed.= "From: \"".MBOXCreator::qp_enc($from)."\"\n";
$feed.= "Date: ".$itemDate->rfc822()."\n";
$feed.= "Subject: ".MBOXCreator::qp_enc(FeedCreator::iTrunc($this->items[$i]->title,100))."\n";
$feed.= "\n";
$body = chunk_split(MBOXCreator::qp_enc($this->items[$i]->description));
$feed.= preg_replace("~\nFrom ([^\n]*)(\n?)~","\n>From $1$2\n",$body);
$feed.= "\n";
$feed.= "\n";
}
return $feed;
}
# }}}
 
# {{{ _generateFilename
/**
* Generate a filename for the feed cache file. Overridden from FeedCreator to prevent XML data types.
* @return string the feed cache filename
* @since 1.4
* @access private
*/
function _generateFilename() {
$fileInfo = pathinfo($_SERVER["PHP_SELF"]);
return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".mbox";
}
# }}}
}
 
 
/**
* OPMLCreator is a FeedCreator that implements OPML 1.0.
*
* @see http://opml.scripting.com/spec
* @author Dirk Clemens, Kai Blankenhorn
* @since 1.5
*/
class OPMLCreator extends FeedCreator {
# {{{ createFeed
function createFeed() {
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$feed.= $this->_createGeneratorComment();
$feed.= "<opml xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
$feed.= " <head>\n";
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
if ($this->pubDate!="") {
$date = new FeedDate($this->pubDate);
$feed.= " <dateCreated>".$date->rfc822()."</dateCreated>\n";
}
if ($this->lastBuildDate!="") {
$date = new FeedDate($this->lastBuildDate);
$feed.= " <dateModified>".$date->rfc822()."</dateModified>\n";
}
if ($this->editor!="") {
$feed.= " <ownerName>".$this->editor."</ownerName>\n";
}
if ($this->editorEmail!="") {
$feed.= " <ownerEmail>".$this->editorEmail."</ownerEmail>\n";
}
$feed.= " </head>\n";
$feed.= " <body>\n";
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <outline type=\"rss\" ";
$title = htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")));
$feed.= " title=\"".$title."\"";
$feed.= " text=\"".$title."\"";
//$feed.= " description=\"".htmlspecialchars($this->items[$i]->description)."\"";
$feed.= " url=\"".htmlspecialchars($this->items[$i]->link)."\"";
$feed.= "/>\n";
}
$feed.= " </body>\n";
$feed.= "</opml>\n";
return $feed;
}
# }}}
}
 
?>
<?php
# vim:et:ts=4:sts=4:sw=4:fdm=marker:
# {{{ Info
/***************************************************************************
 
FeedCreator class v1.6
originally (c) Kai Blankenhorn
www.bitfolge.de
kaib@bitfolge.de
v1.3 work by Scott Reynen (scott@randomchaos.com) and Kai Blankenhorn
v1.5 OPML support by Dirk Clemens
 
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: <http://www.gnu.org/licenses/gpl.txt>
 
****************************************************************************
 
 
Changelog:
 
Modifications for WebSVN:
The main description link wasn't put through htmlspecialcharacters
Output encoding now defined by $config
Remove hardcoded time zone
 
v1.6 05-10-04
added stylesheet to RSS 1.0 feeds
fixed generator comment (thanks Kevin L. Papendick and Tanguy Pruvot)
fixed RFC822 date bug (thanks Tanguy Pruvot)
added TimeZone customization for RFC8601 (thanks Tanguy Pruvot)
fixed Content-type could be empty (thanks Tanguy Pruvot)
fixed author/creator in RSS1.0 (thanks Tanguy Pruvot)
 
 
v1.6 beta 02-28-04
added Atom 0.3 support (not all features, though)
improved OPML 1.0 support (hopefully - added more elements)
added support for arbitrary additional elements (use with caution)
code beautification :-)
considered beta due to some internal changes
 
v1.5.1 01-27-04
fixed some RSS 1.0 glitches (thanks to Stéphane Vanpoperynghe)
fixed some inconsistencies between documentation and code (thanks to Timothy Martin)
 
v1.5 01-06-04
added support for OPML 1.0
added more documentation
 
v1.4 11-11-03
optional feed saving and caching
improved documentation
minor improvements
 
v1.3 10-02-03
renamed to FeedCreator, as it not only creates RSS anymore
added support for mbox
tentative support for echo/necho/atom/pie/???
v1.2 07-20-03
intelligent auto-truncating of RSS 0.91 attributes
don't create some attributes when they're not set
documentation improved
fixed a real and a possible bug with date conversions
code cleanup
 
v1.1 06-29-03
added images to feeds
now includes most RSS 0.91 attributes
added RSS 2.0 feeds
 
v1.0 06-24-03
initial release
 
 
 
***************************************************************************/
 
/*** GENERAL USAGE *********************************************************
 
include("feedcreator.class.php");
 
$rss = new UniversalFeedCreator();
$rss->useCached(); // use cached version if age<1 hour
$rss->title = "PHP news";
$rss->description = "daily news from the PHP scripting world";
$rss->link = "http://www.dailyphp.net/news";
$rss->syndicationURL = "http://www.dailyphp.net/".$_SERVER["PHP_SELF"];
 
$image = new FeedImage();
$image->title = "dailyphp.net logo";
$image->url = "http://www.dailyphp.net/images/logo.gif";
$image->link = "http://www.dailyphp.net";
$image->description = "Feed provided by dailyphp.net. Click to visit.";
$rss->image = $image;
 
// get your news items from somewhere, e.g. your database:
mysql_select_db($dbHost, $dbUser, $dbPass);
$res = mysql_query("SELECT * FROM news ORDER BY newsdate DESC");
while ($data = mysql_fetch_object($res)) {
$item = new FeedItem();
$item->title = $data->title;
$item->link = $data->url;
$item->description = $data->short;
$item->date = $data->newsdate;
$item->source = "http://www.dailyphp.net";
$item->author = "John Doe";
$rss->addItem($item);
}
 
// valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1 (deprecated),
// MBOX, OPML, ATOM0.3
echo $rss->saveFeed("RSS1.0", "news/feed.xml");
 
# }}}
 
***************************************************************************
* A little setup *
**************************************************************************/
 
/**
* Version string.
**/
define("FEEDCREATOR_VERSION", "FeedCreator 1.6");
 
 
 
/**
* A FeedItem is a part of a FeedCreator feed.
*
* @author Kai Blankenhorn <kaib@bitfolge.de>
* @since 1.3
*/
class FeedItem {
# {{{ Properties
 
/**
* Mandatory attributes of an item.
*/
var $title, $description, $link;
/**
* Optional attributes of an item.
*/
var $author, $authorEmail, $image, $category, $comments, $guid, $source, $creator;
/**
* Publishing date of an item. May be in one of the following formats:
*
* RFC 822:
* "Mon, 20 Jan 03 18:05:41 +0400"
* "20 Jan 03 18:05:41 +0000"
*
* ISO 8601:
* "2003-01-20T18:05:41+04:00"
*
* Unix:
* 1043082341
*/
var $date;
/**
* Any additional elements to include as an assiciated array. All $key => $value pairs
* will be included unencoded in the feed item in the form
* <$key>$value</$key>
* Again: No encoding will be used! This means you can invalidate or enhance the feed
* if $value contains markup. This may be abused to embed tags not implemented by
* the FeedCreator class used.
*/
var $additionalElements = Array();
 
// on hold
// var $source;
 
# }}}
}
 
 
 
/**
* An FeedImage may be added to a FeedCreator feed.
* @author Kai Blankenhorn <kaib@bitfolge.de>
* @since 1.3
*/
class FeedImage {
# {{{ Properties
 
/**
* Mandatory attributes of an image.
*/
var $title, $url, $link;
/**
* Optional attributes of an image.
*/
var $width, $height, $description;
 
# }}}
}
 
 
/**
* UniversalFeedCreator lets you choose during runtime which
* format to build.
* For general usage of a feed class, see the FeedCreator class
* below or the example above.
*
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class UniversalFeedCreator extends FeedCreator {
var $_feed;
# {{{ _setFormat
function _setFormat($format) {
switch (strtoupper($format)) {
case "2.0":
// fall through
case "RSS2.0":
$this->_feed = new RSSCreator20();
break;
case "1.0":
// fall through
case "RSS1.0":
$this->_feed = new RSSCreator10();
break;
case "0.91":
// fall through
case "RSS0.91":
$this->_feed = new RSSCreator091();
break;
case "PIE0.1":
$this->_feed = new PIECreator01();
break;
case "MBOX":
$this->_feed = new MBOXCreator();
break;
case "OPML":
$this->_feed = new OPMLCreator();
break;
case "ATOM0.3":
$this->_feed = new AtomCreator03();
break;
default:
$this->_feed = new RSSCreator091();
break;
}
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if ($key!="feed") {
$this->_feed->{$key} = $this->{$key};
}
}
}
# }}}
# {{{ createFeed
/**
* Creates a syndication feed based on the items previously added.
*
* @see FeedCreator::addItem()
* @param string format format the feed should comply to. Valid values are:
* "PIE0.1", "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML".
* @return string the contents of the feed.
*/
function createFeed($format = "RSS0.91") {
$this->_setFormat($format);
return $this->_feed->createFeed();
}
# }}}
 
# {{{ saveFeed
/**
* Saves this feed as a file on the local disk. After the file is saved, an HTTP redirect
* header may be sent to redirect the use to the newly created file.
* @since 1.4
*
* @param string format format the feed should comply to. Valid values are:
* "PIE0.1" (deprecated), "mbox", "RSS0.91", "RSS1.0", "RSS2.0", "OPML", "ATOM0.3".
* @param string filename optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
* @param boolean displayContents optional send the content of the file or not. If true, the file will be sent in the body of the response.
*/
function saveFeed($format="RSS0.91", $filename="", $displayContents=true) {
$this->_setFormat($format);
$this->_feed->saveFeed($filename, $displayContents);
}
# }}}
 
}
 
 
/**
* FeedCreator is the abstract base implementation for concrete
* implementations that implement a specific format of syndication.
*
* @abstract
* @author Kai Blankenhorn <kaib@bitfolge.de>
* @since 1.4
*/
class FeedCreator {
# {{{ Properties
 
/**
* Mandatory attributes of a feed.
*/
var $title, $description, $link;
 
 
/**
* Optional attributes of a feed.
*/
var $syndicationURL, $image, $language, $copyright, $pubDate, $lastBuildDate, $editor, $editorEmail, $webmaster, $category, $docs, $ttl, $rating, $skipHours, $skipDays;
 
 
/**
* @access private
*/
var $items = Array();
 
 
/**
* This feed's MIME content type.
* @since 1.4
* @access private
*/
var $contentType = "text/xml";
 
 
/**
* Any additional elements to include as an assiciated array. All $key => $value pairs
* will be included unencoded in the feed in the form
* <$key>$value</$key>
* Again: No encoding will be used! This means you can invalidate or enhance the feed
* if $value contains markup. This may be abused to embed tags not implemented by
* the FeedCreator class used.
*/
var $additionalElements = Array();
 
# }}}
# {{{ addItem
/**
* Adds an FeedItem to the feed.
*
* @param object FeedItem $item The FeedItem to add to the feed.
* @access public
*/
function addItem($item) {
$this->items[] = $item;
}
# }}}
 
# {{{ iTrunc
/**
* Truncates a string to a certain length at the most sensible point.
* First, if there's a '.' character near the end of the string, the string is truncated after this character.
* If there is no '.', the string is truncated after the last ' ' character.
* If the string is truncated, " ..." is appended.
* If the string is already shorter than $length, it is returned unchanged.
*
* @static
* @param string string A string to be truncated.
* @param int length the maximum length the string should be truncated to
* @return string the truncated string
*/
function iTrunc($string, $length) {
if (strlen($string)<=$length) {
return $string;
}
$pos = strrpos($string,".");
if ($pos>=$length-4) {
$string = substr($string,0,$length-4);
$pos = strrpos($string,".");
}
if ($pos>=$length*0.4) {
return substr($string,0,$pos+1)." ...";
}
$pos = strrpos($string," ");
if ($pos>=$length-4) {
$string = substr($string,0,$length-4);
$pos = strrpos($string," ");
}
if ($pos>=$length*0.4) {
return substr($string,0,$pos)." ...";
}
return substr($string,0,$length-4)." ...";
}
# }}}
 
# {{{ _createGeneratorComment
/**
* Creates a comment indicating the generator of this feed.
* The format of this comment seems to be recognized by
* Syndic8.com.
*/
function _createGeneratorComment() {
return "<!-- generator=\"".FEEDCREATOR_VERSION."\" -->\n";
}
# }}}
 
# {{{ _createAdditionalElements
/**
* Creates a string containing all additional elements specified in
* $additionalElements.
* @param elements array an associative array containing key => value pairs
* @param indentString string a string that will be inserted before every generated line
* @return string the XML tags corresponding to $additionalElements
*/
function _createAdditionalElements($elements, $indentString="") {
$ae = "";
if (is_array($elements)) {
foreach($elements AS $key => $value) {
$ae.= $indentString."<$key>$value</$key>\n";
}
}
return $ae;
}
# }}}
 
# {{{ createFeed
/**
* Builds the feed's text.
* @abstract
* @return string the feed's complete text
*/
function createFeed() {
}
# }}}
 
# {{{ _generateFilename
/**
* Generate a filename for the feed cache file. The result will be $_SERVER["PHP_SELF"] with the extension changed to .xml.
* For example:
*
* echo $_SERVER["PHP_SELF"]."\n";
* echo FeedCreator::_generateFilename();
*
* would produce:
*
* /rss/latestnews.php
* latestnews.xml
*
* @return string the feed cache filename
* @since 1.4
* @access private
*/
function _generateFilename() {
$fileInfo = pathinfo($_SERVER["PHP_SELF"]);
return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".xml";
}
# }}}
 
# {{{ _redirect
/**
* @since 1.4
* @access private
*/
function _redirect($filename) {
// attention, heavily-commented-out-area
// maybe use this in addition to file time checking
//Header("Expires: ".date("r",time()+$this->_timeout));
/* no caching at all, doesn't seem to work as good:
Header("Cache-Control: no-cache");
Header("Pragma: no-cache");
*/
// HTTP redirect, some feed readers' simple HTTP implementations don't follow it
//Header("Location: ".$filename);
 
Header("Content-Type: ".$this->contentType."; filename=".basename($filename));
Header("Content-Disposition: inline; filename=".basename($filename));
readfile($filename, "r");
die();
}
# }}}
 
# {{{ useCached
/**
* Turns on caching and checks if there is a recent version of this feed in the cache.
* If there is, an HTTP redirect header is sent.
* To effectively use caching, you should create the FeedCreator object and call this method
* before anything else, especially before you do the time consuming task to build the feed
* (web fetching, for example).
* @since 1.4
* @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
* @param timeout int optional the timeout in seconds before a cached version is refreshed (defaults to 3600 = 1 hour)
*/
function useCached($filename="", $timeout=3600) {
$this->_timeout = $timeout;
if ($filename=="") {
$filename = $this->_generateFilename();
}
if (file_exists($filename) AND (time()-filemtime($filename) < $timeout)) {
$this->_redirect($filename);
}
}
# }}}
 
# {{{ saveFeed
/**
* Saves this feed as a file on the local disk. After the file is saved, a redirect
* header may be sent to redirect the user to the newly created file.
* @since 1.4
*
* @param filename string optional the filename where a recent version of the feed is saved. If not specified, the filename is $_SERVER["PHP_SELF"] with the extension changed to .xml (see _generateFilename()).
* @param redirect boolean optional send an HTTP redirect header or not. If true, the user will be automatically redirected to the created file.
*/
function saveFeed($filename="", $displayContents=true) {
if ($filename=="") {
$filename = $this->_generateFilename();
}
$feedFile = fopen($filename, "w+");
if ($feedFile) {
fputs($feedFile,$this->createFeed());
fclose($feedFile);
if ($displayContents) {
$this->_redirect($filename);
}
} else {
echo "<br /><b>Error creating feed file, please check write permissions.</b><br />";
}
}
# }}}
}
 
 
/**
* FeedDate is an internal class that stores a date for a feed or feed item.
* Usually, you won't need to use this.
*/
class FeedDate {
var $unix;
 
# {{{ __construct
/**
* Creates a new instance of FeedDate representing a given date.
* Accepts RFC 822, ISO 8601 date formats as well as unix time stamps.
* @param mixed $dateString optional the date this FeedDate will represent. If not specified, the current date and time is used.
*/
function FeedDate($dateString="") {
if ($dateString=="") $dateString = date("r");
if (is_integer($dateString)) {
$this->unix = $dateString;
return;
}
if (preg_match("~(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?(\\d{1,2})\\s+([a-zA-Z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+(.*)~",$dateString,$matches)) {
$months = Array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,"Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12);
$this->unix = mktime($matches[4],$matches[5],$matches[6],$months[$matches[2]],$matches[1],$matches[3]);
if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') {
$tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60;
} else {
if (strlen($matches[7])==1) {
$oneHour = 3600;
$ord = ord($matches[7]);
if ($ord < ord("M")) {
$tzOffset = (ord("A") - $ord - 1) * $oneHour;
} elseif ($ord >= ord("M") AND $matches[7]!="Z") {
$tzOffset = ($ord - ord("M")) * $oneHour;
} elseif ($matches[7]=="Z") {
$tzOffset = 0;
}
}
switch ($matches[7]) {
case "UT":
case "GMT": $tzOffset = 0;
}
}
$this->unix += $tzOffset;
return;
}
if (preg_match("~(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(.*)~",$dateString,$matches)) {
$this->unix = mktime($matches[4],$matches[5],$matches[6],$matches[2],$matches[3],$matches[1]);
if (substr($matches[7],0,1)=='+' OR substr($matches[7],0,1)=='-') {
$tzOffset = (substr($matches[7],0,3) * 60 + substr($matches[7],-2)) * 60;
} else {
if ($matches[7]=="Z") {
$tzOffset = 0;
}
}
$this->unix += $tzOffset;
return;
}
$this->unix = 0;
}
# }}}
 
# {{{ rfc822
/**
* Gets the date stored in this FeedDate as an RFC 822 date.
*
* @return a date in RFC 822 format
*/
function rfc822() {
return gmdate("r",$this->unix);
}
# }}}
 
# {{{ iso8601
/**
* Gets the date stored in this FeedDate as an ISO 8601 date.
*
* @return a date in ISO 8601 format
*/
function iso8601() {
$date = gmdate("Y-m-d\TH:i:sO",$this->unix);
$date = substr($date,0,22) . ':' . substr($date,-2);
return $date;
}
# }}}
 
# {{{ unix
/**
* Gets the date stored in this FeedDate as unix time stamp.
*
* @return a date as a unix time stamp
*/
function unix() {
return $this->unix;
}
# }}}
}
 
 
/**
* RSSCreator10 is a FeedCreator that implements RDF Site Summary (RSS) 1.0.
*
* @see http://www.purl.org/rss/1.0/
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class RSSCreator10 extends FeedCreator {
 
# {{{ createFeed
/**
* Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0.
* The feed will contain all items previously added in the same order.
* @return string the feed's complete text
*/
function createFeed() {
global $config;
$feed = "<?xml version=\"1.0\" encoding=\"".$config->outputEnc."\"?>\n";
$feed.= "<?xml-stylesheet href=\"http://www.w3.org/2000/08/w3c-synd/style.css\" type=\"text/css\"?>\n";
$feed.= $this->_createGeneratorComment();
$feed.= "<rdf:RDF\n";
$feed.= " xmlns=\"http://purl.org/rss/1.0/\"\n";
$feed.= " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n";
$feed.= " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n";
$feed.= " <channel rdf:about=\"".htmlspecialchars($this->syndicationURL)."\">\n";
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
$feed.= " <description>".htmlspecialchars($this->description)."</description>\n";
$feed.= " <link>".htmlspecialchars($this->link)."</link>\n";
if ($this->image!=null) {
$feed.= " <image rdf:resource=\"".$this->image->url."\" />\n";
}
$now = new FeedDate();
$feed.= " <dc:date>".htmlspecialchars($now->iso8601())."</dc:date>\n";
$feed.= " <items>\n";
$feed.= " <rdf:Seq>\n";
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <rdf:li rdf:resource=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
}
$feed.= " </rdf:Seq>\n";
$feed.= " </items>\n";
$feed.= " </channel>\n";
if ($this->image!=null) {
$feed.= " <image rdf:about=\"".$this->image->url."\">\n";
$feed.= " <title>".$this->image->title."</title>\n";
$feed.= " <link>".$this->image->link."</link>\n";
$feed.= " <url>".$this->image->url."</url>\n";
$feed.= " </image>\n";
}
//$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <item rdf:about=\"".htmlspecialchars($this->items[$i]->link)."\">\n";
//$feed.= " <dc:type>Posting</dc:type>\n";
$feed.= " <dc:format>text/html</dc:format>\n";
if ($this->items[$i]->date!=null) {
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= " <dc:date>".htmlspecialchars($itemDate->iso8601())."</dc:date>\n";
}
if ($this->items[$i]->source!="") {
$feed.= " <dc:source>".htmlspecialchars($this->items[$i]->source)."</dc:source>\n";
}
if ($this->items[$i]->author!="") {
$feed.= " <dc:creator>".htmlspecialchars($this->items[$i]->author)."</dc:creator>\n";
}
$feed.= " <title>".htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")))."</title>\n";
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
$feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n";
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
$feed.= " </item>\n";
}
$feed.= "</rdf:RDF>\n";
return $feed;
}
# }}}
}
 
 
 
/**
* RSSCreator091 is a FeedCreator that implements RSS 0.91 Spec, revision 3.
*
* @see http://my.netscape.com/publish/formats/rss-spec-0.91.html
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class RSSCreator091 extends FeedCreator {
 
/**
* Stores this RSS feed's version number.
* @access private
*/
var $RSSVersion;
 
# {{{ __construct
function RSSCreator091() {
$this->_setRSSVersion("0.91");
$this->contentType = "application/rss+xml";
}
# }}}
 
# {{{ _setRSSVersion
/**
* Sets this RSS feed's version number.
* @access private
*/
function _setRSSVersion($version) {
$this->RSSVersion = $version;
}
# }}}
 
# {{{ createFeed
/**
* Builds the RSS feed's text. The feed will be compliant to RDF Site Summary (RSS) 1.0.
* The feed will contain all items previously added in the same order.
* @return string the feed's complete text
*/
function createFeed() {
global $config;
$feed = "<?xml version=\"1.0\" encoding=\"".$config->outputEnc."\"?>\n";
$feed.= $this->_createGeneratorComment();
$feed.= "<rss version=\"".$this->RSSVersion."\">\n";
$feed.= " <channel>\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n";
$feed.= " <description>".FeedCreator::iTrunc(htmlspecialchars($this->description),500)."</description>\n";
$feed.= " <link>".htmlspecialchars($this->link)."</link>\n";
$now = new FeedDate();
$feed.= " <lastBuildDate>".htmlspecialchars($now->rfc822())."</lastBuildDate>\n";
$feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n";
 
if ($this->image!=null) {
$feed.= " <image>\n";
$feed.= " <url>".$this->image->url."</url>\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->image->title),100)."</title>\n";
$feed.= " <link>".$this->image->link."</link>\n";
if ($this->image->width!="") {
$feed.= " <width>".$this->image->width."</width>\n";
}
if ($this->image->height!="") {
$feed.= " <height>".$this->image->height."</height>\n";
}
if ($this->image->description!="") {
$feed.= " <description>".htmlspecialchars($this->image->description)."</description>\n";
}
$feed.= " </image>\n";
}
if ($this->language!="") {
$feed.= " <language>".$this->language."</language>\n";
}
if ($this->copyright!="") {
$feed.= " <copyright>".FeedCreator::iTrunc(htmlspecialchars($this->copyright),100)."</copyright>\n";
}
if ($this->editor!="") {
$feed.= " <managingEditor>".FeedCreator::iTrunc(htmlspecialchars($this->editor),100)."</managingEditor>\n";
}
if ($this->webmaster!="") {
$feed.= " <webMaster>".FeedCreator::iTrunc(htmlspecialchars($this->webmaster),100)."</webMaster>\n";
}
if ($this->pubDate!="") {
$pubDate = new FeedDate($this->pubDate);
$feed.= " <pubDate>".htmlspecialchars($pubDate->rfc822())."</pubDate>\n";
}
if ($this->category!="") {
$feed.= " <category>".htmlspecialchars($this->category)."</category>\n";
}
if ($this->docs!="") {
$feed.= " <docs>".FeedCreator::iTrunc(htmlspecialchars($this->docs),500)."</docs>\n";
}
if ($this->ttl!="") {
$feed.= " <ttl>".htmlspecialchars($this->ttl)."</ttl>\n";
}
if ($this->rating!="") {
$feed.= " <rating>".FeedCreator::iTrunc(htmlspecialchars($this->rating),500)."</rating>\n";
}
if ($this->skipHours!="") {
$feed.= " <skipHours>".htmlspecialchars($this->skipHours)."</skipHours>\n";
}
if ($this->skipDays!="") {
$feed.= " <skipDays>".htmlspecialchars($this->skipDays)."</skipDays>\n";
}
$feed.= $this->_createAdditionalElements($this->additionalElements, " ");
 
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <item>\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n";
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
$feed.= " <description>".htmlspecialchars($this->items[$i]->description)."</description>\n";
if ($this->items[$i]->author!="") {
$feed.= " <author>".htmlspecialchars($this->items[$i]->author)."</author>\n";
}
/*
// on hold
if ($this->items[$i]->source!="") {
$feed.= " <source>".htmlspecialchars($this->items[$i]->source)."</source>\n";
}
*/
if ($this->items[$i]->category!="") {
$feed.= " <category>".htmlspecialchars($this->items[$i]->category)."</category>\n";
}
if ($this->items[$i]->comments!="") {
$feed.= " <comments>".$this->items[$i]->comments."</comments>\n";
}
if ($this->items[$i]->date!="") {
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= " <pubDate>".htmlspecialchars($itemDate->rfc822())."</pubDate>\n";
}
if ($this->items[$i]->guid!="") {
$feed.= " <guid>".$this->items[$i]->guid."</guid>\n";
}
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
$feed.= " </item>\n";
}
$feed.= " </channel>\n";
$feed.= "</rss>\n";
return $feed;
}
# }}}
}
 
 
 
/**
* RSSCreator20 is a FeedCreator that implements RDF Site Summary (RSS) 2.0.
*
* @see http://backend.userland.com/rss
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class RSSCreator20 extends RSSCreator091 {
 
# {{{ __construct
function RSSCreator20() {
parent::_setRSSVersion("2.0");
}
# }}}
 
}
 
 
/**
* PIECreator01 is a FeedCreator that implements the emerging PIE specification,
* as in http://intertwingly.net/wiki/pie/Syntax.
*
* @deprecated
* @since 1.3
* @author Scott Reynen <scott@randomchaos.com> and Kai Blankenhorn <kaib@bitfolge.de>
*/
class PIECreator01 extends FeedCreator {
 
# {{{ createFeed
function createFeed() {
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$feed.= "<feed version=\"0.1\" xmlns=\"http://example.com/newformat#\">\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars($this->title),100)."</title>\n";
$feed.= " <subtitle>".FeedCreator::iTrunc(htmlspecialchars($this->description),500)."</subtitle>\n";
$feed.= " <link>".$this->link."</link>\n";
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <entry>\n";
$feed.= " <title>".FeedCreator::iTrunc(htmlspecialchars(strip_tags($this->items[$i]->title)),100)."</title>\n";
$feed.= " <link>".htmlspecialchars($this->items[$i]->link)."</link>\n";
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n";
$feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n";
$feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n";
$feed.= " <id>".$this->items[$i]->guid."</id>\n";
if ($this->items[$i]->author!="") {
$feed.= " <author>\n";
$feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
if ($this->items[$i]->authorEmail!="") {
$feed.= " <email>".$this->items[$i]->authorEmail."</email>\n";
}
$feed.=" </author>\n";
}
$feed.= " <content type=\"text/html\" xml:lang=\"en-us\">\n";
$feed.= " <div xmlns=\"http://www.w3.org/1999/xhtml\">".$this->items[$i]->description."</div>\n";
$feed.= " </content>\n";
$feed.= " </entry>\n";
}
$feed.= "</feed>\n";
return $feed;
}
# }}}
}
 
 
/**
* AtomCreator03 is a FeedCreator that implements the atom specification,
* as in http://www.intertwingly.net/wiki/pie/FrontPage.
* Please note that just by using AtomCreator03 you won't automatically
* produce valid atom files. For example, you have to specify either an editor
* for the feed or an author for every single feed item.
*
* Some elements have not been implemented yet. These are (incomplete list):
* author URL, item author's email and URL, item contents, alternate links,
* other link content types than text/html. Some of them may be created with
* AtomCreator03::additionalElements.
*
* @see FeedCreator#additionalElements
* @since 1.6
* @author Kai Blankenhorn <kaib@bitfolge.de>, Scott Reynen <scott@randomchaos.com>
*/
class AtomCreator03 extends FeedCreator {
 
# {{{ __construct
function AtomCreator03() {
$this->contentType = "application/atom+xml";
}
# }}}
 
# {{{ createFeed
function createFeed() {
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$feed.= $this->_createGeneratorComment();
$feed.= "<feed version=\"0.3\" xmlns=\"http://purl.org/atom/ns#\"";
if ($this->language!="") {
$feed.= " xml:lang:\"".$this->language."\"";
}
$feed.= ">\n";
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
$feed.= " <tagline>".htmlspecialchars($this->description)."</tagline>\n";
$feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->link)."\"/>\n";
$feed.= " <id>".$this->link."</id>\n";
$now = new FeedDate();
$feed.= " <modified>".htmlspecialchars($now->iso8601())."</modified>\n";
if ($this->editor!="") {
$feed.= " <author>\n";
$feed.= " <name>".$this->editor."</name>\n";
if ($this->editorEmail!="") {
$feed.= " <email>".$this->editorEmail."</email>\n";
}
$feed.= " </author>\n";
}
$feed.= " <generator>".FEEDCREATOR_VERSION."</generator>\n";
$feed.= $this->_createAdditionalElements($this->additionalElements, " ");
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <entry>\n";
$feed.= " <title>".htmlspecialchars(strip_tags($this->items[$i]->title))."</title>\n";
$feed.= " <link rel=\"alternate\" type=\"text/html\" href=\"".htmlspecialchars($this->items[$i]->link)."\"/>\n";
if ($this->items[$i]->date=="") {
$this->items[$i]->date = time();
}
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= " <created>".htmlspecialchars($itemDate->iso8601())."</created>\n";
$feed.= " <issued>".htmlspecialchars($itemDate->iso8601())."</issued>\n";
$feed.= " <modified>".htmlspecialchars($itemDate->iso8601())."</modified>\n";
$feed.= " <id>".$this->items[$i]->link."</id>\n";
$feed.= $this->_createAdditionalElements($this->items[$i]->additionalElements, " ");
if ($this->items[$i]->author!="") {
$feed.= " <author>\n";
$feed.= " <name>".htmlspecialchars($this->items[$i]->author)."</name>\n";
$feed.= " </author>\n";
}
if ($this->items[$i]->description!="") {
$feed.= " <summary>".htmlspecialchars($this->items[$i]->description)."</summary>\n";
}
$feed.= " </entry>\n";
}
$feed.= "</feed>\n";
return $feed;
}
# }}}
}
 
 
/**
* MBOXCreator is a FeedCreator that implements the mbox format
* as described in http://www.qmail.org/man/man5/mbox.html
*
* @since 1.3
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class MBOXCreator extends FeedCreator {
 
# {{{ __construct
function MBOXCreator() {
$this->contentType = "text/plain";
}
# }}}
 
# {{{ qp_enc
function qp_enc($input = "", $line_max = 76) {
$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
$lines = preg_split("/(?:\r\n|\r|\n)/", $input);
$eol = "\r\n";
$escape = "=";
$output = "";
while( list(, $line) = each($lines) ) {
//$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
$linlen = strlen($line);
$newline = "";
for($i = 0; $i < $linlen; $i++) {
$c = substr($line, $i, 1);
$dec = ord($c);
if ( ($dec == 32) && ($i == ($linlen - 1)) ) { // convert space at eol only
$c = "=20";
} elseif ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) { // always encode "\t", which is *not* required
$h2 = floor($dec/16); $h1 = floor($dec%16);
$c = $escape.$hex["$h2"].$hex["$h1"];
}
if ( (strlen($newline) + strlen($c)) >= $line_max ) { // CRLF is not counted
$output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
$newline = "";
}
$newline .= $c;
} // end of for
$output .= $newline.$eol;
}
return trim($output);
}
# }}}
 
# {{{ createFeed
/**
* Builds the MBOX contents.
* @return string the feed's complete text
*/
function createFeed() {
global $config;
for ($i=0;$i<count($this->items);$i++) {
if ($this->items[$i]->author!="") {
$from = $this->items[$i]->author;
} else {
$from = $this->title;
}
$itemDate = new FeedDate($this->items[$i]->date);
$feed.= "From ".strtr(MBOXCreator::qp_enc($from)," ","_")." ".date("D M d H:i:s Y",$itemDate->unix())."\n";
$feed.= "Content-Type: text/plain;\n";
$feed.= " charset=\"".$config->outputEnc."\"\n";
$feed.= "Content-Transfer-Encoding: quoted-printable\n";
$feed.= "Content-Type: text/plain\n";
$feed.= "From: \"".MBOXCreator::qp_enc($from)."\"\n";
$feed.= "Date: ".$itemDate->rfc822()."\n";
$feed.= "Subject: ".MBOXCreator::qp_enc(FeedCreator::iTrunc($this->items[$i]->title,100))."\n";
$feed.= "\n";
$body = chunk_split(MBOXCreator::qp_enc($this->items[$i]->description));
$feed.= preg_replace("~\nFrom ([^\n]*)(\n?)~","\n>From $1$2\n",$body);
$feed.= "\n";
$feed.= "\n";
}
return $feed;
}
# }}}
 
# {{{ _generateFilename
/**
* Generate a filename for the feed cache file. Overridden from FeedCreator to prevent XML data types.
* @return string the feed cache filename
* @since 1.4
* @access private
*/
function _generateFilename() {
$fileInfo = pathinfo($_SERVER["PHP_SELF"]);
return substr($fileInfo["basename"],0,-(strlen($fileInfo["extension"])+1)).".mbox";
}
# }}}
}
 
 
/**
* OPMLCreator is a FeedCreator that implements OPML 1.0.
*
* @see http://opml.scripting.com/spec
* @author Dirk Clemens, Kai Blankenhorn
* @since 1.5
*/
class OPMLCreator extends FeedCreator {
# {{{ createFeed
function createFeed() {
$feed = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$feed.= $this->_createGeneratorComment();
$feed.= "<opml xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
$feed.= " <head>\n";
$feed.= " <title>".htmlspecialchars($this->title)."</title>\n";
if ($this->pubDate!="") {
$date = new FeedDate($this->pubDate);
$feed.= " <dateCreated>".$date->rfc822()."</dateCreated>\n";
}
if ($this->lastBuildDate!="") {
$date = new FeedDate($this->lastBuildDate);
$feed.= " <dateModified>".$date->rfc822()."</dateModified>\n";
}
if ($this->editor!="") {
$feed.= " <ownerName>".$this->editor."</ownerName>\n";
}
if ($this->editorEmail!="") {
$feed.= " <ownerEmail>".$this->editorEmail."</ownerEmail>\n";
}
$feed.= " </head>\n";
$feed.= " <body>\n";
for ($i=0;$i<count($this->items);$i++) {
$feed.= " <outline type=\"rss\" ";
$title = htmlspecialchars(strip_tags(strtr($this->items[$i]->title,"\n\r"," ")));
$feed.= " title=\"".$title."\"";
$feed.= " text=\"".$title."\"";
//$feed.= " description=\"".htmlspecialchars($this->items[$i]->description)."\"";
$feed.= " url=\"".htmlspecialchars($this->items[$i]->link)."\"";
$feed.= "/>\n";
}
$feed.= " </body>\n";
$feed.= "</opml>\n";
return $feed;
}
# }}}
}
 
?>
/WebSVN/include/php5compat.inc
2,12 → 2,6
 
if (version_compare(phpversion(), '5.0.0', 'lt')) {
 
# XXX: these includes shouldn't be necessary!
require_once 'include/configclass.inc';
$config = new Config;
require_once 'include/config.inc';
require_once($config->getPHPCompatFile());
 
// Configure necessary functions here
$funcs = array(
'stripos',
16,6 → 10,13
 
// End configuration
 
// JB: PHP_Compat expects to be able to find functions by including
// 'PHP/Compat/Functions/functionname.php', but that hardcoded path
// isn't relative to the standard include path '.' (it's instead relative
// to './include'). So rather than hack PHP_Compat to work, just add
// './include' to our include path.
ini_set("include_path", ini_get("include_path").$config->pathSeparator."./include");
 
foreach ($funcs as $fn) {
if (PHP_Compat::loadFunction($fn) != true)
error_log('Could not load function `'.$fn.'\' as required by PHP Compat.');
/WebSVN/include/setup.inc
1,430 → 1,454
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// setup.inc
//
// Global setup
 
// --- DON'T CHANGE THIS FILE ---
//
// User changes should be done in config.ini
 
// Include the configuration class
require_once 'include/configclass.inc';
require_once 'include/svnlook.inc';
 
// Define the language array
$lang = array();
$langNames = array();
 
// Include a default language file.
require 'languages/english.inc';
 
// Set up locwebsvnhttp
// Note: we will use nothing in MultiViews mode so that the URLs use the root
// directory by default.
if (empty($locwebsvnhttp))
$locwebsvnhttp = defined('WSVN_MULTIVIEWS') ? '' : '.';
 
if (empty($locwebsvnreal))
$locwebsvnreal = '.';
 
$vars['locwebsvnhttp'] = $locwebsvnhttp;
 
// Make sure that the input locale is set up correctly
setlocale(LC_ALL, '');
 
// Create the config
$config = new Config;
 
// Set up the default character encodings
if (function_exists('iconv_get_encoding'))
{
$config->setInputEncoding(iconv_get_encoding('input_encoding'));
}
 
// {{{ Content-Type's
// Set up the default content-type extension handling
 
$contentType = array (
 
'.dwg' => 'application/acad', // AutoCAD Drawing files
'.DWG' => 'application/acad', // AutoCAD Drawing files
'.arj' => 'application/arj', //  
'.ccAD' => 'application/clariscad', // ClarisCAD files
'.DRW' => 'application/drafting', // MATRA Prelude drafting
'.dxf' => 'application/dxf', // DXF (AutoCAD)
'.DXF' => 'application/dxf', // DXF (AutoCAD)
'.xl' => 'application/excel', // Microsoft Excel
'.unv' => 'application/i-deas', //SDRC I-DEAS files
'.UNV' => 'application/i-deas', //SDRC I-DEAS files
'.igs' => 'application/iges', // IGES graphics format
'.iges' => 'application/iges', // IGES graphics format
'.IGS' => 'application/iges', // IGES graphics format
'.IGES' => 'application/iges', // IGES graphics format
'.hqx' => 'application/mac-binhex40', // Macintosh BinHex format
'.word' => 'application/msword', // Microsoft Word
'.w6w' => 'application/msword', // Microsoft Word
'.doc' => 'application/msword', // Microsoft Word
'.wri' => 'application/mswrite', // Microsoft Write
'.bin' => 'application/octet-stream', // Uninterpreted binary
'.exe' => 'application/x-msdownload', // Windows EXE
'.EXE' => 'application/x-msdownload', // Windows EXE
'.oda' => 'application/oda', //  
'.pdf' => 'application/pdf', // PDF (Adobe Acrobat)
'.ai' => 'application/postscript', // PostScript
'.PS' => 'application/postscript', // PostScript
'.ps' => 'application/postscript', // PostScript
'.eps' => 'application/postscript', // PostScript
'.prt' => 'application/pro_eng', // PTC Pro/ENGINEER
'.PRT' => 'application/pro_eng', // PTC Pro/ENGINEER
'.part' => 'application/pro_eng', // PTC Pro/ENGINEER
'.rtf' => 'application/rtf', // Rich Text Format
'.set' => 'application/set', // SET (French CAD standard)
'.SET' => 'application/set', // SET (French CAD standard)
'.stl' => 'application/sla', // Stereolithography
'.STL' => 'application/sla', // Stereolithography
'.SOL' => 'application/solids', // MATRA Prelude Solids
'.stp' => 'application/STEP', // ISO-10303 STEP data files
'.STP' => 'application/STEP', // ISO-10303 STEP data files
'.step' => 'application/STEP', // ISO-10303 STEP data files
'.STEP' => 'application/STEP', // ISO-10303 STEP data files
'.vda' => 'application/vda', // VDA-FS Surface data
'.VDA' => 'application/vda', // VDA-FS Surface data
'.dir' => 'application/x-director', // Macromedia Director
'.dcr' => 'application/x-director', // Macromedia Director
'.dxr' => 'application/x-director', // Macromedia Director
'.mif' => 'application/x-mif', // FrameMaker MIF Format
'.csh' => 'application/x-csh', // C-shell script
'.dvi' => 'application/x-dvi', // TeX DVI
'.gz' => 'application/x-gzip', // GNU Zip
'.gzip' => 'application/x-gzip', // GNU Zip
'.hdf' => 'application/x-hdf', // ncSA HDF Data File
'.latex' => 'application/x-latex', // LaTeX source
'.nc' => 'application/x-netcdf', // Unidata netCDF
'.cdf' => 'application/x-netcdf', // Unidata netCDF
'.sit' => 'application/x-stuffit', // Stiffut Archive
'.tcl' => 'application/x-tcl', // TCL script
'.texinfo' => 'application/x-texinfo', // Texinfo (Emacs)
'.texi' => 'application/x-texinfo', // Texinfo (Emacs)
'.t' => 'application/x-troff', // Troff
'.tr' => 'application/x-troff', // Troff
'.roff' => 'application/x-troff', // Troff
'.man' => 'application/x-troff-man', // Troff with MAN macros
'.me' => 'application/x-troff-me', // Troff with ME macros
'.ms' => 'application/x-troff-ms', // Troff with MS macros
'.src' => 'application/x-wais-source', // WAIS source
'.bcpio' => 'application/x-bcpio', // Old binary CPIO
'.cpio' => 'application/x-cpio', // POSIX CPIO
'.gtar' => 'application/x-gtar', // GNU tar
'.shar' => 'application/x-shar', // Shell archive
'.sv4cpio' => 'application/x-sv4cpio', // SVR4 CPIO
'.sv4crc' => 'application/x-sv4crc', // SVR4 CPIO with CRC
'.tar' => 'application/x-tar', // 4.3BSD tar format
'.ustar' => 'application/x-ustar', // POSIX tar format
'.hlp' => 'application/x-winhelp', // Windows Help
'.zip' => 'application/zip', // ZIP archive
'.au' => 'audio/basic', // Basic audio (usually m-law)
'.snd' => 'audio/basic', // Basic audio (usually m-law)
'.aif' => 'audio/x-aiff', // AIFF audio
'.aiff' => 'audio/x-aiff', // AIFF audio
'.aifc' => 'audio/x-aiff', // AIFF audio
'.ra' => 'audio/x-pn-realaudio', // RealAudio
'.ram' => 'audio/x-pn-realaudio', // RealAudio
'.rpm' => 'audio/x-pn-realaudio-plugin', // RealAudio (plug-in)
'.wav' => 'audio/x-wav', // Windows WAVE audio
'.mp3' => 'audio/x-mp3', // MP3 files
'.gif' => 'image/gif', // gif image
'.ief' => 'image/ief', // Image Exchange Format
'.jpg' => 'image/jpeg', // JPEG image
'.JPG' => 'image/jpeg', // JPEG image
'.JPE' => 'image/jpeg', // JPEG image
'.jpe' => 'image/jpeg', // JPEG image
'.JPEG' => 'image/jpeg', // JPEG image
'.jpeg' => 'image/jpeg', // JPEG image
'.pict' => 'image/pict', // Macintosh PICT
'.tiff' => 'image/tiff', // TIFF image
'.tif' => 'image/tiff', // TIFF image
'.ras' => 'image/x-cmu-raster', // CMU raster
'.pnm' => 'image/x-portable-anymap', // PBM Anymap format
'.pbm' => 'image/x-portable-bitmap', // PBM Bitmap format
'.pgm' => 'image/x-portable-graymap', // PBM Graymap format
'.ppm' => 'image/x-portable-pixmap', // PBM Pixmap format
'.rgb' => 'image/x-rgb', // RGB Image
'.xbm' => 'image/x-xbitmap', // X Bitmap
'.xpm' => 'image/x-xpixmap', // X Pixmap
'.xwd' => 'image/x-xwindowdump', // X Windows dump (xwd) format
'.zip' => 'multipart/x-zip', // PKZIP Archive
'.gzip' => 'multipart/x-gzip', // GNU ZIP Archive
'.mpeg' => 'video/mpeg', // MPEG video
'.mpg' => 'video/mpeg', // MPEG video
'.MPG' => 'video/mpeg', // MPEG video
'.MPE' => 'video/mpeg', // MPEG video
'.mpe' => 'video/mpeg', // MPEG video
'.MPEG' => 'video/mpeg', // MPEG video
'.mpeg' => 'video/mpeg', // MPEG video
'.qt' => 'video/quicktime', // QuickTime Video
'.mov' => 'video/quicktime', // QuickTime Video
'.avi' => 'video/msvideo', // Microsoft Windows Video
'.movie' => 'video/x-sgi-movie', // SGI Movieplayer format
'.wrl' => 'x-world/x-vrml' // VRML Worlds
 
);
 
// }}}
 
// {{{ Enscript file extensions
 
// List of extensions recognised by enscript.
 
$extEnscript = array
(
'.ada' => 'ada',
'.adb' => 'ada',
'.ads' => 'ada',
'.awk' => 'awk',
'.c' => 'c',
'.c++' => 'cpp',
'.cc' => 'cpp',
'.cpp' => 'cpp',
'.csh' => 'csh',
'.cxx' => 'cpp',
'.diff' => 'diffu',
'.dpr' => 'delphi',
'.el' => 'elisp',
'.eps' => 'postscript',
'.f' => 'fortran',
'.for' => 'fortran',
'.gs' => 'haskell',
'.h' => 'c',
'.hpp' => 'cpp',
'.hs' => 'haskell',
'.htm' => 'html',
'.html' => 'html',
'.idl' => 'idl',
'.java' => 'java',
'.js' => 'javascript',
'.lgs' => 'haskell',
'.lhs' => 'haskell',
'.m' => 'objc',
'.m4' => 'm4',
'.man' => 'nroff',
'.nr' => 'nroff',
'.p' => 'pascal',
'.pas' => 'delphi',
'.patch' => 'diffu',
'.pkg' => 'sql',
'.pl' => 'perl',
'.pm' => 'perl',
'.pp' => 'pascal',
'.ps' => 'postscript',
'.s' => 'asm',
'.scheme' => 'scheme',
'.scm' => 'scheme',
'.scr' => 'synopsys',
'.sh' => 'sh',
'.shtml' => 'html',
'.sql' => 'sql',
'.st' => 'states',
'.syn' => 'synopsys',
'.synth' => 'synopsys',
'.tcl' => 'tcl',
'.tex' => 'tex',
'.texi' => 'tex',
'.texinfo' => 'tex',
'.v' => 'verilog',
'.vba' => 'vba',
'.vh' => 'verilog',
'.vhd' => 'vhdl',
'.vhdl' => 'vhdl',
'.py' => 'python',
// The following are handled internally by WebSVN, since there's no
// support for them in Enscript
'.php' => 'php',
'.phtml' => 'php',
'.php3' => 'php',
'.inc' => 'php'
);
 
// }}}
 
// Default 'zipped' array
 
$zipped = array ();
 
// Get the user's personalised config
 
require_once 'config.inc';
 
// Set up the version info
 
initSvnVersion($major,$minor);
 
// Get the language choice as defained as the default by config.inc
$user_defaultLang = $lang['LANGUAGENAME'];
 
// Override this with the user choice if there is one, and memorise the setting
// as a cookie (since we don't have user accounts, we can't store the setting
// anywhere else). We try to memorise a permanant cookie and a per session cookie
// in case the user's disabled permanant ones.
 
if (!empty($_REQUEST['langchoice']))
{
$user_defaultLang = $_REQUEST['langchoice'];
setcookie('storedlang', $_REQUEST['langchoice'], time()+(3600*24*356*10), '/');
setcookie('storedsesslang', $_REQUEST['langchoice']);
}
else // Try to read an existing cookie if there is one
{
if (!empty($_COOKIE['storedlang'])) $user_defaultLang = $_COOKIE['storedlang'];
else if (!empty($_COOKIE['storedsesslang'])) $user_defaultLang = $_COOKIE['storedsesslang'];
}
$user_defaultFile = '';
if ($handle = opendir('languages'))
{
// Read the language name for each language.
while (false !== ($file = readdir($handle)))
{
if ($file{0} != '.' && !is_dir('languages'.DIRECTORY_SEPARATOR.$file))
{
$lang['LANGUAGENAME'] = '';
require 'languages/'.$file;
if ($lang['LANGUAGENAME'] != '')
{
$langNames[] = $lang['LANGUAGENAME'];
if ($lang['LANGUAGENAME'] == $user_defaultLang)
$user_defaultFile = $file;
}
}
}
 
closedir($handle);
// XXX: this shouldn't be necessary
// ^ i.e. just require english.inc, then the desired language
// Reload english to get untranslated strings
require 'languages/english.inc';
// Reload the default language
if (!empty($user_defaultFile))
require 'languages/'.$user_defaultFile;
$url = getParameterisedSelfUrl(true);
$vars["lang_form"] = "<form action=\"$url\" method=\"post\" id=\"langform\">";
$vars["lang_select"] = "<select name=\"langchoice\" onchange=\"javascript:this.form.submit();\">";
reset($langNames);
foreach ($langNames as $name)
{
$sel = "";
if ($name == $user_defaultLang) $sel = "selected";
$vars["lang_select"] .= "<option value=\"$name\" $sel>$name</option>";
}
$vars["lang_select"] .= "</select>";
$vars["lang_submit"] = "<input type=\"submit\" value=\"${lang["GO"]}\">";
$vars["lang_endform"] = "</form>";
}
 
// Set up headers
 
header("Content-type: text/html; charset=".$config->outputEnc);
 
// Make sure that the user has set up a repository
 
$reps = $config->getRepositories();
if (empty($reps[0]))
{
echo $lang["SUPPLYREP"];
exit;
}
 
// Override the rep parameter with the repository name if it's available
$repname = @$_REQUEST["repname"];
if (isset($repname))
{
$rep = $config->findRepository($repname);
}
else
$rep = $reps[0];
// Retrieve other standard parameters
 
# due to possible XSS exploit, we need to clean up path first
$path = !empty($_REQUEST['path']) ? $_REQUEST['path'] : null;
$vars['safepath'] = htmlentities($path);
$rev = (int)@$_REQUEST["rev"];
$showchanged = (@$_REQUEST["sc"] == 1)?1:0;
 
// Function to create the project selection HTML form
function createProjectSelectionForm()
{
global $config, $vars, $rep, $lang, $showchanged;
$url = $config->getURL(-1, "", "form");
$vars["projects_form"] = "<form action=\"$url\" method=\"post\" id=\"projectform\">";
$reps = $config->getRepositories();
$vars["projects_select"] = "<select name=\"repname\" onchange=\"javascript:this.form.submit();\">";
foreach ($reps as $trep)
{
if ($trep->hasReadAccess("/", true))
{
if ($rep->getDisplayName() == $trep->getDisplayName())
$sel = "selected";
else
$sel = "";
$vars["projects_select"] .= "<option value=\"".$trep->getDisplayName()."\" $sel>".$trep->getDisplayName()."</option>";
}
}
$vars["projects_select"] .= "</select>";
$vars["projects_submit"] = "<input type=\"submit\" value=\"${lang["GO"]}\" />";
$vars["projects_endform"] = "<input type=\"hidden\" name=\"selectproj\" value=\"1\" /><input type=\"hidden\" name=\"op\" value=\"form\" /><input type=\"hidden\" name=\"sc\" value=\"$showchanged\" /></form>";
}
 
// Create the form if we're not in MultiViews. Otherwise wsvn must create the form once the current project has
// been found
 
if (!$config->multiViews)
{
createProjectSelectionForm();
}
 
if ($rep)
{
$vars["allowdownload"] = $rep->getAllowDownload();
$vars["repname"] = $rep->getDisplayName();
}
 
// As of version 1.70 the output encoding is forced to be UTF-8, since this is the output
// encoding returned by svn log --xml. This is good, since we are no longer reliant on PHP's
// rudimentary conversions.
 
$vars["charset"] = "UTF-8";
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// setup.inc
//
// Global setup
 
// --- DON'T CHANGE THIS FILE ---
//
// User changes should be done in config.ini
 
// Include the configuration class
require_once 'include/configclass.inc';
 
// Create the config
$config = new Config;
 
// Set up the default character encodings
if (function_exists('iconv_get_encoding'))
{
$config->setInputEncoding(iconv_get_encoding('input_encoding'));
}
 
// Define the language array
$lang = array();
$langNames = array();
 
// Set up locwebsvnhttp
// Note: we will use nothing in MultiViews mode so that the URLs use the root
// directory by default.
if (empty($locwebsvnhttp))
$locwebsvnhttp = defined('WSVN_MULTIVIEWS') ? '' : '.';
 
if (empty($locwebsvnreal))
$locwebsvnreal = '.';
 
$vars['locwebsvnhttp'] = $locwebsvnhttp;
 
// Include a default language file (must go before config.inc)
require 'languages/english.inc';
 
// Get the user's personalised config (requires the locwebsvnhttp stuff above)
require_once 'config.inc';
 
// Load PHP_Compat if we're going to use it. This needs to be done after including config.inc (which contains
// the setting) but before svnlook.inc (which requires util.inc, which contains PHP4-incompatible functions)
if ($config->isPHPCompatEnabled()) {
require_once($config->getPHPCompatFile());
require_once 'include/php5compat.inc';
}
 
require_once 'include/svnlook.inc';
 
// Make sure that the input locale is set up correctly
setlocale(LC_ALL, '');
 
// {{{ Content-Type's
// Set up the default content-type extension handling
 
$contentType = array (
 
'.dwg' => 'application/acad', // AutoCAD Drawing files
'.DWG' => 'application/acad', // AutoCAD Drawing files
'.arj' => 'application/arj', //  
'.ccAD' => 'application/clariscad', // ClarisCAD files
'.DRW' => 'application/drafting', // MATRA Prelude drafting
'.dxf' => 'application/dxf', // DXF (AutoCAD)
'.DXF' => 'application/dxf', // DXF (AutoCAD)
'.xl' => 'application/excel', // Microsoft Excel
'.unv' => 'application/i-deas', //SDRC I-DEAS files
'.UNV' => 'application/i-deas', //SDRC I-DEAS files
'.igs' => 'application/iges', // IGES graphics format
'.iges' => 'application/iges', // IGES graphics format
'.IGS' => 'application/iges', // IGES graphics format
'.IGES' => 'application/iges', // IGES graphics format
'.hqx' => 'application/mac-binhex40', // Macintosh BinHex format
'.word' => 'application/msword', // Microsoft Word
'.w6w' => 'application/msword', // Microsoft Word
'.doc' => 'application/msword', // Microsoft Word
'.wri' => 'application/mswrite', // Microsoft Write
'.bin' => 'application/octet-stream', // Uninterpreted binary
'.exe' => 'application/x-msdownload', // Windows EXE
'.EXE' => 'application/x-msdownload', // Windows EXE
'.oda' => 'application/oda', //  
'.pdf' => 'application/pdf', // PDF (Adobe Acrobat)
'.ai' => 'application/postscript', // PostScript
'.PS' => 'application/postscript', // PostScript
'.ps' => 'application/postscript', // PostScript
'.eps' => 'application/postscript', // PostScript
'.prt' => 'application/pro_eng', // PTC Pro/ENGINEER
'.PRT' => 'application/pro_eng', // PTC Pro/ENGINEER
'.part' => 'application/pro_eng', // PTC Pro/ENGINEER
'.rtf' => 'application/rtf', // Rich Text Format
'.set' => 'application/set', // SET (French CAD standard)
'.SET' => 'application/set', // SET (French CAD standard)
'.stl' => 'application/sla', // Stereolithography
'.STL' => 'application/sla', // Stereolithography
'.SOL' => 'application/solids', // MATRA Prelude Solids
'.stp' => 'application/STEP', // ISO-10303 STEP data files
'.STP' => 'application/STEP', // ISO-10303 STEP data files
'.step' => 'application/STEP', // ISO-10303 STEP data files
'.STEP' => 'application/STEP', // ISO-10303 STEP data files
'.vda' => 'application/vda', // VDA-FS Surface data
'.VDA' => 'application/vda', // VDA-FS Surface data
'.dir' => 'application/x-director', // Macromedia Director
'.dcr' => 'application/x-director', // Macromedia Director
'.dxr' => 'application/x-director', // Macromedia Director
'.mif' => 'application/x-mif', // FrameMaker MIF Format
'.csh' => 'application/x-csh', // C-shell script
'.dvi' => 'application/x-dvi', // TeX DVI
'.gz' => 'application/x-gzip', // GNU Zip
'.gzip' => 'application/x-gzip', // GNU Zip
'.hdf' => 'application/x-hdf', // ncSA HDF Data File
'.latex' => 'application/x-latex', // LaTeX source
'.nc' => 'application/x-netcdf', // Unidata netCDF
'.cdf' => 'application/x-netcdf', // Unidata netCDF
'.sit' => 'application/x-stuffit', // Stiffut Archive
'.tcl' => 'application/x-tcl', // TCL script
'.texinfo' => 'application/x-texinfo', // Texinfo (Emacs)
'.texi' => 'application/x-texinfo', // Texinfo (Emacs)
'.t' => 'application/x-troff', // Troff
'.tr' => 'application/x-troff', // Troff
'.roff' => 'application/x-troff', // Troff
'.man' => 'application/x-troff-man', // Troff with MAN macros
'.me' => 'application/x-troff-me', // Troff with ME macros
'.ms' => 'application/x-troff-ms', // Troff with MS macros
'.src' => 'application/x-wais-source', // WAIS source
'.bcpio' => 'application/x-bcpio', // Old binary CPIO
'.cpio' => 'application/x-cpio', // POSIX CPIO
'.gtar' => 'application/x-gtar', // GNU tar
'.shar' => 'application/x-shar', // Shell archive
'.sv4cpio' => 'application/x-sv4cpio', // SVR4 CPIO
'.sv4crc' => 'application/x-sv4crc', // SVR4 CPIO with CRC
'.tar' => 'application/x-tar', // 4.3BSD tar format
'.ustar' => 'application/x-ustar', // POSIX tar format
'.hlp' => 'application/x-winhelp', // Windows Help
'.zip' => 'application/zip', // ZIP archive
'.au' => 'audio/basic', // Basic audio (usually m-law)
'.snd' => 'audio/basic', // Basic audio (usually m-law)
'.aif' => 'audio/x-aiff', // AIFF audio
'.aiff' => 'audio/x-aiff', // AIFF audio
'.aifc' => 'audio/x-aiff', // AIFF audio
'.ra' => 'audio/x-pn-realaudio', // RealAudio
'.ram' => 'audio/x-pn-realaudio', // RealAudio
'.rpm' => 'audio/x-pn-realaudio-plugin', // RealAudio (plug-in)
'.wav' => 'audio/x-wav', // Windows WAVE audio
'.mp3' => 'audio/x-mp3', // MP3 files
'.gif' => 'image/gif', // gif image
'.ief' => 'image/ief', // Image Exchange Format
'.jpg' => 'image/jpeg', // JPEG image
'.JPG' => 'image/jpeg', // JPEG image
'.JPE' => 'image/jpeg', // JPEG image
'.jpe' => 'image/jpeg', // JPEG image
'.JPEG' => 'image/jpeg', // JPEG image
'.jpeg' => 'image/jpeg', // JPEG image
'.pict' => 'image/pict', // Macintosh PICT
'.tiff' => 'image/tiff', // TIFF image
'.tif' => 'image/tiff', // TIFF image
'.ras' => 'image/x-cmu-raster', // CMU raster
'.pnm' => 'image/x-portable-anymap', // PBM Anymap format
'.pbm' => 'image/x-portable-bitmap', // PBM Bitmap format
'.pgm' => 'image/x-portable-graymap', // PBM Graymap format
'.ppm' => 'image/x-portable-pixmap', // PBM Pixmap format
'.rgb' => 'image/x-rgb', // RGB Image
'.xbm' => 'image/x-xbitmap', // X Bitmap
'.xpm' => 'image/x-xpixmap', // X Pixmap
'.xwd' => 'image/x-xwindowdump', // X Windows dump (xwd) format
'.zip' => 'multipart/x-zip', // PKZIP Archive
'.gzip' => 'multipart/x-gzip', // GNU ZIP Archive
'.mpeg' => 'video/mpeg', // MPEG video
'.mpg' => 'video/mpeg', // MPEG video
'.MPG' => 'video/mpeg', // MPEG video
'.MPE' => 'video/mpeg', // MPEG video
'.mpe' => 'video/mpeg', // MPEG video
'.MPEG' => 'video/mpeg', // MPEG video
'.mpeg' => 'video/mpeg', // MPEG video
'.qt' => 'video/quicktime', // QuickTime Video
'.mov' => 'video/quicktime', // QuickTime Video
'.avi' => 'video/msvideo', // Microsoft Windows Video
'.movie' => 'video/x-sgi-movie', // SGI Movieplayer format
'.wrl' => 'x-world/x-vrml', // VRML Worlds
'.odt' => 'application/vnd.oasis.opendocument.text', // OpenDocument Text
'.ott' => 'application/vnd.oasis.opendocument.text-template', // OpenDocument Text Template
'.ods' => 'application/vnd.oasis.opendocument.spreadsheet', // OpenDocument Spreadsheet
'.ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', // OpenDocument Spreadsheet Template
'.odp' => 'application/vnd.oasis.opendocument.presentation', // OpenDocument Presentation
'.otp' => 'application/vnd.oasis.opendocument.presentation-template', // OpenDocument Presentation Template
'.odg' => 'application/vnd.oasis.opendocument.graphics', // OpenDocument Drawing
'.otg' => 'application/vnd.oasis.opendocument.graphics-template', // OpenDocument Drawing Template
'.odc' => 'application/vnd.oasis.opendocument.chart', // OpenDocument Chart
'.otc' => 'application/vnd.oasis.opendocument.chart-template', // OpenDocument Chart Template
'.odf' => 'application/vnd.oasis.opendocument.formula', // OpenDocument Formula
'.otf' => 'application/vnd.oasis.opendocument.formula-template', // OpenDocument Formula Template
'.odi' => 'application/vnd.oasis.opendocument.image', // OpenDocument Image
'.oti' => 'application/vnd.oasis.opendocument.image-template', // OpenDocument Image Template
'.odm' => 'application/vnd.oasis.opendocument.text-master', // OpenDocument Master Document
'.oth' => 'application/vnd.oasis.opendocument.text-web', // HTML Document Template
'.odb' => 'application/vnd.oasis.opendocument.database', // OpenDocument Database
 
);
 
// }}}
 
// {{{ Enscript file extensions
 
// List of extensions recognised by enscript.
 
$extEnscript = array
(
'.ada' => 'ada',
'.adb' => 'ada',
'.ads' => 'ada',
'.awk' => 'awk',
'.c' => 'c',
'.c++' => 'cpp',
'.cc' => 'cpp',
'.cpp' => 'cpp',
'.csh' => 'csh',
'.cxx' => 'cpp',
'.diff' => 'diffu',
'.dpr' => 'delphi',
'.el' => 'elisp',
'.eps' => 'postscript',
'.f' => 'fortran',
'.for' => 'fortran',
'.gs' => 'haskell',
'.h' => 'c',
'.hpp' => 'cpp',
'.hs' => 'haskell',
'.htm' => 'html',
'.html' => 'html',
'.idl' => 'idl',
'.java' => 'java',
'.js' => 'javascript',
'.lgs' => 'haskell',
'.lhs' => 'haskell',
'.m' => 'objc',
'.m4' => 'm4',
'.man' => 'nroff',
'.nr' => 'nroff',
'.p' => 'pascal',
'.pas' => 'delphi',
'.patch' => 'diffu',
'.pkg' => 'sql',
'.pl' => 'perl',
'.pm' => 'perl',
'.pp' => 'pascal',
'.ps' => 'postscript',
'.s' => 'asm',
'.scheme' => 'scheme',
'.scm' => 'scheme',
'.scr' => 'synopsys',
'.sh' => 'sh',
'.shtml' => 'html',
'.sql' => 'sql',
'.st' => 'states',
'.syn' => 'synopsys',
'.synth' => 'synopsys',
'.tcl' => 'tcl',
'.tex' => 'tex',
'.texi' => 'tex',
'.texinfo' => 'tex',
'.v' => 'verilog',
'.vba' => 'vba',
'.vh' => 'verilog',
'.vhd' => 'vhdl',
'.vhdl' => 'vhdl',
'.py' => 'python',
// The following are handled internally by WebSVN, since there's no
// support for them in Enscript
'.php' => 'php',
'.phtml' => 'php',
'.php3' => 'php',
'.inc' => 'php'
);
 
// }}}
 
// Default 'zipped' array
 
$zipped = array ();
 
// Set up the version info
 
initSvnVersion($major,$minor);
 
// Get the language choice as defained as the default by config.inc
$user_defaultLang = $lang['LANGUAGENAME'];
 
// Override this with the user choice if there is one, and memorise the setting
// as a cookie (since we don't have user accounts, we can't store the setting
// anywhere else). We try to memorise a permanant cookie and a per session cookie
// in case the user's disabled permanant ones.
 
if (!empty($_REQUEST['langchoice']))
{
$user_defaultLang = $_REQUEST['langchoice'];
setcookie('storedlang', $_REQUEST['langchoice'], time()+(3600*24*356*10), '/');
setcookie('storedsesslang', $_REQUEST['langchoice']);
}
else // Try to read an existing cookie if there is one
{
if (!empty($_COOKIE['storedlang'])) $user_defaultLang = $_COOKIE['storedlang'];
else if (!empty($_COOKIE['storedsesslang'])) $user_defaultLang = $_COOKIE['storedsesslang'];
}
$user_defaultFile = '';
if ($handle = opendir('languages'))
{
// Read the language name for each language.
while (false !== ($file = readdir($handle)))
{
if ($file{0} != '.' && !is_dir('languages'.DIRECTORY_SEPARATOR.$file))
{
$lang['LANGUAGENAME'] = '';
require 'languages/'.$file;
if ($lang['LANGUAGENAME'] != '')
{
$langNames[] = $lang['LANGUAGENAME'];
if ($lang['LANGUAGENAME'] == $user_defaultLang)
$user_defaultFile = $file;
}
}
}
 
closedir($handle);
// XXX: this shouldn't be necessary
// ^ i.e. just require english.inc, then the desired language
// Reload english to get untranslated strings
require 'languages/english.inc';
// Reload the default language
if (!empty($user_defaultFile))
require 'languages/'.$user_defaultFile;
$url = getParameterisedSelfUrl(true);
$vars["lang_form"] = "<form action=\"$url\" method=\"post\" id=\"langform\">";
$vars["lang_select"] = "<select name=\"langchoice\" onchange=\"javascript:this.form.submit();\">";
reset($langNames);
foreach ($langNames as $name)
{
$sel = "";
if ($name == $user_defaultLang) $sel = "selected";
$vars["lang_select"] .= "<option value=\"$name\" $sel>$name</option>";
}
$vars["lang_select"] .= "</select>";
$vars["lang_submit"] = "<input type=\"submit\" value=\"${lang["GO"]}\">";
$vars["lang_endform"] = "</form>";
}
 
// Set up headers
 
header("Content-type: text/html; charset=".$config->outputEnc);
 
// Make sure that the user has set up a repository
 
$reps = $config->getRepositories();
if (empty($reps[0]))
{
echo $lang["SUPPLYREP"];
exit;
}
 
// Override the rep parameter with the repository name if it's available
$repname = @$_REQUEST["repname"];
if (isset($repname))
{
$rep = $config->findRepository($repname);
}
else
$rep = $reps[0];
// Retrieve other standard parameters
 
# due to possible XSS exploit, we need to clean up path first
$path = !empty($_REQUEST['path']) ? $_REQUEST['path'] : null;
$vars['safepath'] = htmlentities($path);
$rev = (int)@$_REQUEST["rev"];
$showchanged = (@$_REQUEST["sc"] == 1)?1:0;
 
// Function to create the project selection HTML form
function createProjectSelectionForm()
{
global $config, $vars, $rep, $lang, $showchanged;
$url = $config->getURL(-1, "", "form");
$vars["projects_form"] = "<form action=\"$url\" method=\"post\" id=\"projectform\">";
$reps = $config->getRepositories();
$vars["projects_select"] = "<select name=\"repname\" onchange=\"javascript:this.form.submit();\">";
foreach ($reps as $trep)
{
if ($trep->hasReadAccess("/", true))
{
if ($rep->getDisplayName() == $trep->getDisplayName())
$sel = "selected";
else
$sel = "";
$vars["projects_select"] .= "<option value=\"".$trep->getDisplayName()."\" $sel>".$trep->getDisplayName()."</option>";
}
}
$vars["projects_select"] .= "</select>";
$vars["projects_submit"] = "<input type=\"submit\" value=\"${lang["GO"]}\" />";
$vars["projects_endform"] = "<input type=\"hidden\" name=\"selectproj\" value=\"1\" /><input type=\"hidden\" name=\"op\" value=\"form\" /><input type=\"hidden\" name=\"sc\" value=\"$showchanged\" /></form>";
}
 
// Create the form if we're not in MultiViews. Otherwise wsvn must create the form once the current project has
// been found
 
if (!$config->multiViews)
{
createProjectSelectionForm();
}
 
if ($rep)
{
$vars["allowdownload"] = $rep->getAllowDownload();
$vars["repname"] = $rep->getDisplayName();
}
 
// As of version 1.70 the output encoding is forced to be UTF-8, since this is the output
// encoding returned by svn log --xml. This is good, since we are no longer reliant on PHP's
// rudimentary conversions.
 
$vars["charset"] = "UTF-8";
?>
/WebSVN/include/svnlook.inc
1,770 → 1,769
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// svn-look.inc
//
// Svn bindings
//
// These binding currently use the svn command line to achieve their goal. Once a proper
// SWIG binding has been produced for PHP, there'll be an option to use that instead.
 
require_once("include/utils.inc");
 
// {{{ Classes for retaining log information ---
 
$debugxml = false;
 
Class SVNMod
{
var $action = '';
var $copyfrom = '';
var $copyrev = '';
var $path = '';
}
 
Class SVNLogEntry
{
var $rev = 1;
var $author = '';
var $date = '';
var $committime;
var $age = '';
var $msg = '';
var $path = '';
var $mods;
var $curMod;
}
 
Class SVNLog
{
var $entries; // Array of entries
var $curEntry; // Current entry
var $path = ''; // Temporary variable used to trace path history
// findEntry
//
// Return the entry for a given revision
function findEntry($rev)
{
foreach ($this->entries as $index => $entry)
{
if ($entry->rev == $rev)
return $index;
 
}
}
}
 
// }}}
 
// {{{ XML parsing functions---
 
$curLog = 0;
$curTag = '';
 
// {{{ startElement
 
function startElement($parser, $name, $attrs)
{
global $curLog, $curTag, $debugxml;
 
switch ($name)
{
case "LOGENTRY":
if ($debugxml) print "Creating new log entry\n";
$curLog->curEntry = new SVNLogEntry;
$curLog->curEntry->mods = array();
$curLog->curEntry->path = $curLog->path;
if (sizeof($attrs))
{
while (list($k, $v) = each($attrs))
{
switch ($k)
{
case "REVISION":
if ($debugxml) print "Revision $v\n";
$curLog->curEntry->rev = $v;
break;
}
}
}
break;
case "PATH":
if ($debugxml) print "Creating new path\n";
$curLog->curEntry->curMod = new SVNMod;
if (sizeof($attrs))
{
while (list($k, $v) = each($attrs))
{
switch ($k)
{
case "ACTION":
if ($debugxml) print "Action $v\n";
$curLog->curEntry->curMod->action = $v;
break;
 
case "COPYFROM-PATH":
if ($debugxml) print "Copy from: $v\n";
$curLog->curEntry->curMod->copyfrom = $v;
break;
 
case "COPYFROM-REV":
$curLog->curEntry->curMod->copyrev = $v;
break;
}
}
}
$curTag = $name;
break;
default:
$curTag = $name;
break;
}
}
 
// }}}
 
// {{{ endElement
 
function endElement($parser, $name)
{
global $curLog, $debugxml, $curTag;
 
switch ($name)
{
case "LOGENTRY":
if ($debugxml) print "Ending new log entry\n";
$curLog->entries[] = $curLog->curEntry;
break;
 
case "PATH":
if ($debugxml) print "Ending path\n";
$curLog->curEntry->mods[] = $curLog->curEntry->curMod;
break;
 
case "MSG":
$curLog->curEntry->msg = trim($curLog->curEntry->msg);
if ($debugxml) print "Completed msg = '".$curLog->curEntry->msg."'\n";
break;
}
$curTag = "";
}
 
// }}}
 
// {{{ characterData
 
function characterData($parser, $data)
{
global $curLog, $curTag, $lang, $debugxml;
 
switch ($curTag)
{
case "AUTHOR":
if ($debugxml) print "Author: $data\n";
if (empty($data)) return;
$curLog->curEntry->author .= htmlentities($data, ENT_COMPAT, "UTF-8");
break;
 
case "DATE":
if ($debugxml) print "Date: $data\n";
$data = trim($data);
if (empty($data)) return;
sscanf($data, "%d-%d-%dT%d:%d:%d.", $y, $mo, $d, $h, $m, $s);
$mo = substr("00".$mo, -2);
$d = substr("00".$d, -2);
$h = substr("00".$h, -2);
$m = substr("00".$m, -2);
$s = substr("00".$s, -2);
$curLog->curEntry->date = "$y-$mo-$d $h:$m:$s GMT";
$committime = strtotime($curLog->curEntry->date);
$curLog->curEntry->committime = $committime;
$curtime = time();
// Get the number of seconds since the commit
$agesecs = $curtime - $committime;
if ($agesecs < 0) $agesecs = 0;
$curLog->curEntry->age = datetimeFormatDuration($agesecs, true, true);
break;
 
case "MSG":
if ($debugxml) print "Msg: '$data'\n";
$curLog->curEntry->msg .= htmlentities($data, ENT_COMPAT, "UTF-8");
break;
case "PATH":
if ($debugxml) print "Path name: '$data'\n";
$data = trim($data);
if (empty($data)) return;
 
$curLog->curEntry->curMod->path .= $data;
// The XML returned when a file is renamed/branched in inconsistant. In the case
// of a branch, the path information doesn't include the leafname. In the case of
// a rename, it does. Ludicrous.
if (!empty($curLog->path))
{
$pos = strrpos($curLog->path, "/");
$curpath = substr($curLog->path, 0, $pos);
$leafname = substr($curLog->path, $pos + 1);
}
else
{
$curpath = "";
$leafname = "";
}
if ($curLog->curEntry->curMod->action == "A")
{
if ($debugxml) print "Examining added path '".$curLog->curEntry->curMod->copyfrom."' - Current path = '$curpath', leafname = '$leafname'\n";
if ($data == $curLog->path) // For directories and renames
{
if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."'\n";
$curLog->path = $curLog->curEntry->curMod->copyfrom;
}
else if ($data == $curpath || $data == $curpath."/") // Logs of files that have moved due to branching
{
if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."/$leafname'\n";
$curLog->path = $curLog->curEntry->curMod->copyfrom."/$leafname";
}
}
break;
}
}
 
// }}}
 
// }}}
 
// Function returns true if the give entry in a directory tree is at the top level
 
function _topLevel($entry)
{
// To be at top level, there must be one space before the entry
return (strlen($entry) > 1 && $entry{0} == " " && $entry{1} != " ");
}
 
// Function to sort two given directory entries. Directories go at the top
 
function _dirSort($e1, $e2)
{
$isDir1 = $e1{strlen($e1) - 1} == "/";
$isDir2 = $e2{strlen($e2) - 1} == "/";
if ($isDir1 && !$isDir2) return -1;
if ($isDir2 && !$isDir1) return 1;
return strnatcasecmp($e1, $e2);
}
 
// Return the revision string to pass to a command
 
function _revStr($rev)
{
if ($rev > 0)
return "-r $rev";
else
return "";
}
 
// {{{ encodePath
 
// Function to encode a URL without encoding the /'s
 
function encodePath($uri)
{
global $config;
 
$uri = str_replace(DIRECTORY_SEPARATOR, "/", $uri);
 
$parts = explode('/', $uri);
for ($i = 0; $i < count($parts); $i++)
-
- if ( function_exists("mb_detect_encoding") && function_exists("mb_convert_encoding"))
- {
- $parts[$i] = mb_convert_encoding($parts[$i], "UTF-8", mb_detect_encoding($parts[$i]));
- }
-
- $parts[$i] = rawurlencode($parts[$i]);
- }
-
- $uri = implode('/', $parts);
-
- // Quick hack. Subversion seems to have a bug surrounding the use of %3A instead of :
-
- $uri = str_replace("%3A" ,":", $uri);
-
- // Correct for Window share names
- if ( $config->serverIsWindows==true )
- {
- if ( substr($uri, 0,2)=="//" )
- $uri="\\".substr($uri, 2, strlen($uri));
- }
-
- return $uri;
-}
-
-// }}}
-
-// The SVNRepository Class
-
-Class SVNRepository
-{
- var $repConfig;
-
- function SVNRepository($repConfig)
- {
- $this->repConfig = $repConfig;
- }
-
- // {{{ dirContents
-
- function dirContents($path, $rev = 0)
- {
- global $config, $locwebsvnreal;
-
- $revstr = _revStr($rev);
-
- $tree = array();
-
- if ($rev == 0)
- {
- $headlog = $this->getLog("/", "", "", true, 1);
- $rev = $headlog->entries[0]->rev;
- }
-
- $path = encodepath($this->repConfig->path.$path);
- $output = runCommand($config->svn." list $revstr ".$this->repConfig->svnParams().quote($path), true);
-
- foreach ($output as $entry)
- {
- if ($entry != "")
- $tree[] = $entry;
- }
-
- // Sort the entries into alphabetical order with the directories at the top of the list
- usort($tree, "_dirSort");
-
- return $tree;
- }
-
- // }}}
-
- // {{{ highlightLine
- //
- // Distill line-spanning syntax highlighting so that each line can stand alone
- // (when invoking on the first line, $attributes should be an empty array)
- // Invoked to make sure all open syntax highlighting tags (<font>, <i>, <b>, etc.)
- // are closed at the end of each line and re-opened on the next line
-
- function highlightLine($line, &$attributes)
- {
- $hline = "";
-
- // Apply any highlighting in effect from the previous line
- foreach($attributes as $attr)
- {
- $hline.=$attr['text'];
- }
-
- // append the new line
- $hline.=$line;
-
- // update attributes
- for ($line = strstr($line, "<"); $line; $line = strstr(substr($line,1), "<"))
- {
- // if this closes a tag, remove most recent corresponding opener
- if (substr($line,1,1) == "/")
- {
- $tagNamLen = strcspn($line, "> \t", 2);
- $tagNam = substr($line,2,$tagNamLen);
- foreach(array_reverse(array_keys($attributes)) as $k)
- {
- if ($attributes[$k]['tag'] == $tagNam)
- {
- unset($attributes[$k]);
- break;
- }
- }
- }
- else
- // if this opens a tag, add it to the list
- {
- $tagNamLen = strcspn($line, "> \t", 1);
- $tagNam = substr($line,1,$tagNamLen);
- $tagLen = strcspn($line, ">") + 1;
- $attributes[] = array('tag' => $tagNam, 'text' => substr($line,0,$tagLen));
- }
- }
-
- // close any still-open tags
- foreach(array_reverse($attributes) as $attr)
- {
- $hline.="</".$attr['tag'].">";
- }
-
- return($hline);
- }
-
- // }}}
-
- // {{{ getFileContents
- //
- // Dump the content of a file to the given filename
-
- function getFileContents($path, $filename, $rev = 0, $pipe = "", $perLineHighlighting = false)
- {
- global $config, $extEnscript;
-
- $revstr = _revStr($rev);
-
- // If there's no filename, we'll just deliver the contents as it is to the user
- if ($filename == "")
- {
- $path = encodepath($this->repConfig->path.$path);
- passthru(quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." $pipe", false));
- return;
- }
-
- // Get the file contents info
-
- $ext = strrchr($path, ".");
- $l = @$extEnscript[$ext];
-
- if ($l == "php")
- {
- // Output the file to the filename
- $path = encodepath($this->repConfig->path.$path);
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false);
- @exec($cmd);
-
- // Get the file as a string (memory hogging, but we have no other options)
- $content = highlight_file($filename, true);
-
- // Destroy the previous version, and replace it with the highlighted version
- $f = fopen($filename, "w");
- if ($f)
- {
- // The highlight file function doesn't deal with line endings very nicely at all. We'll have to do it
- // by hand.
-
- // Remove the first line generated by highlight()
- $pos = strpos($content, "\n");
- $content = substr($content, $pos+1);
-
- $content = explode("<br />", $content);
-
- if ($perLineHighlighting)
- {
- // If we need each line independently highlighted (e.g. for diff or blame)
- // hen we'll need to filter the output of the highlighter
- // to make sure tags like <font>, <i> or <b> don't span lines
-
- // $attributes is used to remember what highlighting attributes
- // are in effect from one line to the next
- $attributes = array(); // start with no attributes in effect
-
- foreach ($content as $line)
- {
- fputs($f, $this->highlightLine(rtrim($line),$attributes)."\n");
- }
- }
- else
- {
- foreach ($content as $line)
- {
- fputs($f, rtrim($line)."\n");
- }
- }
-
- fclose($f);
- }
- }
- else
- {
- if ($config->useEnscript)
- {
- // Get the files, feed it through enscript, then remove the enscript headers using sed
- //
- // Note that the sec command returns only the part of the file between <PRE> and </PRE>.
- // It's complicated because it's designed not to return those lines themselves.
-
- $path = encodepath($this->repConfig->path.$path);
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." | ".
- $config->enscript." --language=html ".
- ($l ? "--color --pretty-print=$l" : "")." -o - | ".
- $config->sed." -n ".$config->quote."1,/^<PRE.$/!{/^<\\/PRE.$/,/^<PRE.$/!p;}".$config->quote." > $filename", false);
- @exec($cmd);
- }
- else
- {
- $path = encodepath(str_replace(DIRECTORY_SEPARATOR, "/", $this->repConfig->path.$path));
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false);
- @exec($cmd);
- }
- }
- }
-
- // }}}
-
- // {{{ listFileContents
- //
- // Print the contents of a file without filling up Apache's memory
-
- function listFileContents($path, $rev = 0)
- {
- global $config, $extEnscript;
-
- $revstr = _revStr($rev);
- $pre = false;
-
- // Get the file contents info
-
- $ext = strrchr($path, ".");
- $l = @$extEnscript[$ext];
-
- // Deal with php highlighting internally
- if ($l == "php")
- {
- $tmp = tempnam("temp", "wsvn");
-
- // Output the file to a temporary file
- $path = encodepath($this->repConfig->path.$path);
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $tmp", false);
- @exec($cmd);
- highlight_file($tmp);
- unlink($tmp);
- }
- else
- {
- if ($config->useEnscript)
- {
- $path = encodepath($this->repConfig->path.$path);
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." | ".
- $config->enscript." --language=html ".
- ($l ? "--color --pretty-print=$l" : "")." -o - | ".
- $config->sed." -n ".$config->quote."/^<PRE.$/,/^<\\/PRE.$/p".$config->quote." 2>&1", false);
-
- if (!($result = popen($cmd, "r")))
- return;
- }
- else
- {
- $path = encodepath($this->repConfig->path.$path);
- $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." 2>&1", false);
-
- if (!($result = popen($cmd, "r")))
- return;
-
- $pre = true;
- }
-
- if ($pre)
- echo "<PRE>";
-
- while (!feof($result))
- {
- $line = fgets($result, 1024);
- if ($pre) $line = replaceEntities($line, $this->repConfig);
-
- print hardspace($line);
- }
-
- if ($pre)
- echo "</PRE>";
-
- pclose($result);
- }
- }
-
- // }}}
-
- // {{{ getBlameDetails
- //
- // Dump the blame content of a file to the given filename
-
- function getBlameDetails($path, $filename, $rev = 0)
- {
- global $config;
-
- $revstr = _revStr($rev);
-
- $path = encodepath($this->repConfig->path.$path);
- $cmd = quoteCommand($config->svn." blame $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false);
-
- @exec($cmd);
- }
-
- // }}}
-
- // {{{ getProperty
-
- function getProperty($path, $property, $rev = 0)
- {
- global $config;
-
- $revstr = _revStr($rev);
-
- $path = encodepath($this->repConfig->path.$path);
- $ret = runCommand($config->svn." propget $property $revstr ".$this->repConfig->svnParams().quote($path), true);
-
- // Remove the surplus newline
- if (count($ret))
- unset($ret[count($ret) - 1]);
-
- return implode("\n", $ret);
- }
-
- // }}}
-
- // {{{ exportDirectory
- //
- // Exports the directory to the given location
-
- function exportDirectory($path, $filename, $rev = 0)
- {
- global $config;
-
- $revstr = _revStr($rev);
-
- $path = encodepath($this->repConfig->path.$path);
- $cmd = quoteCommand($config->svn." export $revstr ".$this->repConfig->svnParams().quote($path)." ".quote($filename), false);
-
- @exec($cmd);
- }
-
- // }}}
-
- // {{{ getLog
-
- function getLog($path, $brev = "", $erev = 1, $quiet = false, $limit = 2)
- {
- global $config, $curLog, $vars, $lang;
-
- $xml_parser = xml_parser_create("UTF-8");
- xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
- xml_set_element_handler($xml_parser, "startElement", "endElement");
- xml_set_character_data_handler($xml_parser, "characterData");
-
- // Since directories returned by svn log don't have trailing slashes (:-(), we need to remove
- // the trailing slash from the path for comparison purposes
-
- if ($path{strlen($path) - 1} == "/" && $path != "/")
- $path = substr($path, 0, -1);
-
- $curLog = new SVNLog;
- $curLog->entries = array();
- $curLog->path = $path;
-
- $revStr = "";
-
- if ($brev && $erev)
- $revStr = "-r$brev:$erev";
- else if ($brev)
- $revStr = "-r$brev:1";
-
- if (($config->subversionMajorVersion > 1 || $config->subversionMinorVersion >=2) && $limit != 0)
- $revStr .= " --limit $limit";
-
- // Get the log info
- $path = encodepath($this->repConfig->path.$path);
- $info = "--verbose";
- if ($quiet)
- $info = "--quiet";
-
- $cmd = quoteCommand($config->svn." log --xml $info $revStr ".$this->repConfig->svnParams().quote($path), false);
-
- if ($handle = popen($cmd, "r"))
- {
- $firstline = true;
- while (!feof($handle))
- {
- $line = fgets($handle);
- if (!xml_parse($xml_parser, $line, feof($handle)))
- {
- if (xml_get_error_code($xml_parser) != 5)
- {
- die(sprintf("XML error: %s (%d) at line %d column %d byte %d<br>cmd: %s<nr>",
- xml_error_string(xml_get_error_code($xml_parser)),
- xml_get_error_code($xml_parser),
- xml_get_current_line_number($xml_parser),
- xml_get_current_column_number($xml_parser),
- xml_get_current_byte_index($xml_parser),
- $cmd));
- }
- else
- {
- $vars["error"] = $lang["UNKNOWNREVISION"];
- return 0;
- }
- }
- }
- pclose($handle);
- }
-
- xml_parser_free($xml_parser);
- return $curLog;
- }
-
- // }}}
-
-}
-
-// {{{ initSvnVersion
-
-function initSvnVersion(&$major, &$minor)
-{
- global $config;
-
- $ret = runCommand($config->svn_noparams." --version", false);
-
- if (preg_match("~([0-9]?)\.([0-9]?)\.([0-9]?)~",$ret[0],$matches))
- {
- $major = $matches[1];
- $minor = $matches[2];
- }
-
- $config->setSubversionMajorVersion($major);
- $config->setSubversionMinorVersion($minor);
-}
-
-// }}}
-
-?>
+<?php
+# vim:et:ts=3:sts=3:sw=3:fdm=marker:
+
+// WebSVN - Subversion repository viewing via the web using PHP
+// Copyright © 2004-2006 Tim Armes, Matt Sicker
+//
+// 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
+//
+// --
+//
+// svn-look.inc
+//
+// Svn bindings
+//
+// These binding currently use the svn command line to achieve their goal. Once a proper
+// SWIG binding has been produced for PHP, there'll be an option to use that instead.
+
+require_once("include/utils.inc");
+
+// {{{ Classes for retaining log information ---
+
+$debugxml = false;
+
+Class SVNMod
+{
+ var $action = '';
+ var $copyfrom = '';
+ var $copyrev = '';
+ var $path = '';
+}
+
+Class SVNLogEntry
+{
+ var $rev = 1;
+ var $author = '';
+ var $date = '';
+ var $committime;
+ var $age = '';
+ var $msg = '';
+ var $path = '';
+
+ var $mods;
+ var $curMod;
+}
+
+Class SVNLog
+{
+ var $entries; // Array of entries
+ var $curEntry; // Current entry
+
+ var $path = ''; // Temporary variable used to trace path history
+
+ // findEntry
+ //
+ // Return the entry for a given revision
+
+ function findEntry($rev)
+ {
+ foreach ($this->entries as $index => $entry)
+ {
+ if ($entry->rev == $rev)
+ return $index;
+
+ }
+ }
+}
+
+// }}}
+
+// {{{ XML parsing functions---
+
+$curLog = 0;
+$curTag = '';
+
+// {{{ startElement
+
+function startElement($parser, $name, $attrs)
+{
+ global $curLog, $curTag, $debugxml;
+
+ switch ($name)
+ {
+ case "LOGENTRY":
+ if ($debugxml) print "Creating new log entry\n";
+ $curLog->curEntry = new SVNLogEntry;
+ $curLog->curEntry->mods = array();
+
+ $curLog->curEntry->path = $curLog->path;
+
+ if (sizeof($attrs))
+ {
+ while (list($k, $v) = each($attrs))
+ {
+ switch ($k)
+ {
+ case "REVISION":
+ if ($debugxml) print "Revision $v\n";
+ $curLog->curEntry->rev = $v;
+ break;
+ }
+ }
+ }
+ break;
+
+ case "PATH":
+ if ($debugxml) print "Creating new path\n";
+ $curLog->curEntry->curMod = new SVNMod;
+
+ if (sizeof($attrs))
+ {
+ while (list($k, $v) = each($attrs))
+ {
+ switch ($k)
+ {
+ case "ACTION":
+ if ($debugxml) print "Action $v\n";
+ $curLog->curEntry->curMod->action = $v;
+ break;
+
+ case "COPYFROM-PATH":
+ if ($debugxml) print "Copy from: $v\n";
+ $curLog->curEntry->curMod->copyfrom = $v;
+ break;
+
+ case "COPYFROM-REV":
+ $curLog->curEntry->curMod->copyrev = $v;
+ break;
+ }
+ }
+ }
+
+ $curTag = $name;
+ break;
+
+ default:
+ $curTag = $name;
+ break;
+ }
+}
+
+// }}}
+
+// {{{ endElement
+
+function endElement($parser, $name)
+{
+ global $curLog, $debugxml, $curTag;
+
+ switch ($name)
+ {
+ case "LOGENTRY":
+ if ($debugxml) print "Ending new log entry\n";
+ $curLog->entries[] = $curLog->curEntry;
+ break;
+
+ case "PATH":
+ if ($debugxml) print "Ending path\n";
+ $curLog->curEntry->mods[] = $curLog->curEntry->curMod;
+ break;
+
+ case "MSG":
+ $curLog->curEntry->msg = trim($curLog->curEntry->msg);
+ if ($debugxml) print "Completed msg = '".$curLog->curEntry->msg."'\n";
+ break;
+ }
+
+ $curTag = "";
+}
+
+// }}}
+
+// {{{ characterData
+
+function characterData($parser, $data)
+{
+ global $curLog, $curTag, $lang, $debugxml;
+
+ switch ($curTag)
+ {
+ case "AUTHOR":
+ if ($debugxml) print "Author: $data\n";
+ if (empty($data)) return;
+ $curLog->curEntry->author .= htmlentities($data, ENT_COMPAT, "UTF-8");
+ break;
+
+ case "DATE":
+ if ($debugxml) print "Date: $data\n";
+ $data = trim($data);
+ if (empty($data)) return;
+
+ sscanf($data, "%d-%d-%dT%d:%d:%d.", $y, $mo, $d, $h, $m, $s);
+
+ $mo = substr("00".$mo, -2);
+ $d = substr("00".$d, -2);
+ $h = substr("00".$h, -2);
+ $m = substr("00".$m, -2);
+ $s = substr("00".$s, -2);
+
+ $curLog->curEntry->date = "$y-$mo-$d $h:$m:$s GMT";
+
+ $committime = strtotime($curLog->curEntry->date);
+ $curLog->curEntry->committime = $committime;
+ $curtime = time();
+
+ // Get the number of seconds since the commit
+ $agesecs = $curtime - $committime;
+ if ($agesecs < 0) $agesecs = 0;
+
+ $curLog->curEntry->age = datetimeFormatDuration($agesecs, true, true);
+
+ break;
+
+ case "MSG":
+ if ($debugxml) print "Msg: '$data'\n";
+ $curLog->curEntry->msg .= htmlentities($data, ENT_COMPAT, "UTF-8");
+ break;
+
+ case "PATH":
+ if ($debugxml) print "Path name: '$data'\n";
+ $data = trim($data);
+ if (empty($data)) return;
+
+ $curLog->curEntry->curMod->path .= $data;
+
+ // The XML returned when a file is renamed/branched in inconsistant. In the case
+ // of a branch, the path information doesn't include the leafname. In the case of
+ // a rename, it does. Ludicrous.
+
+ if (!empty($curLog->path))
+ {
+ $pos = strrpos($curLog->path, "/");
+ $curpath = substr($curLog->path, 0, $pos);
+ $leafname = substr($curLog->path, $pos + 1);
+ }
+ else
+ {
+ $curpath = "";
+ $leafname = "";
+ }
+
+ if ($curLog->curEntry->curMod->action == "A")
+ {
+ if ($debugxml) print "Examining added path '".$curLog->curEntry->curMod->copyfrom."' - Current path = '$curpath', leafname = '$leafname'\n";
+ if ($data == $curLog->path) // For directories and renames
+ {
+ if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."'\n";
+ $curLog->path = $curLog->curEntry->curMod->copyfrom;
+ }
+ else if ($data == $curpath || $data == $curpath."/") // Logs of files that have moved due to branching
+ {
+ if ($debugxml) print "New path for comparison: '".$curLog->curEntry->curMod->copyfrom."/$leafname'\n";
+ $curLog->path = $curLog->curEntry->curMod->copyfrom."/$leafname";
+ }
+ }
+ break;
+ }
+}
+
+// }}}
+
+// }}}
+
+// Function returns true if the give entry in a directory tree is at the top level
+
+function _topLevel($entry)
+{
+ // To be at top level, there must be one space before the entry
+ return (strlen($entry) > 1 && $entry{0} == " " && $entry{1} != " ");
+}
+
+// Function to sort two given directory entries. Directories go at the top
+
+function _dirSort($e1, $e2)
+{
+ $isDir1 = $e1{strlen($e1) - 1} == "/";
+ $isDir2 = $e2{strlen($e2) - 1} == "/";
+
+ if ($isDir1 && !$isDir2) return -1;
+ if ($isDir2 && !$isDir1) return 1;
+
+ return strnatcasecmp($e1, $e2);
+}
+
+// Return the revision string to pass to a command
+
+function _revStr($rev)
+{
+ if ($rev > 0)
+ return "-r $rev";
+ else
+ return "";
+}
+
+// {{{ encodePath
+
+// Function to encode a URL without encoding the /'s
+
+function encodePath($uri)
+{
+ global $config;
+
+ $uri = str_replace(DIRECTORY_SEPARATOR, "/", $uri);
+
+ $parts = explode('/', $uri);
+ for ($i = 0; $i < count($parts); $i++)
+ {
+ if ( function_exists("mb_detect_encoding") && function_exists("mb_convert_encoding"))
+ {
+ $parts[$i] = mb_convert_encoding($parts[$i], "UTF-8", mb_detect_encoding($parts[$i]));
+ }
+
+ $parts[$i] = rawurlencode($parts[$i]);
+ }
+
+ $uri = implode('/', $parts);
+
+ // Quick hack. Subversion seems to have a bug surrounding the use of %3A instead of :
+
+ $uri = str_replace("%3A" ,":", $uri);
+
+ // Correct for Window share names
+ if ( $config->serverIsWindows==true )
+ {
+ if ( substr($uri, 0,2)=="//" )
+ $uri="\\".substr($uri, 2, strlen($uri));
+ }
+
+ return $uri;
+}
+
+// }}}
+
+// The SVNRepository Class
+
+Class SVNRepository
+{
+ var $repConfig;
+
+ function SVNRepository($repConfig)
+ {
+ $this->repConfig = $repConfig;
+ }
+
+ // {{{ dirContents
+
+ function dirContents($path, $rev = 0)
+ {
+ global $config, $locwebsvnreal;
+
+ $revstr = _revStr($rev);
+
+ $tree = array();
+
+ if ($rev == 0)
+ {
+ $headlog = $this->getLog("/", "", "", true, 1);
+ $rev = $headlog->entries[0]->rev;
+ }
+
+ $path = encodepath($this->repConfig->path.$path);
+ $output = runCommand($config->svn." list $revstr ".$this->repConfig->svnParams().quote($path), true);
+
+ foreach ($output as $entry)
+ {
+ if ($entry != "")
+ $tree[] = $entry;
+ }
+
+ // Sort the entries into alphabetical order with the directories at the top of the list
+ usort($tree, "_dirSort");
+
+ return $tree;
+ }
+
+ // }}}
+
+ // {{{ highlightLine
+ //
+ // Distill line-spanning syntax highlighting so that each line can stand alone
+ // (when invoking on the first line, $attributes should be an empty array)
+ // Invoked to make sure all open syntax highlighting tags (<font>, <i>, <b>, etc.)
+ // are closed at the end of each line and re-opened on the next line
+
+ function highlightLine($line, &$attributes)
+ {
+ $hline = "";
+
+ // Apply any highlighting in effect from the previous line
+ foreach($attributes as $attr)
+ {
+ $hline.=$attr['text'];
+ }
+
+ // append the new line
+ $hline.=$line;
+
+ // update attributes
+ for ($line = strstr($line, "<"); $line; $line = strstr(substr($line,1), "<"))
+ {
+ // if this closes a tag, remove most recent corresponding opener
+ if (substr($line,1,1) == "/")
+ {
+ $tagNamLen = strcspn($line, "> \t", 2);
+ $tagNam = substr($line,2,$tagNamLen);
+ foreach(array_reverse(array_keys($attributes)) as $k)
+ {
+ if ($attributes[$k]['tag'] == $tagNam)
+ {
+ unset($attributes[$k]);
+ break;
+ }
+ }
+ }
+ else
+ // if this opens a tag, add it to the list
+ {
+ $tagNamLen = strcspn($line, "> \t", 1);
+ $tagNam = substr($line,1,$tagNamLen);
+ $tagLen = strcspn($line, ">") + 1;
+ $attributes[] = array('tag' => $tagNam, 'text' => substr($line,0,$tagLen));
+ }
+ }
+
+ // close any still-open tags
+ foreach(array_reverse($attributes) as $attr)
+ {
+ $hline.="</".$attr['tag'].">";
+ }
+
+ return($hline);
+ }
+
+ // }}}
+
+ // {{{ getFileContents
+ //
+ // Dump the content of a file to the given filename
+
+ function getFileContents($path, $filename, $rev = 0, $pipe = "", $perLineHighlighting = false)
+ {
+ global $config, $extEnscript;
+
+ $revstr = _revStr($rev);
+
+ // If there's no filename, we'll just deliver the contents as it is to the user
+ if ($filename == "")
+ {
+ $path = encodepath($this->repConfig->path.$path);
+ passthru(quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." $pipe", false));
+ return;
+ }
+
+ // Get the file contents info
+
+ $ext = strrchr($path, ".");
+ $l = @$extEnscript[$ext];
+
+ if ($l == "php")
+ {
+ // Output the file to the filename
+ $path = encodepath($this->repConfig->path.$path);
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false);
+ @exec($cmd);
+
+ // Get the file as a string (memory hogging, but we have no other options)
+ $content = highlight_file($filename, true);
+
+ // Destroy the previous version, and replace it with the highlighted version
+ $f = fopen($filename, "w");
+ if ($f)
+ {
+ // The highlight file function doesn't deal with line endings very nicely at all. We'll have to do it
+ // by hand.
+
+ // Remove the first line generated by highlight()
+ $pos = strpos($content, "\n");
+ $content = substr($content, $pos+1);
+
+ $content = explode("<br />", $content);
+
+ if ($perLineHighlighting)
+ {
+ // If we need each line independently highlighted (e.g. for diff or blame)
+ // hen we'll need to filter the output of the highlighter
+ // to make sure tags like <font>, <i> or <b> don't span lines
+
+ // $attributes is used to remember what highlighting attributes
+ // are in effect from one line to the next
+ $attributes = array(); // start with no attributes in effect
+
+ foreach ($content as $line)
+ {
+ fputs($f, $this->highlightLine(rtrim($line),$attributes)."\n");
+ }
+ }
+ else
+ {
+ foreach ($content as $line)
+ {
+ fputs($f, rtrim($line)."\n");
+ }
+ }
+
+ fclose($f);
+ }
+ }
+ else
+ {
+ if ($config->useEnscript)
+ {
+ // Get the files, feed it through enscript, then remove the enscript headers using sed
+ //
+ // Note that the sec command returns only the part of the file between <PRE> and </PRE>.
+ // It's complicated because it's designed not to return those lines themselves.
+
+ $path = encodepath($this->repConfig->path.$path);
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." | ".
+ $config->enscript." --language=html ".
+ ($l ? "--color --pretty-print=$l" : "")." -o - | ".
+ $config->sed." -n ".$config->quote."1,/^<PRE.$/!{/^<\\/PRE.$/,/^<PRE.$/!p;}".$config->quote." > $filename", false);
+ @exec($cmd);
+ }
+ else
+ {
+ $path = encodepath(str_replace(DIRECTORY_SEPARATOR, "/", $this->repConfig->path.$path));
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false);
+ @exec($cmd);
+ }
+ }
+ }
+
+ // }}}
+
+ // {{{ listFileContents
+ //
+ // Print the contents of a file without filling up Apache's memory
+
+ function listFileContents($path, $rev = 0)
+ {
+ global $config, $extEnscript;
+
+ $revstr = _revStr($rev);
+ $pre = false;
+
+ // Get the file contents info
+
+ $ext = strrchr($path, ".");
+ $l = @$extEnscript[$ext];
+
+ // Deal with php highlighting internally
+ if ($l == "php")
+ {
+ $tmp = tempnam("temp", "wsvn");
+
+ // Output the file to a temporary file
+ $path = encodepath($this->repConfig->path.$path);
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." > $tmp", false);
+ @exec($cmd);
+ highlight_file($tmp);
+ unlink($tmp);
+ }
+ else
+ {
+ if ($config->useEnscript)
+ {
+ $path = encodepath($this->repConfig->path.$path);
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." | ".
+ $config->enscript." --language=html ".
+ ($l ? "--color --pretty-print=$l" : "")." -o - | ".
+ $config->sed." -n ".$config->quote."/^<PRE.$/,/^<\\/PRE.$/p".$config->quote." 2>&1", false);
+
+ if (!($result = popen($cmd, "r")))
+ return;
+ }
+ else
+ {
+ $path = encodepath($this->repConfig->path.$path);
+ $cmd = quoteCommand($config->svn." cat $revstr ".$this->repConfig->svnParams().quote($path)." 2>&1", false);
+
+ if (!($result = popen($cmd, "r")))
+ return;
+
+ $pre = true;
+ }
+
+ if ($pre)
+ echo "<PRE>";
+
+ while (!feof($result))
+ {
+ $line = fgets($result, 1024);
+ if ($pre) $line = replaceEntities($line, $this->repConfig);
+
+ print hardspace($line);
+ }
+
+ if ($pre)
+ echo "</PRE>";
+
+ pclose($result);
+ }
+ }
+
+ // }}}
+
+ // {{{ getBlameDetails
+ //
+ // Dump the blame content of a file to the given filename
+
+ function getBlameDetails($path, $filename, $rev = 0)
+ {
+ global $config;
+
+ $revstr = _revStr($rev);
+
+ $path = encodepath($this->repConfig->path.$path);
+ $cmd = quoteCommand($config->svn." blame $revstr ".$this->repConfig->svnParams().quote($path)." > $filename", false);
+
+ @exec($cmd);
+ }
+
+ // }}}
+
+ // {{{ getProperty
+
+ function getProperty($path, $property, $rev = 0)
+ {
+ global $config;
+
+ $revstr = _revStr($rev);
+
+ $path = encodepath($this->repConfig->path.$path);
+ $ret = runCommand($config->svn." propget $property $revstr ".$this->repConfig->svnParams().quote($path), true);
+
+ // Remove the surplus newline
+ if (count($ret))
+ unset($ret[count($ret) - 1]);
+
+ return implode("\n", $ret);
+ }
+
+ // }}}
+
+ // {{{ exportDirectory
+ //
+ // Exports the directory to the given location
+
+ function exportDirectory($path, $filename, $rev = 0)
+ {
+ global $config;
+
+ $revstr = _revStr($rev);
+
+ $path = encodepath($this->repConfig->path.$path);
+ $cmd = quoteCommand($config->svn." export $revstr ".$this->repConfig->svnParams().quote($path)." ".quote($filename), false);
+
+ @exec($cmd);
+ }
+
+ // }}}
+
+ // {{{ getLog
+
+ function getLog($path, $brev = "", $erev = 1, $quiet = false, $limit = 2)
+ {
+ global $config, $curLog, $vars, $lang;
+
+ $xml_parser = xml_parser_create("UTF-8");
+ xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
+ xml_set_element_handler($xml_parser, "startElement", "endElement");
+ xml_set_character_data_handler($xml_parser, "characterData");
+
+ // Since directories returned by svn log don't have trailing slashes (:-(), we need to remove
+ // the trailing slash from the path for comparison purposes
+
+ if ($path{strlen($path) - 1} == "/" && $path != "/")
+ $path = substr($path, 0, -1);
+
+ $curLog = new SVNLog;
+ $curLog->entries = array();
+ $curLog->path = $path;
+
+ $revStr = "";
+
+ if ($brev && $erev)
+ $revStr = "-r$brev:$erev";
+ else if ($brev)
+ $revStr = "-r$brev:1";
+
+ if (($config->subversionMajorVersion > 1 || $config->subversionMinorVersion >=2) && $limit != 0)
+ $revStr .= " --limit $limit";
+
+ // Get the log info
+ $path = encodepath($this->repConfig->path.$path);
+ $info = "--verbose";
+ if ($quiet)
+ $info = "--quiet";
+
+ $cmd = quoteCommand($config->svn." log --xml $info $revStr ".$this->repConfig->svnParams().quote($path), false);
+
+ if ($handle = popen($cmd, "r"))
+ {
+ $firstline = true;
+ while (!feof($handle))
+ {
+ $line = fgets($handle);
+ if (!xml_parse($xml_parser, $line, feof($handle)))
+ {
+ if (xml_get_error_code($xml_parser) != 5)
+ {
+ die(sprintf("XML error: %s (%d) at line %d column %d byte %d<br>cmd: %s<nr>",
+ xml_error_string(xml_get_error_code($xml_parser)),
+ xml_get_error_code($xml_parser),
+ xml_get_current_line_number($xml_parser),
+ xml_get_current_column_number($xml_parser),
+ xml_get_current_byte_index($xml_parser),
+ $cmd));
+ }
+ else
+ {
+ $vars["error"] = $lang["UNKNOWNREVISION"];
+ return 0;
+ }
+ }
+ }
+ pclose($handle);
+ }
+
+ xml_parser_free($xml_parser);
+ return $curLog;
+ }
+
+ // }}}
+
+}
+
+// {{{ initSvnVersion
+
+function initSvnVersion(&$major, &$minor)
+{
+ global $config;
+
+ $ret = runCommand($config->svn_noparams." --version", false);
+
+ if (preg_match("~([0-9]?)\.([0-9]?)\.([0-9]?)~",$ret[0],$matches))
+ {
+ $major = $matches[1];
+ $minor = $matches[2];
+ }
+
+ $config->setSubversionMajorVersion($major);
+ $config->setSubversionMinorVersion($minor);
+}
+
+// }}}
+
+?>
/WebSVN/include/template.inc
1,306 → 1,306
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// templates.inc
//
// Templating system to allow advanced page customisation
 
$ignore = false;
 
// Stack of previous test results
$ignorestack = array();
 
// Number of test levels currently ignored
$ignorelevel = 0;
 
// parseCommand
//
// Parse a special command
 
function parseCommand($line, $vars, $handle)
{
global $ignore, $ignorestack, $ignorelevel;
// Check for test conditions
if (strncmp(trim($line), "[websvn-test:", 13) == 0)
{
if (!$ignore)
{
$line = trim($line);
$var = substr($line, 13, -1);
if (empty($vars[$var]))
{
array_push($ignorestack, $ignore);
$ignore = true;
}
}
else
{
$ignorelevel++;
}
return true;
}
if (strncmp(trim($line), "[websvn-else]", 13) == 0)
{
if ($ignorelevel == 0)
{
$ignore = !$ignore;
}
return true;
}
 
if (strncmp(trim($line), "[websvn-endtest]", 16) == 0)
{
if ($ignorelevel > 0)
{
$ignorelevel--;
}
else
{
$ignore = array_pop($ignorestack);
}
return true;
}
if (strncmp(trim($line), "[websvn-getlisting]", 19) == 0)
{
global $path, $rev, $svnrep;
if (!$ignore)
$svnrep->listFileContents($path, $rev);
return true;
}
if (strncmp(trim($line), "[websvn-defineicons]", 19) == 0)
{
global $icons;
if (!isset($icons))
$icons = array();
// Read all the lines until we reach the end of the definition, storing
// each one...
if (!$ignore)
{
while (!feof($handle))
{
$line = trim(fgets($handle));
if (strncmp($line, "[websvn-enddefineicons]", 22) == 0)
{
return true;
}
$eqsign = strpos($line, "=");
$match = substr($line, 0, $eqsign);
$def = substr($line, $eqsign + 1);
$icons[$match] = $def;
}
}
return true;
}
 
if (strncmp(trim($line), "[websvn-icon]", 13) == 0)
{
global $icons, $vars;
if (!$ignore)
{
// The current filetype should be defined my $vars["filetype"]
if (!empty($icons[$vars["filetype"]]))
{
echo parseTags($icons[$vars["filetype"]], $vars);
}
else if (!empty($icons["*"]))
{
echo parseTags($icons["*"], $vars);
}
}
return true;
}
 
if (strncmp(trim($line), "[websvn-treenode]", 17) == 0)
{
global $icons, $vars;
if (!$ignore)
{
if ((!empty($icons["i-node"])) && (!empty($icons["t-node"])) && (!empty($icons["l-node"])))
{
for ($n = 1; $n < $vars["level"]; $n++)
{
if ($vars["last_i_node"][$n])
{
echo parseTags($icons["e-node"], $vars);
}
else
{
echo parseTags($icons["i-node"], $vars);
}
}
 
if ($vars["level"] != 0)
{
if ($vars["node"] == 0)
{
echo parseTags($icons["t-node"], $vars);
}
else
{
echo parseTags($icons["l-node"], $vars);
$vars["last_i_node"][$vars["level"]] = TRUE;
}
}
}
}
 
return true;
}
return false;
}
 
// parseTemplate
//
// Parse the given template, replacing the variables with the values passed
 
function parseTemplate($template, $vars, $listing)
{
global $ignore, $vars;
if (!is_file($template))
{
print "No template file found ($template)";
exit;
}
 
$handle = fopen($template, "r");
$inListing = false;
$ignore = false;
$listLines = array();
while (!feof($handle))
{
$line = fgets($handle);
// Check for the end of the file list
if ($inListing)
{
if (strcmp(trim($line), "[websvn-endlisting]") == 0)
{
$inListing = false;
 
// For each item in the list
foreach ($listing as $listvars)
{
// Copy the value for this list item into the $vars array
foreach ($listvars as $id => $value)
{
$vars[$id] = $value;
}
// Output the list item
foreach ($listLines as $line)
{
if (!parseCommand($line, $vars, $handle))
{
if (!$ignore)
print parseTags($line, $vars);
}
}
}
}
else
{
if ($ignore == false)
$listLines[] = $line;
}
}
else if (parseCommand($line, $vars, $handle))
{
continue;
}
else
{
// Check for the start of the file list
if (strncmp(trim($line), "[websvn-startlisting]", 21) == 0)
{
$inListing = true;
}
else
{
if ($ignore == false)
print parseTags($line, $vars);
}
}
}
fclose($handle);
}
 
// parseTags
//
// Replace all occurences of [websvn:varname] with the give variable
 
function parseTags($line, $vars)
{
global $lang;
// Replace the websvn variables
while (ereg("\[websvn:([a-zA-Z0-9_]+)\]", $line, $matches))
{
// Make sure that the variable exists
if (!isset($vars[$matches[1]]))
{
$vars[$matches[1]] = "?${matches[1]}?";
}
$line = str_replace($matches[0], $vars[$matches[1]], $line);
}
// Replace the language strings
while (ereg("\[lang:([a-zA-Z0-9_]+)\]", $line, $matches))
{
// Make sure that the variable exists
if (!isset($lang[$matches[1]]))
{
$lang[$matches[1]] = "?${matches[1]}?";
}
$line = str_replace($matches[0], $lang[$matches[1]], $line);
}
 
// Return the results
return $line;
}
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// templates.inc
//
// Templating system to allow advanced page customisation
 
$ignore = false;
 
// Stack of previous test results
$ignorestack = array();
 
// Number of test levels currently ignored
$ignorelevel = 0;
 
// parseCommand
//
// Parse a special command
 
function parseCommand($line, $vars, $handle)
{
global $ignore, $ignorestack, $ignorelevel;
// Check for test conditions
if (strncmp(trim($line), "[websvn-test:", 13) == 0)
{
if (!$ignore)
{
$line = trim($line);
$var = substr($line, 13, -1);
if (empty($vars[$var]))
{
array_push($ignorestack, $ignore);
$ignore = true;
}
}
else
{
$ignorelevel++;
}
return true;
}
if (strncmp(trim($line), "[websvn-else]", 13) == 0)
{
if ($ignorelevel == 0)
{
$ignore = !$ignore;
}
return true;
}
 
if (strncmp(trim($line), "[websvn-endtest]", 16) == 0)
{
if ($ignorelevel > 0)
{
$ignorelevel--;
}
else
{
$ignore = array_pop($ignorestack);
}
return true;
}
if (strncmp(trim($line), "[websvn-getlisting]", 19) == 0)
{
global $path, $rev, $svnrep;
if (!$ignore)
$svnrep->listFileContents($path, $rev);
return true;
}
if (strncmp(trim($line), "[websvn-defineicons]", 19) == 0)
{
global $icons;
if (!isset($icons))
$icons = array();
// Read all the lines until we reach the end of the definition, storing
// each one...
if (!$ignore)
{
while (!feof($handle))
{
$line = trim(fgets($handle));
if (strncmp($line, "[websvn-enddefineicons]", 22) == 0)
{
return true;
}
$eqsign = strpos($line, "=");
$match = substr($line, 0, $eqsign);
$def = substr($line, $eqsign + 1);
$icons[$match] = $def;
}
}
return true;
}
 
if (strncmp(trim($line), "[websvn-icon]", 13) == 0)
{
global $icons, $vars;
if (!$ignore)
{
// The current filetype should be defined my $vars["filetype"]
if (!empty($icons[$vars["filetype"]]))
{
echo parseTags($icons[$vars["filetype"]], $vars);
}
else if (!empty($icons["*"]))
{
echo parseTags($icons["*"], $vars);
}
}
return true;
}
 
if (strncmp(trim($line), "[websvn-treenode]", 17) == 0)
{
global $icons, $vars;
if (!$ignore)
{
if ((!empty($icons["i-node"])) && (!empty($icons["t-node"])) && (!empty($icons["l-node"])))
{
for ($n = 1; $n < $vars["level"]; $n++)
{
if ($vars["last_i_node"][$n])
{
echo parseTags($icons["e-node"], $vars);
}
else
{
echo parseTags($icons["i-node"], $vars);
}
}
 
if ($vars["level"] != 0)
{
if ($vars["node"] == 0)
{
echo parseTags($icons["t-node"], $vars);
}
else
{
echo parseTags($icons["l-node"], $vars);
$vars["last_i_node"][$vars["level"]] = TRUE;
}
}
}
}
 
return true;
}
return false;
}
 
// parseTemplate
//
// Parse the given template, replacing the variables with the values passed
 
function parseTemplate($template, $vars, $listing)
{
global $ignore, $vars;
if (!is_file($template))
{
print "No template file found ($template)";
exit;
}
 
$handle = fopen($template, "r");
$inListing = false;
$ignore = false;
$listLines = array();
while (!feof($handle))
{
$line = fgets($handle);
// Check for the end of the file list
if ($inListing)
{
if (strcmp(trim($line), "[websvn-endlisting]") == 0)
{
$inListing = false;
 
// For each item in the list
foreach ($listing as $listvars)
{
// Copy the value for this list item into the $vars array
foreach ($listvars as $id => $value)
{
$vars[$id] = $value;
}
// Output the list item
foreach ($listLines as $line)
{
if (!parseCommand($line, $vars, $handle))
{
if (!$ignore)
print parseTags($line, $vars);
}
}
}
}
else
{
if ($ignore == false)
$listLines[] = $line;
}
}
else if (parseCommand($line, $vars, $handle))
{
continue;
}
else
{
// Check for the start of the file list
if (strncmp(trim($line), "[websvn-startlisting]", 21) == 0)
{
$inListing = true;
}
else
{
if ($ignore == false)
print parseTags($line, $vars);
}
}
}
fclose($handle);
}
 
// parseTags
//
// Replace all occurences of [websvn:varname] with the give variable
 
function parseTags($line, $vars)
{
global $lang;
// Replace the websvn variables
while (ereg("\[websvn:([a-zA-Z0-9_]+)\]", $line, $matches))
{
// Make sure that the variable exists
if (!isset($vars[$matches[1]]))
{
$vars[$matches[1]] = "?${matches[1]}?";
}
$line = str_replace($matches[0], $vars[$matches[1]], $line);
}
// Replace the language strings
while (ereg("\[lang:([a-zA-Z0-9_]+)\]", $line, $matches))
{
// Make sure that the variable exists
if (!isset($lang[$matches[1]]))
{
$lang[$matches[1]] = "?${matches[1]}?";
}
$line = str_replace($matches[0], $lang[$matches[1]], $line);
}
 
// Return the results
return $line;
}
?>
/WebSVN/include/utils.inc
1,259 → 1,340
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// utils.inc
//
// General utility commands
 
#require_once 'php5compat.inc';
 
// {{{ createDirLinks
//
// Create a list of links to the current path that'll be available from the template
 
function createDirLinks($rep, $path, $rev, $showchanged)
{
global $vars, $config;
$subs = explode('/', htmlentities($path));
$sofar = "";
$count = count($subs);
$vars["curdirlinks"] = "";
// The number of links depends on the last item. It's empty if
// we're looing at a directory, and full if it's a file
if (empty($subs[$count - 1]))
{
$limit = $count - 2;
$dir = true;
}
else
{
$limit = $count - 1;
$dir = false;
}
for ($n = 0; $n < $limit; $n++)
{
$sofar .= html_entity_decode($subs[$n])."/";
$sofarurl = $config->getURL($rep, $sofar, "dir");
$vars["curdirlinks"] .= "[<a href=\"${sofarurl}rev=$rev&amp;sc=$showchanged\">".$subs[$n]."/]</a> ";
}
if ($dir)
{
$vars["curdirlinks"] .= "[<b>".html_entity_decode($subs[$n])."</b>/]";
}
else
{
$vars["curdirlinks"] .= "[<b>".html_entity_decode($subs[$n])."</b>]";
}
}
 
// }}}
 
// {{{ create_anchors
//
// Create links out of http:// and mailto: tags
 
# TODO: the target="_blank" nonsense should be optional (or specified by the template)
function create_anchors($text)
{
$ret = $text;
 
// Match correctly formed URLs that aren't already links
$ret = preg_replace("#\b(?<!href=\")([a-z]+?)://(\S*)([\w/]+)#i",
"<a href=\"\\1://\\2\\3\" target=\"_blank\">\\1://\\2\\3</a>",
$ret);
// Now match anything beginning with www, as long as it's not //www since they were matched above
$ret = preg_replace("#\b(?<!//)www\.(\S*)([\w/]+)#i",
"<a href=\"http://www.\\1\\2\" target=\"_blank\">www.\\1\\2</a>",
$ret);
 
// Match email addresses
$ret = preg_replace("#\b([\w\-_.]+)@([\w\-.]+)\b#i",
"<a href=\"mailto:\\1@\\2\">\\1@\\2</a>",
$ret);
return ($ret);
}
 
// }}}
 
// {{{ getFullURL
 
function getFullURL($loc)
{
$protocol = 'http';
if (isset($_SERVER["HTTPS"]) && (strtolower($_SERVER["HTTPS"]) != "off"))
{
$protocol = "https";
}
$port = ":".$_SERVER["SERVER_PORT"];
if ((":80" == $port && "http" == $protocol) ||
(":443" == $port && "https" == $protocol))
{
$port = "";
}
if (isset($_SERVER["HTTP_HOST"]))
{
$host = $_SERVER["HTTP_HOST"];
}
else if (isset($_SERVER["SERVER_NAME"]))
{
$host = $_SERVER["SERVER_NAME"].$port;
}
else if (isset($_SERVER["SERVER_ADDR"]))
{
$host = $_SERVER["SERVER_ADDR"].$port;
}
else
{
print "Unable to redirect";
exit;
}
$url = $protocol . "://" . $host . $loc;
 
return $url;
}
 
// }}}
 
// {{{ hardspace
//
// Replace the spaces at the front of a line with hard spaces
 
# XXX: this is an unnecessary function; you can prevent whitespace from being
# trimmed via CSS (use the "white-space: pre;" properties). ~J
# in the meantime, here's an improved function (does nothing)
 
function hardspace($s)
{
return '<code>'.$s.'</code>';
}
 
// }}}
 
// {{{ datetimeFormatDuration
//
// Formats a duration of seconds for display.
//
// $seconds the number of seconds until something
// $nbsp true if spaces should be replaced by nbsp
// $skipSeconds true if seconds should be omitted
//
// return the formatted duration (e.g. @c "8h 6m 1s")
 
function datetimeFormatDuration($seconds, $nbsp = false, $skipSeconds = false)
{
global $lang;
if ($seconds < 0)
{
$seconds = -$seconds;
$neg = true;
}
else
$neg = false;
 
$qty = array();
 
$qty[] = (int)($seconds / (60 * 60 * 24));
$seconds %= 60 * 60 * 24;
 
$qty[] = (int)($seconds / (60 * 60));
$seconds %= 60 * 60;
 
$qty[] = (int)($seconds / 60);
$qty[] = (int)($seconds % 60);
 
$text = "";
$names = $lang["DAYLETTER"].$lang["HOURLETTER"].$lang["MINUTELETTER"];
if (!$skipSeconds) $names .= $lang["SECONDLETTER"];
 
$any = false;
for ($i = 0; $i < strlen($names); $i++)
// If a "higher valued" time slot had a value or this time slot
// has a value or this is the very last entry (i.e. all values
// are 0 and we still want to print seconds)
if ($any || $qty[$i] || $i == sizeof($qty) - 1)
{
$any = true;
 
if ($i)
$text .= sprintf("%2d", $qty[$i]);
else
$text .= $qty[$i];
 
$text .= "{$names{$i}} ";
}
 
$text = trim($text);
if ($neg)
$text = "-$text";
 
return $nbsp ? str_replace(" ", "&nbsp;", $text) : $text;
}
 
// }}}
 
// {{{ getParameterisedSelfUrl
//
// Get the relative URL (PHP_SELF) with GET and POST data
 
function getParameterisedSelfUrl($params = true)
{
$url = null;
 
if ($config->multiViews)
{
// Get rid of the file's name
$url = preg_replace('/\.php/', '', $_SERVER['PHP_SELF'], 1);
}
else
{
$url = basename($_SERVER['PHP_SELF']);
 
// Sometimes the .php isn't on the end. Damn strange...
if (strchr($url, '.') === false)
$url .= '.php';
}
 
if ($params)
{
$arr = $_GET + $_POST;
# XXX: the point of HTTP POST is that URIs have a set size limit, so POST
# data is typically too large to bother with; why include it?
$url .= '?'.htmlentities(http_build_query($arr));
}
 
return $url;
}
 
// }}}
 
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// utils.inc
//
// General utility commands
 
// {{{ createDirLinks
//
// Create a list of links to the current path that'll be available from the template
 
function createDirLinks($rep, $path, $rev, $showchanged)
{
global $vars, $config;
$subs = explode('/', htmlentities($path));
$sofar = "";
$count = count($subs);
$vars["curdirlinks"] = "";
// The number of links depends on the last item. It's empty if
// we're looing at a directory, and full if it's a file
if (empty($subs[$count - 1]))
{
$limit = $count - 2;
$dir = true;
}
else
{
$limit = $count - 1;
$dir = false;
}
for ($n = 0; $n < $limit; $n++)
{
$sofar .= html_entity_decode($subs[$n])."/";
$sofarurl = $config->getURL($rep, $sofar, "dir");
$vars["curdirlinks"] .= "[<a href=\"${sofarurl}rev=$rev&amp;sc=$showchanged\">".$subs[$n]."/]</a> ";
}
if ($dir)
{
$vars["curdirlinks"] .= "[<b>".html_entity_decode($subs[$n])."</b>/]";
}
else
{
$vars["curdirlinks"] .= "[<b>".html_entity_decode($subs[$n])."</b>]";
}
}
 
// }}}
 
// {{{ create_anchors
//
// Create links out of http:// and mailto: tags
 
# TODO: the target="_blank" nonsense should be optional (or specified by the template)
function create_anchors($text)
{
$ret = $text;
 
// Match correctly formed URLs that aren't already links
$ret = preg_replace("#\b(?<!href=\")([a-z]+?)://(\S*)([\w/]+)#i",
"<a href=\"\\1://\\2\\3\" target=\"_blank\">\\1://\\2\\3</a>",
$ret);
// Now match anything beginning with www, as long as it's not //www since they were matched above
$ret = preg_replace("#\b(?<!//)www\.(\S*)([\w/]+)#i",
"<a href=\"http://www.\\1\\2\" target=\"_blank\">www.\\1\\2</a>",
$ret);
 
// Match email addresses
$ret = preg_replace("#\b([\w\-_.]+)@([\w\-.]+)\b#i",
"<a href=\"mailto:\\1@\\2\">\\1@\\2</a>",
$ret);
return ($ret);
}
 
// }}}
 
// {{{ getFullURL
 
function getFullURL($loc)
{
$protocol = 'http';
if (isset($_SERVER["HTTPS"]) && (strtolower($_SERVER["HTTPS"]) != "off"))
{
$protocol = "https";
}
$port = ":".$_SERVER["SERVER_PORT"];
if ((":80" == $port && "http" == $protocol) ||
(":443" == $port && "https" == $protocol))
{
$port = "";
}
if (isset($_SERVER["HTTP_HOST"]))
{
$host = $_SERVER["HTTP_HOST"];
}
else if (isset($_SERVER["SERVER_NAME"]))
{
$host = $_SERVER["SERVER_NAME"].$port;
}
else if (isset($_SERVER["SERVER_ADDR"]))
{
$host = $_SERVER["SERVER_ADDR"].$port;
}
else
{
print "Unable to redirect";
exit;
}
$url = $protocol . "://" . $host . $loc;
 
return $url;
}
 
// }}}
 
// {{{ hardspace
//
// Replace the spaces at the front of a line with hard spaces
 
# XXX: this is an unnecessary function; you can prevent whitespace from being
# trimmed via CSS (use the "white-space: pre;" properties). ~J
# in the meantime, here's an improved function (does nothing)
 
function hardspace($s)
{
return '<code>' . expandTabs($s) . '</code>';
}
 
// }}}
 
// {{{ expandTabs
 
/**
* Expands the tabs in a line that may or may not include HTML.
*
* Enscript generates code with HTML, so we need to take that into account.
*
* @param string $s Line of possibly HTML-encoded text to expand
* @param int $tabwidth Tab width, -1 to use repository's default, 0 to collapse
* all tabs.
* @return string The expanded line.
* @since 2.1
*/
 
function expandTabs($s, $tabwidth = -1)
{
global $rep;
 
if ($tabwidth == -1)
$tabwidth = $rep->getExpandTabsBy();
$pos = 0;
 
// Parse the string into chunks that are either 1 of: HTML tag, tab char, run of any other stuff
$chunks = preg_split("/((?:<.+?>)|(?:&.+?;)|(?:\t))/", $s, -1, PREG_SPLIT_DELIM_CAPTURE);
 
// Count the sizes of the chunks and replace tabs as we go
for ($i = 0; $i < count($chunks); $i++)
{
# make sure we're not dealing with an empty string
if (empty($chunks[$i])) continue;
switch ($chunks[$i]{0})
{
case '<': // HTML tag : ignore its width by doing nothing
break;
 
case '&': // HTML entity: count its width as 1 char
$pos += 1;
break;
 
case "\t": // Tab char: replace it with a run of spaces between length tabwidth and 1
$tabsize = $tabwidth - ($pos % $tabwidth);
$chunks[$i] = str_repeat(' ', $tabsize);
$pos += $tabsize;
break;
 
default: // Anything else: just keep track of its width
$pos += strlen($chunks[$i]);
break;
}
}
 
// Put the chunks back together and we've got the original line, detabbed.
return join('', $chunks);
}
 
// }}}
 
// {{{ datetimeFormatDuration
//
// Formats a duration of seconds for display.
//
// $seconds the number of seconds until something
// $nbsp true if spaces should be replaced by nbsp
// $skipSeconds true if seconds should be omitted
//
// return the formatted duration (e.g. @c "8h 6m 1s")
 
function datetimeFormatDuration($seconds, $nbsp = false, $skipSeconds = false)
{
global $lang;
if ($seconds < 0)
{
$seconds = -$seconds;
$neg = true;
}
else
$neg = false;
 
$qty = array();
 
$qty[] = (int)($seconds / (60 * 60 * 24));
$seconds %= 60 * 60 * 24;
 
$qty[] = (int)($seconds / (60 * 60));
$seconds %= 60 * 60;
 
$qty[] = (int)($seconds / 60);
$qty[] = (int)($seconds % 60);
 
$text = "";
$names = $lang["DAYLETTER"].$lang["HOURLETTER"].$lang["MINUTELETTER"];
if (!$skipSeconds) $names .= $lang["SECONDLETTER"];
 
$any = false;
for ($i = 0; $i < strlen($names); $i++)
// If a "higher valued" time slot had a value or this time slot
// has a value or this is the very last entry (i.e. all values
// are 0 and we still want to print seconds)
if ($any || $qty[$i] || $i == sizeof($qty) - 1)
{
$any = true;
 
if ($i)
$text .= sprintf("%2d", $qty[$i]);
else
$text .= $qty[$i];
 
$text .= "{$names{$i}} ";
}
 
$text = trim($text);
if ($neg)
$text = "-$text";
 
return $nbsp ? str_replace(" ", "&nbsp;", $text) : $text;
}
 
// }}}
 
// {{{ getParameterisedSelfUrl
//
// Get the relative URL (PHP_SELF) with GET and POST data
 
function getParameterisedSelfUrl($params = true)
{
global $config;
 
$url = null;
 
if ($config->multiViews)
{
// Get rid of the file's name
$url = preg_replace('/\.php/', '', $_SERVER['PHP_SELF'], 1);
}
else
{
$url = basename($_SERVER['PHP_SELF']);
 
// Sometimes the .php isn't on the end. Damn strange...
if (strchr($url, '.') === false)
$url .= '.php';
}
 
if ($params)
{
$arr = $_GET + $_POST;
# XXX: the point of HTTP POST is that URIs have a set size limit, so POST
# data is typically too large to bother with; why include it?
$url .= '?'.htmlentities(http_build_query($arr));
}
 
return $url;
}
 
// }}}
 
// {{{ getUserLanguage
 
function getUserLanguage() {
$languages = !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : 'en';
if (strpos($languages, ',') === false) return $languages; # only one specified
# split off the languages into an array of languages and q-values
$qvals = array();
$langs = array();
preg_match_all('#(\S+?)\s*(?:;q=([01](?:\.\d{1,3})?))?\s*,\s*#', $languages, $qvals, PREG_SET_ORDER);
foreach ($qvals as $q) {
$langs[] = array (
$q[1],
floatval(!empty($q[2]) ? $q[2] : 1.0)
);
}
# XXX: now, we should loop through our available languages and choose an
# appropriate one for the user
# note that we shouldn't match the region unless we have a specific region
# to use (e.g. zh-CN vs. zh-TW)
# FIXME: see above; otherwise, this function doesn't really do anything
}
 
// }}}
 
?>
/WebSVN/include/version.inc
1,28 → 1,28
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// version.inc
//
// Version information
 
$version = '2.1 alpha 1';
?>
<?php
# vim:et:ts=3:sts=3:sw=3:fdm=marker:
 
// WebSVN - Subversion repository viewing via the web using PHP
// Copyright © 2004-2006 Tim Armes, Matt Sicker
//
// 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
//
// --
//
// version.inc
//
// Version information
 
$version = '2.1 alpha 1';
?>