1,122 → 1,326 |
<?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 |
// |
// -- |
// |
// dl.php |
// |
// Create gz/tar files of the requested item |
|
require_once("include/setup.inc"); |
require_once("include/svnlook.inc"); |
require_once("include/utils.inc"); |
|
// Make sure that this operation is allowed |
|
if (!$rep->isDownloadAllowed($path)) |
exit; |
|
$svnrep = new SVNRepository($rep); |
|
if ($path{0} != "/") |
$ppath = "/".$path; |
else |
$ppath = $path; |
|
// If there's no revision info, go to the lastest revision for this path |
$history = $svnrep->getLog($path, "", "", true); |
$youngest = $history->entries[0]->rev; |
|
if (empty($rev)) |
$rev = $youngest; |
|
// Create a temporary directory. Here we have an unavoidable but highly |
// unlikely to occure race condition |
|
if (count(glob("/tmp/wsvn-*"))<=40) // Omezeni na pocet soucasne stahovanych souboru |
{ |
|
// Ošetření doběhnutí skriptu i když klient klikne jinam (smazání tmp) |
ignore_user_abort(true); |
|
$tmpname1 = tempnam("temp", "wsvn-"); |
$tmpname = $tmpname1; |
$tmpname .= "dir"; |
if (mkdir($tmpname)) |
{ |
// Get the name of the directory being archived |
$arcname = substr($path, 0, -1); |
$arcname = basename($arcname); |
if (empty($arcname)) |
$arcname = $rep->name; |
|
$svnrep->exportDirectory($path, $tmpname.DIRECTORY_SEPARATOR.$arcname, $rev); |
|
// ZIP it up |
chdir($tmpname); |
if (! connection_aborted()) |
exec("zip -r ".quote("$arcname")." ."); |
|
$size = filesize("$arcname.zip"); |
|
// Give the file to the browser |
|
if (!connection_aborted() && $fp = @fopen("$arcname.zip","rb")) |
{ |
header("Content-Type: application/zip"); |
header("Content-Length: $size"); |
header("Content-Disposition: attachment; filename=\"".$rep->name."-$arcname.zip\""); |
// Use a loop to transfer the data 4KB at a time. |
while(!feof($fp)) |
{ |
echo fread($fp, 4096); |
ob_flush(); |
} |
} |
else |
{ |
print "Unable to open file $arcname.zip"; |
} |
|
fclose($fp); |
|
chdir(".."); |
|
// Delete the directory. Why doesn't PHP have a generic recursive directory |
// deletion command? It's stupid. |
|
// if ($config->serverIsWindows) |
// { |
// $cmd = quoteCommand("rmdir /S /Q ".quote($tmpname), false); |
// } |
// else |
// { |
$cmd = quoteCommand("rm -fr ".quote($tmpname), false); |
// } |
|
@exec($cmd); |
} |
unlink($tmpname1); // Pomocny soubor smazeme az po smazani adresare |
} |
else |
{ |
print "We are sorry. The server is overloaded..."; |
} |
?> |
<?php |
// WebSVN - Subversion repository viewing via the web using PHP |
// Copyright (C) 2004-2006 Tim Armes |
// |
// This program is free software; you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation; either version 2 of the License, or |
// (at your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public License |
// along with this program; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// |
// -- |
// |
// dl.php |
// |
// Allow for file and directory downloads, creating zip/tar/gz files if needed. |
|
require_once 'include/setup.php'; |
require_once 'include/svnlook.php'; |
require_once 'include/utils.php'; |
|
if (!defined('USE_AUTOLOADER')) { |
@include_once 'Archive/Tar.php'; |
} |
|
function setDirectoryTimestamp($dir, $timestamp) { |
global $config; |
// Changing the timestamp of a directory in Windows is only supported in PHP 5.3.0+ |
touch($dir, $timestamp); |
if (is_dir($dir)) { |
// Set timestamp for all contents, recursing into subdirectories |
$handle = opendir($dir); |
if ($handle) { |
while (($file = readdir($handle)) !== false) { |
if ($file == '.' || $file == '..') { |
continue; |
} |
$f = $dir.DIRECTORY_SEPARATOR.$file; |
if (is_dir($f)) { |
setDirectoryTimestamp($f, $timestamp); |
} |
} |
closedir($handle); |
} |
} |
} |
|
// Make sure that downloading the specified file/directory is permitted |
|
if (!$rep) |
{ |
http_response_code(404); |
exit; |
} |
|
if (!$rep->isDownloadAllowed($path)) { |
http_response_code(403); |
error_log('Unable to download resource at path: '.$path); |
print 'Unable to download resource at path: '.xml_entities($path); |
exit; |
} |
|
$svnrep = new SVNRepository($rep); |
|
// Fetch information about a revision (if unspecified, the latest) for this path |
if (empty($rev)) |
{ |
$history = $svnrep->getLog($path, 'HEAD', '', true, 1, $peg); |
} |
else if ($rev == $peg) |
{ |
$history = $svnrep->getLog($path, '', 1, true, 1, $peg); |
} |
else |
{ |
$history = $svnrep->getLog($path, $rev, $rev - 1, true, 1, $peg); |
} |
|
$logEntry = ($history && !empty($history->entries)) ? $history->entries[0] : null; |
|
if (!$logEntry) |
{ |
http_response_code(404); |
error_log('Unable to download resource at path: '.$path); |
print 'Unable to download resource at path: '.xml_entities($path); |
exit(0); |
} |
|
if (empty($rev)) |
{ |
$rev = $logEntry->rev; |
} |
|
// Create a temporary filename to be used for a directory to archive a download. |
// Here we have an unavoidable but highly unlikely to occur race condition. |
$tempDir = tempnamWithCheck($config->getTempDir(), 'websvn'); |
|
@unlink($tempDir); |
mkdir($tempDir); |
// Create the name of the directory being archived |
$archiveName = $path; |
$isDir = (substr($archiveName, -1) == '/'); |
|
if ($isDir) |
{ |
$archiveName = substr($archiveName, 0, -1); |
} |
|
$archiveName = basename($archiveName); |
|
if ($archiveName == '') |
{ |
$archiveName = $rep->name; |
} |
|
$plainfilename = $archiveName; |
$archiveName .= '.r'.$rev; |
|
// Export the requested path from SVN repository to the temp directory |
$svnExportResult = $svnrep->exportRepositoryPath($path, $tempDir.DIRECTORY_SEPARATOR.$archiveName, $rev, $peg); |
|
if ($svnExportResult != 0) |
{ |
http_response_code(500); |
error_log('svn export failed for: '.$archiveName); |
print 'svn export failed for "'.xml_entities($archiveName).'".'; |
removeDirectory($tempDir); |
exit(0); |
} |
|
// For security reasons, disallow direct downloads of filenames that |
// are a symlink, since they may be a symlink to anywhere (/etc/passwd) |
// Deciding whether the symlink is relative and legal within the |
// repository would be nice but seems to error prone at this moment. |
if ( is_link($tempDir.DIRECTORY_SEPARATOR.$archiveName) ) |
{ |
http_response_code(500); |
error_log('to be downloaded file is symlink, aborting: '.$archiveName); |
print 'Download of symlinks disallowed: "'.xml_entities($archiveName).'".'; |
removeDirectory($tempDir); |
exit(0); |
} |
|
// Set timestamp of exported directory (and subdirectories) to timestamp of |
// the revision so every archive of a given revision has the same timestamp. |
$revDate = $logEntry->date; |
$timestamp = mktime(substr($revDate, 11, 2), // hour |
substr($revDate, 14, 2), // minute |
substr($revDate, 17, 2), // second |
substr($revDate, 5, 2), // month |
substr($revDate, 8, 2), // day |
substr($revDate, 0, 4)); // year |
setDirectoryTimestamp($tempDir, $timestamp); |
|
// Change to temp directory so that only relative paths are stored in archive. |
$oldcwd = getcwd(); |
chdir($tempDir); |
|
if ($isDir) |
{ |
$downloadMode = $config->getDefaultDirectoryDlMode(); |
} |
else |
{ |
$downloadMode = $config->getDefaultFileDlMode(); |
} |
|
// $_REQUEST parameter can override dlmode |
if (!empty($_REQUEST['dlmode'])) |
{ |
$downloadMode = $_REQUEST['dlmode']; |
|
if (substr($logEntry->path, -1) == '/') |
{ |
if (!in_array($downloadMode, $config->validDirectoryDlModes)) |
{ |
$downloadMode = $config->getDefaultDirectoryDlMode(); |
} |
} |
else |
{ |
if (!in_array($downloadMode, $config->validFileDlModes)) |
{ |
$downloadMode = $config->getDefaultFileDlMode(); |
} |
} |
} |
|
$downloadArchive = $archiveName; |
|
if ($downloadMode == 'plain') |
{ |
$downloadMimeType = 'application/octet-stream'; |
|
} |
else if ($downloadMode == 'zip') |
{ |
$downloadMimeType = 'application/zip'; |
$downloadArchive .= '.zip'; |
// Create zip file |
$cmd = $config->zip.' --symlinks -r '.quote($downloadArchive).' '.quote($archiveName); |
execCommand($cmd, $retcode); |
|
if ($retcode != 0) |
{ |
error_log('Unable to call zip command: '.$cmd); |
print 'Unable to call zip command. See webserver error log for details.'; |
} |
} |
else |
{ |
$downloadMimeType = 'application/gzip'; |
$downloadArchive .= '.tar.gz'; |
$tarArchive = $archiveName.'.tar'; |
|
// Create the tar file |
$retcode = 0; |
|
if (class_exists('Archive_Tar')) |
{ |
$tar = new Archive_Tar($tarArchive); |
$created = $tar->create(array($archiveName)); |
|
if (!$created) |
{ |
$retcode = 1; |
http_response_code(500); |
print 'Unable to create tar archive.'; |
} |
} |
else |
{ |
$cmd = $config->tar.' -cf '.quote($tarArchive).' '.quote($archiveName); |
execCommand($cmd, $retcode); |
|
if ($retcode != 0) |
{ |
http_response_code(500); |
error_log('Unable to call tar command: '.$cmd); |
print 'Unable to call tar command. See webserver error log for details.'; |
} |
} |
|
if ($retcode != 0) |
{ |
chdir($oldcwd); |
removeDirectory($tempDir); |
exit(0); |
} |
|
// Set timestamp of tar file to timestamp of revision |
touch($tarArchive, $timestamp); |
|
// GZIP it up |
if (function_exists('gzopen')) |
{ |
$srcHandle = fopen($tarArchive, 'rb'); |
$dstHandle = gzopen($downloadArchive, 'wb'); |
|
if (!$srcHandle || !$dstHandle) |
{ |
http_response_code(500); |
print 'Unable to open file for gz-compression.'; |
chdir($oldcwd); |
removeDirectory($tempDir); |
exit(0); |
} |
|
while (!feof($srcHandle)) |
{ |
gzwrite($dstHandle, fread($srcHandle, 1024 * 512)); |
} |
|
fclose($srcHandle); |
gzclose($dstHandle); |
} |
else |
{ |
$cmd = $config->gzip.' '.quote($tarArchive); |
$retcode = 0; |
execCommand($cmd, $retcode); |
|
if ($retcode != 0) |
{ |
http_response_code(500); |
error_log('Unable to call gzip command: '.$cmd); |
print 'Unable to call gzip command. See webserver error log for details.'; |
chdir($oldcwd); |
removeDirectory($tempDir); |
exit(0); |
} |
} |
} |
|
// Give the file to the browser |
if (is_readable($downloadArchive)) |
{ |
if ($downloadMode == 'plain') |
{ |
$downloadFilename = $plainfilename; |
} |
else |
{ |
$downloadFilename = $rep->name.'-'.$downloadArchive; |
} |
|
header('Content-Type: '.$downloadMimeType); |
header('Content-Length: '.filesize($downloadArchive)); |
header("Content-Disposition: attachment; filename*=UTF-8''".rawurlencode($downloadFilename)); |
readfile($downloadArchive); |
} |
else |
{ |
http_response_code(404); |
print 'Unable to open file: '.xml_entities($downloadArchive); |
} |
|
chdir($oldcwd); |
removeDirectory($tempDir); |