1 | 1 | simandl | <?php |
2 | | | // WebSVN - Subversion repository viewing via the web using PHP |
3 | | | // Copyright (C) 2004-2006 Tim Armes |
4 | | | // |
5 | | | // This program is free software; you can redistribute it and/or modify |
6 | | | // it under the terms of the GNU General Public License as published by |
7 | | | // the Free Software Foundation; either version 2 of the License, or |
8 | | | // (at your option) any later version. |
9 | | | // |
10 | | | // This program is distributed in the hope that it will be useful, |
11 | | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | | // GNU General Public License for more details. |
14 | | | // |
15 | | | // You should have received a copy of the GNU General Public License |
16 | | | // along with this program; if not, write to the Free Software |
17 | | | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | | | // |
19 | | | // -- |
20 | | | // |
21 | | | // dl.php |
22 | | | // |
23 | | | // Create gz/tar files of the requested item |
24 | | | |
25 | | | require_once("include/setup.php"); |
26 | | | require_once("include/svnlook.php"); |
27 | | | require_once("include/utils.php"); |
28 | | | |
29 | 3 | simandl | ini_set('include_path', $locwebsvnreal.'/lib/pear'.$config->pathSeparator.ini_get('include_path')); |
30 | | | @include_once("Archive/Tar.php"); |
31 | | | |
32 | | | function setDirectoryTimestamp($dir, $ts) { |
33 | | | global $config; |
34 | | | // changing the modification time of a directory under windows is only supported since php 5.3.0 |
35 | | | if (!$config->serverIsWindows || version_compare(PHP_VERSION, '5.3.0alpha') !== -1) { |
36 | | | touch($dir, $ts); |
37 | | | |
38 | | | $handle = opendir($dir); |
39 | | | if ($handle) { |
40 | | | while (($file = readdir($handle)) !== false) { |
41 | | | if ($file == '.' || $file == '..') { |
42 | | | continue; |
43 | | | } |
44 | | | $f = $dir.DIRECTORY_SEPARATOR.$file; |
45 | | | if (is_dir($f)) { |
46 | | | setDirectoryTimestamp($f, $ts); |
47 | | | } |
48 | | | } |
49 | | | closedir($handle); |
50 | | | } |
51 | | | } |
52 | | | } |
53 | | | |
54 | 1 | simandl | function removeDirectory($dir) { |
55 | | | if (is_dir($dir)) { |
56 | | | $dir = rtrim($dir, '/'); |
57 | | | $handle = dir($dir); |
58 | | | while (($file = $handle->read()) !== false) { |
59 | | | if ($file == '.' || $file == '..') { |
60 | | | continue; |
61 | | | } |
62 | | | |
63 | | | $f = $dir.DIRECTORY_SEPARATOR.$file; |
64 | | | if (!is_link($f) && is_dir($f)) { |
65 | | | removeDirectory($f); |
66 | | | } else { |
67 | | | @unlink($f); |
68 | | | } |
69 | | | } |
70 | | | $handle->close(); |
71 | | | @rmdir($dir); |
72 | | | return true; |
73 | | | } |
74 | | | return false; |
75 | | | } |
76 | | | |
77 | | | // Make sure that this operation is allowed |
78 | | | |
79 | | | if (!$rep->isDownloadAllowed($path)) { |
80 | 3 | simandl | header('HTTP/1.x 403 Forbidden', true, 403); |
81 | | | print 'Unable to download path '.$path."\n"; |
82 | 1 | simandl | exit; |
83 | | | } |
84 | | | |
85 | | | $svnrep = new SVNRepository($rep); |
86 | | | |
87 | | | // Fetch information about latest revision for this path |
88 | | | if (empty($rev)) { |
89 | | | $history = $svnrep->getLog($path, 'HEAD', '', true, 1); |
90 | | | } else { |
91 | | | $history = $svnrep->getLog($path, $rev, $rev - 1, true, 1); |
92 | | | } |
93 | 3 | simandl | if (is_string($history)) { |
94 | | | echo $history; |
95 | | | exit; |
96 | | | } |
97 | 1 | simandl | $logEntry = $history->entries[0]; |
98 | | | |
99 | | | if (empty($rev)) { |
100 | | | $rev = $logEntry->rev; |
101 | | | } |
102 | | | |
103 | | | // Create a temporary directory. Here we have an unavoidable but highly |
104 | | | // unlikely to occure race condition |
105 | | | |
106 | | | $tmpname = tempnam($config->getTarballTmpDir(), 'wsvn'); |
107 | | | @unlink($tmpname); |
108 | | | |
109 | | | if (mkdir($tmpname)) { |
110 | | | // Get the name of the directory being archived |
111 | | | $arcname = $path; |
112 | 3 | simandl | $isDir = (substr($arcname, -1) == '/'); |
113 | | | if ($isDir) { |
114 | 1 | simandl | $arcname = substr($arcname, 0, -1); |
115 | | | } |
116 | | | $arcname = basename($arcname); |
117 | | | if ($arcname == '') { |
118 | | | $arcname = $rep->name; |
119 | | | } |
120 | | | |
121 | 3 | simandl | $plainfilename = $arcname; |
122 | 1 | simandl | $arcname = $arcname.'.r'.$rev; |
123 | | | |
124 | | | $svnrep->exportDirectory($path, $tmpname.DIRECTORY_SEPARATOR.$arcname, $rev); |
125 | | | |
126 | 3 | simandl | // Set datetime of exported directory (and subdirectories) to datetime of revision so that every archive is equal |
127 | | | $date = $logEntry->date; |
128 | | | $ts = mktime(substr($date, 11, 2), substr($date, 14, 2), substr($date, 17, 2), substr($date, 5, 2), substr($date, 8, 2), substr($date, 0, 4)); |
129 | | | setDirectoryTimestamp($tmpname.DIRECTORY_SEPARATOR.$arcname, $ts); |
130 | | | |
131 | 1 | simandl | // change to temp directory so that only relative paths are stored in tar |
132 | | | chdir($tmpname); |
133 | | | |
134 | 3 | simandl | if ($isDir) { |
135 | | | $dlmode = $config->getDefaultFolderDlMode(); |
136 | | | } else { |
137 | | | $dlmode = $config->getDefaultFileDlMode(); |
138 | | | } |
139 | 1 | simandl | |
140 | 3 | simandl | // $_REQUEST parameter can override dlmode |
141 | | | if (!empty($_REQUEST['dlmode'])) { |
142 | | | $dlmode = $_REQUEST['dlmode']; |
143 | | | if (substr($logEntry->path, -1) == '/') { |
144 | | | if (!in_array($dlmode, $config->validFolderDlModes)) { |
145 | | | $dlmode = $config->getDefaultFolderDlMode(); |
146 | | | } |
147 | | | } else { |
148 | | | if (!in_array($dlmode, $config->validFileDlModes)) { |
149 | | | $dlmode = $config->getDefaultFileDlMode(); |
150 | | | } |
151 | | | } |
152 | | | } |
153 | 1 | simandl | |
154 | 3 | simandl | if ($dlmode == 'plain') { |
155 | | | $dlarc = $arcname; |
156 | | | $dlmime = 'application/octetstream'; |
157 | 1 | simandl | |
158 | 3 | simandl | } else if ($dlmode == 'zip') { |
159 | | | $dlarc = $arcname.'.zip'; |
160 | | | $dlmime = 'application/x-zip'; |
161 | | | // Create zip file |
162 | | | $cmd = $config->zip.' -r '.quote($dlarc).' '.quote($arcname); |
163 | | | execCommand($cmd, $retcode); |
164 | | | if ($retcode != 0) { |
165 | | | print'Unable to call zip command "'.$config->zip.'"'; |
166 | | | } |
167 | 1 | simandl | |
168 | 3 | simandl | } else { |
169 | | | $tararc = $arcname.'.tar'; |
170 | | | $dlarc = $arcname.'.tar.gz'; |
171 | | | $dlmime = 'application/x-gzip'; |
172 | | | |
173 | | | // Create the tar file |
174 | | | $retcode = 0; |
175 | | | if (class_exists('Archive_Tar')) { |
176 | | | $tar = new Archive_Tar($tararc); |
177 | | | $created = $tar->create($arcname); |
178 | | | if (!$created) { |
179 | | | $retcode = 1; |
180 | | | print'Unable to create tar archive'; |
181 | | | } |
182 | | | |
183 | | | } else { |
184 | | | $cmd = $config->tar.' -cf '.quote($tararc).' '.quote($arcname); |
185 | | | execCommand($cmd, $retcode); |
186 | | | if ($retcode != 0) { |
187 | | | print'Unable to call tar command "'.$config->tar.'"'; |
188 | | | } |
189 | | | } |
190 | | | if ($retcode != 0) { |
191 | | | chdir('..'); |
192 | | | removeDirectory($tmpname); |
193 | | | exit(0); |
194 | | | } |
195 | | | |
196 | | | // Set datetime of tar file to datetime of revision |
197 | | | touch($tararc, $ts); |
198 | | | |
199 | | | // GZIP it up |
200 | | | if (function_exists('gzopen')) { |
201 | | | $srcHandle = fopen($tmpname.DIRECTORY_SEPARATOR.$tararc, 'rb'); |
202 | | | $dstHandle = gzopen($tmpname.DIRECTORY_SEPARATOR.$dlarc, 'wb'); |
203 | | | if (!$srcHandle || !$dstHandle) { |
204 | | | print'Unable to open file for gz-compression'; |
205 | | | chdir('..'); |
206 | | | removeDirectory($tmpname); |
207 | | | exit(0); |
208 | | | } |
209 | | | while (!feof($srcHandle)) { |
210 | | | gzwrite($dstHandle, fread($srcHandle, 1024 * 512)); |
211 | | | } |
212 | | | fclose($srcHandle); |
213 | | | gzclose($dstHandle); |
214 | | | |
215 | | | } else { |
216 | | | $cmd = $config->gzip.' '.quote($tararc); |
217 | | | $retcode = 0; |
218 | | | execCommand($cmd, $retcode); |
219 | | | if ($retcode != 0) { |
220 | | | print'Unable to call gzip command "'.$config->gzip.'"'; |
221 | | | chdir('..'); |
222 | | | removeDirectory($tmpname); |
223 | | | exit(0); |
224 | | | } |
225 | | | } |
226 | | | } |
227 | | | |
228 | 1 | simandl | // Give the file to the browser |
229 | 3 | simandl | if (is_readable($dlarc)) { |
230 | | | $size = filesize($dlarc); |
231 | 1 | simandl | |
232 | 3 | simandl | if ($dlmode == 'plain') { |
233 | | | $dlfilename = $plainfilename; |
234 | | | } else { |
235 | | | $dlfilename = $rep->name.'-'.$dlarc; |
236 | | | } |
237 | | | |
238 | | | header('Content-Type: '.$dlmime); |
239 | 1 | simandl | header('Content-Length: '.$size); |
240 | 3 | simandl | header('Content-Disposition: attachment; filename="'. $dlfilename .'"'); |
241 | 1 | simandl | |
242 | 3 | simandl | readfile($dlarc); |
243 | | | |
244 | 1 | simandl | } else { |
245 | 3 | simandl | header('HTTP/1.x 404 Not Found', true, 404); |
246 | | | |
247 | | | print 'Unable to open file '.$dlarc."\n"; |
248 | 1 | simandl | } |
249 | | | |
250 | | | chdir('..'); |
251 | | | |
252 | | | removeDirectory($tmpname); |
253 | | | } |