b54ab3e827ff8fadd773a8dd6687c95f6bd944f5
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Tests / Unit / Utility / FileHandlingUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Tests\Unit\Utility;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * Testcase
19 *
20 */
21 class FileHandlingUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
22
23 /**
24 * @var array List of created fake extensions to be deleted in tearDown() again
25 */
26 protected $fakedExtensions = array();
27
28 /**
29 * Creates a fake extension inside typo3temp/. No configuration is created,
30 * just the folder
31 *
32 * @param bool $extkeyOnly
33 * @return string The extension key
34 */
35 protected function createFakeExtension($extkeyOnly = FALSE) {
36 $extKey = strtolower(uniqid('testing'));
37 $absExtPath = PATH_site . 'typo3temp/ext-' . $extKey . '/';
38 $relPath = 'typo3temp/ext-' . $extKey . '/';
39 $this->fakedExtensions[$extKey] = array(
40 'siteRelPath' => $relPath,
41 'siteAbsPath' => $absExtPath
42 );
43 if ($extkeyOnly === TRUE) {
44 return $extKey;
45 }
46 \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir($absExtPath);
47 $this->testFilesToDelete[] = PATH_site . 'typo3temp/ext-' . $extKey;
48 return $extKey;
49 }
50
51 /**
52 * @test
53 * @return void
54 */
55 public function makeAndClearExtensionDirRemovesExtensionDirIfAlreadyExists() {
56 $extKey = $this->createFakeExtension();
57 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('removeDirectory', 'addDirectory', 'getExtensionDir'), array(), '', FALSE);
58 $fileHandlerMock->expects($this->once())
59 ->method('removeDirectory')
60 ->with(PATH_site . 'typo3temp/ext-' . $extKey . '/');
61 $fileHandlerMock->expects($this->any())
62 ->method('getExtensionDir')
63 ->willReturn(PATH_site . 'typo3temp/ext-' . $extKey . '/');
64 $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
65 }
66
67 /**
68 * @return array
69 */
70 public function invalidRelativePathDataProvider() {
71 return array(
72 array('../../'),
73 array('/foo/bar'),
74 array('foo//bar'),
75 array('foo/bar' . chr(0)),
76 );
77 }
78
79 /**
80 * @param string $invalidRelativePath
81 * @test
82 * @dataProvider invalidRelativePathDataProvider
83 * @expectedException \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
84 */
85 public function getAbsolutePathThrowsExceptionForInvalidRelativePaths($invalidRelativePath) {
86 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('dummy'), array());
87 $fileHandlerMock->_call('getAbsolutePath', $invalidRelativePath);
88 }
89
90 /**
91 * @return array
92 */
93 public function validRelativePathDataProvider() {
94 return array(
95 array('foo/../bar', PATH_site . 'bar'),
96 array('bas', PATH_site . 'bas'),
97 );
98 }
99
100 /**
101 * @param string $validRelativePath
102 * @param string $expectedAbsolutePath
103 * @test
104 * @dataProvider validRelativePathDataProvider
105 */
106 public function getAbsolutePathReturnsAbsolutePathForValidRelativePaths($validRelativePath, $expectedAbsolutePath) {
107 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('dummy'));
108 $this->assertSame($expectedAbsolutePath, $fileHandlerMock->_call('getAbsolutePath', $validRelativePath));
109 }
110
111 /**
112 * @test
113 * @return void
114 */
115 public function makeAndClearExtensionDirAddsDir() {
116 $extKey = $this->createFakeExtension();
117 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('removeDirectory', 'addDirectory', 'getExtensionDir'));
118 $fileHandlerMock->expects($this->once())
119 ->method('addDirectory')
120 ->with(PATH_site . 'typo3temp/ext-' . $extKey . '/');
121 $fileHandlerMock->expects($this->any())
122 ->method('getExtensionDir')
123 ->willReturn(PATH_site . 'typo3temp/ext-' . $extKey . '/');
124 $fileHandlerMock->_call('makeAndClearExtensionDir', $extKey);
125 }
126
127 /**
128 * @test
129 * @expectedException \TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException
130 * @return void
131 */
132 public function makeAndClearExtensionDirThrowsExceptionOnInvalidPath() {
133 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('removeDirectory', 'addDirectory'));
134 $languageServiceMock = $this->getMock('TYPO3\\CMS\\Lang\\LanguageService');
135 $fileHandlerMock->_set('languageService', $languageServiceMock);
136 $fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
137 }
138
139 /**
140 * @test
141 * @return void
142 */
143 public function addDirectoryAddsDirectory() {
144 $extDirPath = PATH_site . '/typo3temp/' . uniqid('test-extensions-', TRUE);
145 $this->testFilesToDelete[] = $extDirPath;
146 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('dummy'));
147 $fileHandlerMock->_call('addDirectory', $extDirPath);
148 $this->assertTrue(is_dir($extDirPath));
149 }
150
151 /**
152 * @test
153 * @return void
154 */
155 public function removeDirectoryRemovesDirectory() {
156 $extDirPath = PATH_site . '/typo3temp/' . uniqid('test-extensions-', TRUE);
157 @mkdir($extDirPath);
158 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('dummy'));
159 $fileHandlerMock->_call('removeDirectory', $extDirPath);
160 $this->assertFalse(is_dir($extDirPath));
161 }
162
163 /**
164 * @test
165 * @return void
166 */
167 public function removeDirectoryRemovesSymlink() {
168 $absoluteSymlinkPath = PATH_site . 'typo3temp/' . uniqid('test_symlink_');
169 $absoluteFilePath = PATH_site . 'typo3temp/' . uniqid('test_file_');
170 touch($absoluteFilePath);
171 $this->testFilesToDelete[] = $absoluteFilePath;
172 symlink($absoluteFilePath, $absoluteSymlinkPath);
173 $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
174 $fileHandler->removeDirectory($absoluteSymlinkPath);
175 $this->assertFalse(is_link($absoluteSymlinkPath));
176 }
177
178 /**
179 * @test
180 * @return void
181 */
182 public function removeDirectoryDoesNotRemoveContentOfSymlinkedTargetDirectory() {
183 $absoluteSymlinkPath = PATH_site . 'typo3temp/' . uniqid('test_symlink_');
184 $absoluteDirectoryPath = PATH_site . 'typo3temp/' . uniqid('test_dir_') . '/';
185 $relativeFilePath = uniqid('test_file_');
186
187 mkdir($absoluteDirectoryPath);
188 touch($absoluteDirectoryPath . $relativeFilePath);
189
190 $this->testFilesToDelete[] = $absoluteDirectoryPath . $relativeFilePath;
191 $this->testFilesToDelete[] = $absoluteDirectoryPath;
192
193 symlink($absoluteDirectoryPath, $absoluteSymlinkPath);
194
195 $fileHandler = new \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility();
196 $fileHandler->removeDirectory($absoluteSymlinkPath);
197 $this->assertTrue(is_file($absoluteDirectoryPath . $relativeFilePath));
198 }
199
200 /**
201 * @test
202 * @return void
203 */
204 public function unpackExtensionFromExtensionDataArrayCreatesTheExtensionDirectory() {
205 $extensionData = array(
206 'extKey' => 'test'
207 );
208 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array(
209 'makeAndClearExtensionDir',
210 'writeEmConfToFile',
211 'extractFilesArrayFromExtensionData',
212 'extractDirectoriesFromExtensionData',
213 'createDirectoriesForExtensionFiles',
214 'writeExtensionFiles'
215 ));
216 $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue(array()));
217 $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue(array()));
218 $fileHandlerMock->expects($this->once())->method('makeAndClearExtensionDir')->with($extensionData['extKey']);
219 $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
220 }
221
222 /**
223 * @test
224 * @return void
225 */
226 public function unpackExtensionFromExtensionDataArrayStripsDirectoriesFromFilesArray() {
227 $extensionData = array(
228 'extKey' => 'test'
229 );
230 $files = array(
231 'ChangeLog' => array(
232 'name' => 'ChangeLog',
233 'size' => 4559,
234 'mtime' => 1219448527,
235 'is_executable' => FALSE,
236 'content' => 'some content to write'
237 ),
238 'doc/' => array(
239 'name' => 'doc/',
240 'size' => 0,
241 'mtime' => 1219448527,
242 'is_executable' => FALSE,
243 'content' => ''
244 ),
245 'doc/ChangeLog' => array(
246 'name' => 'ChangeLog',
247 'size' => 4559,
248 'mtime' => 1219448527,
249 'is_executable' => FALSE,
250 'content' => 'some content to write'
251 ),
252 );
253 $cleanedFiles = array(
254 'ChangeLog' => array(
255 'name' => 'ChangeLog',
256 'size' => 4559,
257 'mtime' => 1219448527,
258 'is_executable' => FALSE,
259 'content' => 'some content to write'
260 ),
261 'doc/ChangeLog' => array(
262 'name' => 'ChangeLog',
263 'size' => 4559,
264 'mtime' => 1219448527,
265 'is_executable' => FALSE,
266 'content' => 'some content to write'
267 ),
268 );
269 $directories = array(
270 'doc/',
271 'mod/doc/'
272 );
273
274 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array(
275 'makeAndClearExtensionDir',
276 'writeEmConfToFile',
277 'extractFilesArrayFromExtensionData',
278 'extractDirectoriesFromExtensionData',
279 'createDirectoriesForExtensionFiles',
280 'writeExtensionFiles'
281 ));
282 $fileHandlerMock->expects($this->once())->method('extractFilesArrayFromExtensionData')->will($this->returnValue($files));
283 $fileHandlerMock->expects($this->once())->method('extractDirectoriesFromExtensionData')->will($this->returnValue($directories));
284 $fileHandlerMock->expects($this->once())->method('createDirectoriesForExtensionFiles')->with($directories);
285 $fileHandlerMock->expects($this->once())->method('writeExtensionFiles')->with($cleanedFiles);
286 $fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
287 }
288
289 /**
290 * @test
291 * @return void
292 */
293 public function extractFilesArrayFromExtensionDataReturnsFileArray() {
294 $extensionData = array(
295 'key' => 'test',
296 'FILES' => array(
297 'filename1' => 'dummycontent',
298 'filename2' => 'dummycontent2'
299 )
300 );
301 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
302 $extractedFiles = $fileHandlerMock->_call('extractFilesArrayFromExtensionData', $extensionData);
303 $this->assertArrayHasKey('filename1', $extractedFiles);
304 $this->assertArrayHasKey('filename2', $extractedFiles);
305 }
306
307 /**
308 * @test
309 * @return void
310 */
311 public function writeExtensionFilesWritesFiles() {
312 $files = array(
313 'ChangeLog' => array(
314 'name' => 'ChangeLog',
315 'size' => 4559,
316 'mtime' => 1219448527,
317 'is_executable' => FALSE,
318 'content' => 'some content to write'
319 ),
320 'README' => array(
321 'name' => 'README',
322 'size' => 4566,
323 'mtime' => 1219448533,
324 'is_executable' => FALSE,
325 'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
326 )
327 );
328 $rootPath = ($extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath']);
329 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
330 $fileHandlerMock->_call('writeExtensionFiles', $files, $rootPath);
331 $this->assertTrue(file_exists($rootPath . 'ChangeLog'));
332 }
333
334 /**
335 * @test
336 * @return void
337 */
338 public function extractDirectoriesFromExtensionDataExtractsDirectories() {
339 $files = array(
340 'ChangeLog' => array(
341 'name' => 'ChangeLog',
342 'size' => 4559,
343 'mtime' => 1219448527,
344 'is_executable' => FALSE,
345 'content' => 'some content to write'
346 ),
347 'doc/' => array(
348 'name' => 'doc/',
349 'size' => 0,
350 'mtime' => 1219448527,
351 'is_executable' => FALSE,
352 'content' => ''
353 ),
354 'doc/ChangeLog' => array(
355 'name' => 'ChangeLog',
356 'size' => 4559,
357 'mtime' => 1219448527,
358 'is_executable' => FALSE,
359 'content' => 'some content to write'
360 ),
361 'doc/README' => array(
362 'name' => 'README',
363 'size' => 4566,
364 'mtime' => 1219448533,
365 'is_executable' => FALSE,
366 'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
367 ),
368 'mod/doc/README' => array(
369 'name' => 'README',
370 'size' => 4566,
371 'mtime' => 1219448533,
372 'is_executable' => FALSE,
373 'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
374 )
375 );
376 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
377 $extractedDirectories = $fileHandlerMock->_call('extractDirectoriesFromExtensionData', $files);
378 $expected = array(
379 'doc/',
380 'mod/doc/'
381 );
382 $this->assertSame($expected, array_values($extractedDirectories));
383 }
384
385 /**
386 * @test
387 * @return void
388 */
389 public function createDirectoriesForExtensionFilesCreatesDirectories() {
390 $rootPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath'];
391 $directories = array(
392 'doc/',
393 'mod/doc/'
394 );
395 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
396 $this->assertFalse(is_dir($rootPath . 'doc/'));
397 $this->assertFalse(is_dir($rootPath . 'mod/doc/'));
398 $fileHandlerMock->_call('createDirectoriesForExtensionFiles', $directories, $rootPath);
399 $this->assertTrue(is_dir($rootPath . 'doc/'));
400 $this->assertTrue(is_dir($rootPath . 'mod/doc/'));
401 }
402
403 /**
404 * @test
405 * @return void
406 */
407 public function writeEmConfWritesEmConfFile() {
408 $extKey = $this->createFakeExtension();
409 $extensionData = array(
410 'extKey' => $extKey,
411 'EM_CONF' => array(
412 'title' => 'Plugin cache engine',
413 'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
414 'category' => 'Frontend',
415 'shy' => 0
416 )
417 );
418 $rootPath = $this->fakedExtensions[$extKey]['siteAbsPath'];
419 $emConfUtilityMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\EmConfUtility', array('constructEmConf'));
420 $emConfUtilityMock->expects($this->once())->method('constructEmConf')->with($extensionData)->will($this->returnValue(var_export($extensionData['EM_CONF'], TRUE)));
421 $fileHandlerMock = $this->getAccessibleMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('makeAndClearExtensionDir'));
422 $fileHandlerMock->_set('emConfUtility', $emConfUtilityMock);
423 $fileHandlerMock->_call('writeEmConfToFile', $extensionData, $rootPath);
424 $this->assertTrue(file_exists($rootPath . 'ext_emconf.php'));
425 }
426
427 /**
428 * @return \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility
429 */
430 protected function getPreparedFileHandlingMockForDirectoryCreationTests() {
431 /** @var $fileHandlerMock \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility|\PHPUnit_Framework_MockObject_MockObject */
432 $fileHandlerMock = $this->getMock('TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility', array('createNestedDirectory', 'getAbsolutePath', 'directoryExists'));
433 $fileHandlerMock->expects($this->any())
434 ->method('getAbsolutePath')
435 ->will($this->returnArgument(0));
436 return $fileHandlerMock;
437 }
438
439 /**
440 * @test
441 */
442 public function uploadFolderIsNotCreatedIfNotRequested() {
443 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
444 $fileHandlerMock->expects($this->never())
445 ->method('createNestedDirectory');
446 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
447 'key' => 'foo_bar',
448 'uploadfolder' => 0,
449 )
450 );
451 }
452
453 /**
454 * @test
455 */
456 public function additionalFoldersAreNotCreatedIfNotRequested() {
457 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
458 $fileHandlerMock->expects($this->never())
459 ->method('createNestedDirectory');
460 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
461 'key' => 'foo_bar',
462 'createDirs' => '',
463 )
464 );
465 }
466
467 /**
468 * @test
469 */
470 public function configuredUploadFolderIsCreatedIfRequested() {
471 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
472 $fileHandlerMock->expects($this->once())
473 ->method('createNestedDirectory')
474 ->with('uploads/tx_foobar/');
475 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
476 'key' => 'foo_bar',
477 'uploadfolder' => 1,
478 )
479 );
480 }
481
482 /**
483 * @test
484 */
485 public function configuredAdditionalDirectoriesAreCreatedIfRequested() {
486 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
487 $fileHandlerMock->expects($this->exactly(2))
488 ->method('createNestedDirectory')
489 ->will($this->returnCallback(function($path) {
490 if (!in_array($path, array('foo/bar', 'baz/foo'))) {
491 throw new \Exception('Path "' . $path . '" is not expected to be created');
492 }
493
494 })
495 );
496 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
497 'key' => 'foo_bar',
498 'createDirs' => 'foo/bar, baz/foo',
499 )
500 );
501 }
502
503 /**
504 * @test
505 */
506 public function configuredDirectoriesAreNotCreatedIfTheyAlreadyExist() {
507 $fileHandlerMock = $this->getPreparedFileHandlingMockForDirectoryCreationTests();
508 $fileHandlerMock->expects($this->exactly(3))
509 ->method('directoryExists')
510 ->will($this->returnValue(TRUE));
511 $fileHandlerMock->expects($this->never())
512 ->method('createNestedDirectory');
513 $fileHandlerMock->ensureConfiguredDirectoriesExist(array(
514 'key' => 'foo_bar',
515 'uploadfolder' => 1,
516 'createDirs' => 'foo/bar, baz/foo',
517 )
518 );
519 }
520
521 /**
522 * Warning: This test asserts multiple things at once to keep the setup short.
523 *
524 * @test
525 */
526 public function createZipFileFromExtensionGeneratesCorrectArchive() {
527 // Create extension for testing:
528 $extKey = $this->createFakeExtension();
529 $extensionRoot = $this->fakedExtensions[$extKey]['siteAbsPath'];
530
531 // Build mocked fileHandlingUtility:
532 $fileHandlerMock = $this->getAccessibleMock(
533 'TYPO3\\CMS\\Extensionmanager\\Utility\\FileHandlingUtility',
534 array('getAbsoluteExtensionPath', 'getExtensionVersion')
535 );
536 $fileHandlerMock->expects($this->any())
537 ->method('getAbsoluteExtensionPath')
538 ->will($this->returnValue($extensionRoot));
539 $fileHandlerMock->expects($this->any())
540 ->method('getExtensionVersion')
541 ->will($this->returnValue('0.0.0'));
542
543 // Add files and directories to extension:
544 touch($extensionRoot . 'emptyFile.txt');
545 file_put_contents($extensionRoot . 'notEmptyFile.txt', 'content');
546 touch($extensionRoot . '.hiddenFile');
547 mkdir($extensionRoot . 'emptyDir');
548 mkdir($extensionRoot . 'notEmptyDir');
549 touch($extensionRoot . 'notEmptyDir/file.txt');
550
551 // Create zip-file from extension
552 $filename = $fileHandlerMock->_call('createZipFileFromExtension', $extKey);
553
554 $expectedFilename = PATH_site . 'typo3temp/' . $extKey . '_0.0.0_' . date('YmdHi') . '.zip';
555 $this->assertEquals($expectedFilename, $filename, 'Archive file name differs from expectation');
556
557 // File was created
558 $this->assertTrue(file_exists($filename), 'Zip file not created');
559 $this->testFilesToDelete[] = $filename;
560
561 // Read archive and check its contents
562 $archive = new \ZipArchive();
563 $this->assertTrue($archive->open($filename), 'Unable to open archive');
564 $this->assertEquals($archive->statName('emptyFile.txt')->size, 0, 'Empty file not in archive');
565 $this->assertEquals($archive->getFromName('notEmptyFile.txt'), 'content', 'Expected content not found');
566 $this->assertFalse($archive->statName('.hiddenFile'), 'Hidden file not in archive');
567 $this->assertTrue(is_array($archive->statName('emptyDir/')), 'Empty directory not in archive');
568 $this->assertTrue(is_array($archive->statName('notEmptyDir/')), 'Not empty directory not in archive');
569 $this->assertTrue(is_array($archive->statName('notEmptyDir/file.txt')), 'File within directory not in archive');
570
571 // Check that the archive has no additional content
572 $this->assertEquals($archive->numFiles, 5, 'Too many or too less files in archive');
573 }
574 }