This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of Zippy. |
||
5 | * |
||
6 | * (c) Alchemy <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | namespace Alchemy\Zippy\Adapter; |
||
13 | |||
14 | use Alchemy\Zippy\Adapter\Resource\ResourceInterface; |
||
15 | use Alchemy\Zippy\Adapter\Resource\ZipArchiveResource; |
||
16 | use Alchemy\Zippy\Adapter\VersionProbe\ZipExtensionVersionProbe; |
||
17 | use Alchemy\Zippy\Archive\Archive; |
||
18 | use Alchemy\Zippy\Archive\Member; |
||
19 | use Alchemy\Zippy\Exception\NotSupportedException; |
||
20 | use Alchemy\Zippy\Exception\RuntimeException; |
||
21 | use Alchemy\Zippy\Exception\InvalidArgumentException; |
||
22 | use Alchemy\Zippy\Resource\Resource as ZippyResource; |
||
23 | use Alchemy\Zippy\Resource\ResourceManager; |
||
24 | |||
25 | /** |
||
26 | * ZipExtensionAdapter allows you to create and extract files from archives |
||
27 | * using PHP Zip extension |
||
28 | * |
||
29 | * @see http://www.php.net/manual/en/book.zip.php |
||
30 | */ |
||
31 | class ZipExtensionAdapter extends AbstractAdapter |
||
32 | { |
||
33 | private $errorCodesMapping = array( |
||
34 | \ZipArchive::ER_EXISTS => "File already exists", |
||
35 | \ZipArchive::ER_INCONS => "Zip archive inconsistent", |
||
36 | \ZipArchive::ER_INVAL => "Invalid argument", |
||
37 | \ZipArchive::ER_MEMORY => "Malloc failure", |
||
38 | \ZipArchive::ER_NOENT => "No such file", |
||
39 | \ZipArchive::ER_NOZIP => "Not a zip archive", |
||
40 | \ZipArchive::ER_OPEN => "Can't open file", |
||
41 | \ZipArchive::ER_READ => "Read error", |
||
42 | \ZipArchive::ER_SEEK => "Seek error" |
||
43 | ); |
||
44 | |||
45 | public function __construct(ResourceManager $manager) |
||
46 | { |
||
47 | parent::__construct($manager); |
||
48 | $this->probe = new ZipExtensionVersionProbe(); |
||
49 | } |
||
50 | |||
51 | /** |
||
52 | * @inheritdoc |
||
53 | */ |
||
54 | protected function doListMembers(ResourceInterface $resource) |
||
55 | { |
||
56 | $members = array(); |
||
57 | for ($i = 0; $i < $resource->getResource()->numFiles; $i++) { |
||
58 | $stat = $resource->getResource()->statIndex($i); |
||
59 | $members[] = new Member( |
||
60 | $resource, |
||
61 | $this, |
||
62 | $stat['name'], |
||
63 | $stat['size'], |
||
64 | new \DateTime('@' . $stat['mtime']), |
||
65 | 0 === strlen($resource->getResource()->getFromIndex($i, 1)) |
||
66 | ); |
||
67 | } |
||
68 | |||
69 | return $members; |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * @inheritdoc |
||
74 | */ |
||
75 | public static function getName() |
||
76 | { |
||
77 | return 'zip-extension'; |
||
78 | } |
||
79 | |||
80 | /** |
||
81 | * @inheritdoc |
||
82 | */ |
||
83 | protected function doExtract(ResourceInterface $resource, $to) |
||
84 | { |
||
85 | return $this->extractMembers($resource, null, $to); |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * @inheritdoc |
||
90 | */ |
||
91 | protected function doExtractMembers(ResourceInterface $resource, $members, $to, $overwrite = false) |
||
92 | { |
||
93 | if (null === $to) { |
||
94 | // if no destination is given, will extract to zip current folder |
||
95 | $to = dirname(realpath($resource->getResource()->filename)); |
||
96 | } |
||
97 | |||
98 | if (!is_dir($to)) { |
||
99 | $resource->getResource()->close(); |
||
100 | throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); |
||
101 | } |
||
102 | |||
103 | if (!is_writable($to)) { |
||
104 | $resource->getResource()->close(); |
||
105 | throw new InvalidArgumentException(sprintf("%s is not writable", $to)); |
||
106 | } |
||
107 | |||
108 | if (null !== $members) { |
||
109 | $membersTemp = (array) $members; |
||
110 | if (empty($membersTemp)) { |
||
111 | $resource->getResource()->close(); |
||
112 | |||
113 | throw new InvalidArgumentException("no members provided"); |
||
114 | } |
||
115 | $members = array(); |
||
116 | // allows $members to be an array of strings or array of Members |
||
117 | foreach ($membersTemp as $member) { |
||
118 | if ($member instanceof Member) { |
||
119 | $member = $member->getLocation(); |
||
120 | } |
||
121 | |||
122 | if ($resource->getResource()->locateName($member) === false) { |
||
123 | $resource->getResource()->close(); |
||
124 | |||
125 | throw new InvalidArgumentException(sprintf('%s is not in the zip file', $member)); |
||
126 | } |
||
127 | |||
128 | if ($overwrite == false) { |
||
0 ignored issues
–
show
|
|||
129 | if (file_exists($member)) { |
||
130 | $resource->getResource()->close(); |
||
131 | |||
132 | throw new RuntimeException('Target file ' . $member . ' already exists.'); |
||
133 | } |
||
134 | } |
||
135 | |||
136 | $members[] = $member; |
||
137 | } |
||
138 | } |
||
139 | |||
140 | if (!$resource->getResource()->extractTo($to, $members)) { |
||
141 | $resource->getResource()->close(); |
||
142 | |||
143 | throw new InvalidArgumentException(sprintf('Unable to extract archive : %s', $resource->getResource()->getStatusString())); |
||
144 | } |
||
145 | |||
146 | return new \SplFileInfo($to); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * @inheritdoc |
||
151 | */ |
||
152 | protected function doRemove(ResourceInterface $resource, $files) |
||
153 | { |
||
154 | $files = (array) $files; |
||
155 | |||
156 | if (empty($files)) { |
||
157 | throw new InvalidArgumentException("no files provided"); |
||
158 | } |
||
159 | |||
160 | // either remove all files or none in case of error |
||
161 | foreach ($files as $file) { |
||
162 | if ($resource->getResource()->locateName($file) === false) { |
||
163 | $resource->getResource()->unchangeAll(); |
||
164 | $resource->getResource()->close(); |
||
165 | |||
166 | throw new InvalidArgumentException(sprintf('%s is not in the zip file', $file)); |
||
167 | } |
||
168 | if (!$resource->getResource()->deleteName($file)) { |
||
169 | $resource->getResource()->unchangeAll(); |
||
170 | $resource->getResource()->close(); |
||
171 | |||
172 | throw new RuntimeException(sprintf('unable to remove %s', $file)); |
||
173 | } |
||
174 | } |
||
175 | $this->flush($resource->getResource()); |
||
176 | |||
177 | return $files; |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @inheritdoc |
||
182 | */ |
||
183 | protected function doAdd(ResourceInterface $resource, $files, $recursive) |
||
184 | { |
||
185 | $files = (array) $files; |
||
186 | if (empty($files)) { |
||
187 | $resource->getResource()->close(); |
||
188 | throw new InvalidArgumentException("no files provided"); |
||
189 | } |
||
190 | $this->addEntries($resource, $files, $recursive); |
||
191 | |||
192 | return $files; |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * @inheritdoc |
||
197 | */ |
||
198 | protected function doCreate($path, $files, $recursive) |
||
199 | { |
||
200 | $files = (array) $files; |
||
201 | |||
202 | if (empty($files)) { |
||
203 | throw new NotSupportedException("Cannot create an empty zip"); |
||
204 | } |
||
205 | |||
206 | $resource = $this->getResource($path, \ZipArchive::CREATE); |
||
207 | $this->addEntries($resource, $files, $recursive); |
||
208 | |||
209 | return new Archive($resource, $this, $this->manager); |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Returns a new instance of the invoked adapter |
||
214 | * |
||
215 | * @return AbstractAdapter |
||
216 | * |
||
217 | * @throws RuntimeException In case object could not be instanciated |
||
218 | */ |
||
219 | public static function newInstance() |
||
220 | { |
||
221 | return new ZipExtensionAdapter(ResourceManager::create()); |
||
222 | } |
||
223 | |||
224 | protected function createResource($path) |
||
225 | { |
||
226 | return $this->getResource($path, \ZipArchive::CHECKCONS); |
||
227 | } |
||
228 | |||
229 | private function getResource($path, $mode) |
||
230 | { |
||
231 | $zip = new \ZipArchive(); |
||
232 | $res = $zip->open($path, $mode); |
||
233 | |||
234 | if ($res !== true) { |
||
235 | throw new RuntimeException($this->errorCodesMapping[$res]); |
||
236 | } |
||
237 | |||
238 | return new ZipArchiveResource($zip); |
||
239 | } |
||
240 | |||
241 | private function addEntries(ResourceInterface $zipResource, array $files, $recursive) |
||
242 | { |
||
243 | $stack = new \SplStack(); |
||
244 | |||
245 | $error = null; |
||
246 | $cwd = getcwd(); |
||
247 | $collection = $this->manager->handle($cwd, $files); |
||
248 | |||
249 | $this->chdir($collection->getContext()); |
||
250 | |||
251 | $adapter = $this; |
||
252 | |||
253 | try { |
||
254 | $collection->forAll(function($i, ZippyResource $resource) use ($zipResource, $stack, $recursive, $adapter) { |
||
255 | $adapter->checkReadability($zipResource->getResource(), $resource->getTarget()); |
||
256 | if (is_dir($resource->getTarget())) { |
||
257 | if ($recursive) { |
||
258 | $stack->push($resource->getTarget() . ((substr($resource->getTarget(), -1) === DIRECTORY_SEPARATOR) ? '' : DIRECTORY_SEPARATOR)); |
||
259 | } else { |
||
260 | $adapter->addEmptyDir($zipResource->getResource(), $resource->getTarget()); |
||
261 | } |
||
262 | } else { |
||
263 | $adapter->addFileToZip($zipResource->getResource(), $resource->getTarget()); |
||
264 | } |
||
265 | |||
266 | return true; |
||
267 | }); |
||
268 | |||
269 | // recursively add dirs |
||
270 | while (!$stack->isEmpty()) { |
||
271 | $dir = $stack->pop(); |
||
272 | // removes . and .. |
||
273 | $files = array_diff(scandir($dir), array(".", "..")); |
||
274 | if (count($files) > 0) { |
||
275 | foreach ($files as $file) { |
||
276 | $file = $dir . $file; |
||
277 | $this->checkReadability($zipResource->getResource(), $file); |
||
278 | if (is_dir($file)) { |
||
279 | $stack->push($file . DIRECTORY_SEPARATOR); |
||
280 | } else { |
||
281 | $this->addFileToZip($zipResource->getResource(), $file); |
||
282 | } |
||
283 | } |
||
284 | } else { |
||
285 | $this->addEmptyDir($zipResource->getResource(), $dir); |
||
286 | } |
||
287 | } |
||
288 | $this->flush($zipResource->getResource()); |
||
289 | |||
290 | $this->manager->cleanup($collection); |
||
291 | } catch (\Exception $e) { |
||
292 | $error = $e; |
||
293 | } |
||
294 | |||
295 | $this->chdir($cwd); |
||
296 | |||
297 | if ($error) { |
||
298 | throw $error; |
||
299 | } |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * Is public for PHP 5.3 compatibility, should be private |
||
304 | * |
||
305 | * @param \ZipArchive $zip |
||
306 | * @param string $file |
||
307 | */ |
||
308 | public function checkReadability(\ZipArchive $zip, $file) |
||
309 | { |
||
310 | if (!is_readable($file)) { |
||
311 | $zip->unchangeAll(); |
||
312 | $zip->close(); |
||
313 | |||
314 | throw new InvalidArgumentException(sprintf('could not read %s', $file)); |
||
315 | } |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * Is public for PHP 5.3 compatibility, should be private |
||
320 | * |
||
321 | * @param \ZipArchive $zip |
||
322 | * @param string $file |
||
323 | */ |
||
324 | View Code Duplication | public function addFileToZip(\ZipArchive $zip, $file) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
325 | { |
||
326 | if (!$zip->addFile($file)) { |
||
327 | $zip->unchangeAll(); |
||
328 | $zip->close(); |
||
329 | |||
330 | throw new RuntimeException(sprintf('unable to add %s to the zip file', $file)); |
||
331 | } |
||
332 | } |
||
333 | |||
334 | /** |
||
335 | * Is public for PHP 5.3 compatibility, should be private |
||
336 | * |
||
337 | * @param \ZipArchive $zip |
||
338 | * @param string $dir |
||
339 | */ |
||
340 | View Code Duplication | public function addEmptyDir(\ZipArchive $zip, $dir) |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
341 | { |
||
342 | if (!$zip->addEmptyDir($dir)) { |
||
343 | $zip->unchangeAll(); |
||
344 | $zip->close(); |
||
345 | |||
346 | throw new RuntimeException(sprintf('unable to add %s to the zip file', $dir)); |
||
347 | } |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Flushes changes to the archive |
||
352 | * |
||
353 | * @param \ZipArchive $zip |
||
354 | */ |
||
355 | private function flush(\ZipArchive $zip) // flush changes by reopening the file |
||
356 | { |
||
357 | $path = $zip->filename; |
||
358 | $zip->close(); |
||
359 | $zip->open($path, \ZipArchive::CHECKCONS); |
||
360 | } |
||
361 | } |
||
362 |
When comparing two booleans, it is generally considered safer to use the strict comparison operator.