Skip to content
This repository was archived by the owner on Nov 25, 2020. It is now read-only.

Commit 66bd0ac

Browse files
committed
Add tar binary support to compression plugin
1 parent 35b87fd commit 66bd0ac

File tree

1 file changed

+173
-54
lines changed

1 file changed

+173
-54
lines changed

core/src/plugins/action.compression/PluginCompression.php

Lines changed: 173 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,33 @@ class PluginCompression extends Plugin
6060
* @throws \Pydio\Core\Exception\ActionNotFoundException
6161
* @throws \Pydio\Core\Exception\AuthRequiredException
6262
*/
63+
64+
// Multi-platform function taken from stackoverflow
65+
// http://stackoverflow.com/questions/12424787/how-to-check-if-a-shell-command-exists-from-php
66+
// Check that a command exists on the system for use with exec
67+
public function command_exists ($command)
68+
{
69+
$whereIsCommand = (PHP_OS == 'WINNT') ? 'where' : 'which';
70+
$process = proc_open(
71+
"$whereIsCommand $command",
72+
array(
73+
0 => array("pipe", "r"), //STDIN
74+
1 => array("pipe", "w"), //STDOUT
75+
2 => array("pipe", "w"), //STDERR
76+
),
77+
$pipes
78+
);
79+
if ($process !== false) {
80+
$stdout = stream_get_contents($pipes[1]);
81+
$stderr = stream_get_contents($pipes[2]);
82+
fclose($pipes[1]);
83+
fclose($pipes[2]);
84+
proc_close($process);
85+
return $stdout != '';
86+
}
87+
return false;
88+
}
89+
6390
public function receiveAction(\Psr\Http\Message\ServerRequestInterface &$requestInterface, \Psr\Http\Message\ResponseInterface &$responseInterface)
6491
{
6592
/** @var \Pydio\Core\Model\ContextInterface $ctx */
@@ -78,9 +105,9 @@ public function receiveAction(\Psr\Http\Message\ServerRequestInterface &$request
78105
$responseInterface = $responseInterface->withBody($serializableStream);
79106

80107
switch ($requestInterface->getAttribute("action")) {
81-
108+
82109
case "compression":
83-
110+
84111
$archiveName = InputFilter::decodeSecureMagic($httpVars["archive_name"], InputFilter::SANITIZE_FILENAME);
85112
$archiveFormat = $httpVars["type_archive"];
86113
$tabTypeArchive = array(".tar", ".tar.gz", ".tar.bz2");
@@ -140,51 +167,107 @@ public function receiveAction(\Psr\Http\Message\ServerRequestInterface &$request
140167
$postMessageStatus($messages["compression.17"], Task::STATUS_FAILED);
141168
throw new PydioException($messages["compression.17"]);
142169
}
143-
try {
144-
$tmpArchiveName = tempnam(ApplicationState::getTemporaryFolder(), "tar-compression") . ".tar";
145-
$archive = new PharData($tmpArchiveName);
146-
} catch (Exception $e) {
147-
$postMessageStatus($e->getMessage(), Task::STATUS_FAILED);
148-
throw $e;
149-
}
150-
$counterCompression = 0;
151-
//THE TWO ARRAY ARE MERGED FOR THE FOREACH LOOP
152-
$tabAllFiles = array_combine($tabAllRecursiveFiles, $tabFilesNames);
153-
foreach ($tabAllFiles as $fullPath => $fileName) {
170+
// Builds a shell command that changes to the workspace location in the filesystem, and then
171+
// compresses with tar and any parallel compression utilities that are installed.
172+
if ($this->command_exists("tar")) {
173+
$FileNames = array();
174+
$FolderNames = array();
175+
foreach ($nodes as $node) {
176+
$nodeUrl = $node->getUrl();
177+
if (is_file($nodeUrl) && filesize($nodeUrl) < $maxAuthorizedSize) {
178+
array_push($FileNames, escapeshellarg(substr($nodeUrl, $currentDirUrlLength)));
179+
}
180+
if (is_dir($nodeUrl)) {
181+
array_push($FolderNames, escapeshellarg(substr($nodeUrl, $currentDirUrlLength)));
182+
}
183+
}
184+
if ($archiveFormat == ".tar.gz") {
185+
if ($this->command_exists("pigz")) {
186+
$tarCommand = "tar -I pigz -cf ";
187+
} else {
188+
$tarCommand = "tar -czf ";
189+
}
190+
} elseif ($archiveFormat == ".tar.bz2") {
191+
if ($this->command_exists("lbzip2")) {
192+
$tarCommand = "tar -I lbzip2 -cf ";
193+
} elseif ($this->command_exists("pbzip2")) {
194+
$tarCommand = "tar -I pbzip2 -cf ";
195+
} else {
196+
$tarCommand = "tar -cjf ";
197+
}
198+
} elseif ($archiveFormat == ".tar") {
199+
$tarCommand = "tar -cf ";
200+
} else {
201+
file_put_contents($progressCompressionFileName, "Error : " . $messages["compression.15"]);
202+
throw new PydioException($messages["compression.15"]);
203+
}
204+
$changeDirCommand = "cd " . MetaStreamWrapper::getRealFSReference($currentDirUrl) . " && ";
205+
$compressCommand = $changeDirCommand . $tarCommand . escapeshellarg($archiveName) . " " . implode(" ", $FolderNames) . " " . implode(" ", $FileNames);
206+
$cmdExitCode = 0;
207+
$cmdOutput = '';
208+
exec($compressCommand, $cmdOutput, $cmdExitCode);
209+
if ($cmdExitCode > 0) {
210+
file_put_contents($progressCompressionFileName, "Error : " . $messages["compression.15"]);
211+
throw new PydioException($messages["compression.15"]);
212+
}
213+
} else {
214+
// If tar command is not found, compress with PHP Phar instead
154215
try {
155-
$archive->addFile(MetaStreamWrapper::getRealFSReference($fullPath), $fileName);
156-
$counterCompression++;
157-
$percent = round(($counterCompression / count($tabAllFiles)) * 100, 0, PHP_ROUND_HALF_DOWN);
158-
$postMessageStatus(sprintf($messages["compression.6"], $percent . " %"), Task::STATUS_RUNNING, $percent);
216+
$tmpArchiveName = tempnam(ApplicationState::getAjxpTmpDir(), "tar-compression") . ".tar";
217+
$archive = new PharData($tmpArchiveName);
159218
} catch (Exception $e) {
160-
unlink($tmpArchiveName);
161219
$postMessageStatus($e->getMessage(), Task::STATUS_FAILED);
162220
throw $e;
163221
}
164-
}
165-
$finalArchive = $tmpArchiveName;
166-
if ($typeArchive != ".tar") {
167-
$archiveTypeCompress = substr(strrchr($typeArchive, "."), 1);
168-
$postMessageStatus(sprintf($messages["compression.7"], strtoupper($archiveTypeCompress)), Task::STATUS_RUNNING);
169-
if ($archiveTypeCompress == "gz") {
170-
$archive->compress(Phar::GZ);
171-
} elseif ($archiveTypeCompress == "bz2") {
172-
$archive->compress(Phar::BZ2);
222+
$counterCompression = 0;
223+
//THE TWO ARRAY ARE MERGED FOR THE FOREACH LOOP
224+
$tabAllFiles = array_combine($tabAllRecursiveFiles, $tabFilesNames);
225+
foreach ($tabAllFiles as $fullPath => $fileName) {
226+
try {
227+
$archive->addFile(MetaStreamWrapper::getRealFSReference($fullPath), $fileName);
228+
$counterCompression++;
229+
$percent = round(($counterCompression / count($tabAllFiles)) * 100, 0, PHP_ROUND_HALF_DOWN);
230+
$postMessageStatus(sprintf($messages["compression.6"], $percent . " %"), Task::STATUS_RUNNING, $percent);
231+
} catch (Exception $e) {
232+
unlink($tmpArchiveName);
233+
$postMessageStatus($e->getMessage(), Task::STATUS_FAILED);
234+
throw $e;
235+
}
236+
}
237+
$finalArchive = $tmpArchiveName;
238+
if ($typeArchive != ".tar") {
239+
$archiveTypeCompress = substr(strrchr($typeArchive, "."), 1);
240+
$postMessageStatus(sprintf($messages["compression.7"], strtoupper($archiveTypeCompress)), Task::STATUS_RUNNING);
241+
if ($archiveTypeCompress == "gz") {
242+
$archive->compress(Phar::GZ);
243+
} elseif ($archiveTypeCompress == "bz2") {
244+
$archive->compress(Phar::BZ2);
245+
}
246+
$finalArchive = $tmpArchiveName . "." . $archiveTypeCompress;
173247
}
174-
$finalArchive = $tmpArchiveName . "." . $archiveTypeCompress;
175-
}
248+
249+
$newNode = new AJXP_Node($currentDirUrl . $archiveName);
250+
$destArchive = $newNode->getRealFile();
251+
rename($finalArchive, $destArchive);
252+
Controller::applyHook("node.before_create", array($newNode, filesize($destArchive)));
253+
254+
if (file_exists($tmpArchiveName)) {
255+
unlink($tmpArchiveName);
256+
unlink(substr($tmpArchiveName, 0, -4));
257+
}
258+
Controller::applyHook("node.change", array(null, $newNode, false), true);
259+
$postMessageStatus("Finished", Task::STATUS_COMPLETE);
260+
break;
261+
262+
// End of Phar compression section
263+
}
176264

177265
$newNode = new AJXP_Node($currentDirUrl . $archiveName);
178266
$destArchive = $newNode->getRealFile();
179-
rename($finalArchive, $destArchive);
180267
Controller::applyHook("node.before_create", array($newNode, filesize($destArchive)));
181-
if (file_exists($tmpArchiveName)) {
182-
unlink($tmpArchiveName);
183-
unlink(substr($tmpArchiveName, 0, -4));
184-
}
185268
Controller::applyHook("node.change", array(null, $newNode, false), true);
186269
$postMessageStatus("Finished", Task::STATUS_COMPLETE);
187-
270+
188271
break;
189272

190273
case "extraction":
@@ -238,29 +321,65 @@ public function receiveAction(\Psr\Http\Message\ServerRequestInterface &$request
238321

239322
mkdir($currentDirUrl . $onlyFileName, 0777, true);
240323
chmod(MetaStreamWrapper::getRealFSReference($currentDirUrl . $onlyFileName), 0777);
241-
try {
242-
$archive = new PharData(MetaStreamWrapper::getRealFSReference($currentAllPydioPath));
243-
$fichiersArchive = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pharCurrentAllPydioPath));
244-
foreach ($fichiersArchive as $file) {
245-
$fileGetPathName = $file->getPathname();
246-
if ($file->isDir()) {
247-
continue;
324+
325+
// Builds a command that will use any parallel extraction utilities installed to extract an archive file though tar.
326+
if ($this->command_exists("tar")) {
327+
if ($pathInfoCurrentAllPydioPath == "gz") {
328+
if ($this->command_exists("pigz")) {
329+
$tarCommand = "tar -I pigz -xf ";
330+
} else {
331+
$tarCommand = "tar -xzf ";
248332
}
249-
$fileNameInArchive = substr(strstr($fileGetPathName, $fileArchive), strlen($fileArchive) + 1);
250-
try {
251-
$archive->extractTo(MetaStreamWrapper::getRealFSReference($currentDirUrl . $onlyFileName), $fileNameInArchive, false);
252-
} catch (Exception $e) {
253-
$postMessageStatus($e->getMessage(), Task::STATUS_FAILED);
254-
throw new PydioException($e);
333+
} elseif ($pathInfoCurrentAllPydioPath == "bz2") {
334+
if ($this->command_exists("lbzip2")) {
335+
$tarCommand = "tar -I lbzip2 -xf ";
336+
} elseif ($this->command_exists("pbzip2")) {
337+
$tarCommand = "tar -I pbzip2 -xf ";
338+
} else {
339+
$tarCommand = "tar -xjf ";
340+
}
341+
} elseif ($pathInfoCurrentAllPydioPath == "tar") {
342+
$tarCommand = "tar -xf ";
343+
} else {
344+
file_put_contents($progressExtractFileName, "Error : " . $messages["compression.15"]);
345+
throw new PydioException($messages["compression.15"]);
346+
}
347+
$extractCommand = $tarCommand . escapeshellarg(MetaStreamWrapper::getRealFSReference($currentDirUrl . $fileArchive)) . " -C " . escapeshellarg(MetaStreamWrapper::getRealFSReference($currentDirUrl . $onlyFileName));
348+
$cmdExitCode = 0;
349+
$cmdOutput = '';
350+
exec($extractCommand, $cmdOutput, $cmdExitCode);
351+
if ($cmdExitCode > 0) {
352+
file_put_contents($progressExtractFileName, "Error : " . $messages["compression.15"]);
353+
throw new PydioException($messages["compression.15"]);
354+
}
355+
} else {
356+
// If tar command is not found, extract using PHP Phar instead
357+
try {
358+
$archive = new PharData(MetaStreamWrapper::getRealFSReference($currentAllPydioPath));
359+
$fichiersArchive = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pharCurrentAllPydioPath));
360+
foreach ($fichiersArchive as $file) {
361+
$fileGetPathName = $file->getPathname();
362+
if ($file->isDir()) {
363+
continue;
364+
}
365+
$fileNameInArchive = substr(strstr($fileGetPathName, $fileArchive), strlen($fileArchive) + 1);
366+
try {
367+
$archive->extractTo(MetaStreamWrapper::getRealFSReference($currentDirUrl . $onlyFileName), $fileNameInArchive, false);
368+
} catch (Exception $e) {
369+
$postMessageStatus($e->getMessage(), Task::STATUS_FAILED);
370+
throw new PydioException($e);
371+
}
372+
$counterExtract++;
373+
$progress = round(($counterExtract / $archive->count()) * 100, 0, PHP_ROUND_HALF_DOWN);
374+
$postMessageStatus(sprintf($messages["compression.13"], $progress . "%"), Task::STATUS_RUNNING, $progress);
255375
}
256-
$counterExtract++;
257-
$progress = round(($counterExtract / $archive->count()) * 100, 0, PHP_ROUND_HALF_DOWN);
258-
$postMessageStatus(sprintf($messages["compression.13"], $progress . "%"), Task::STATUS_RUNNING, $progress);
376+
} catch (Exception $e) {
377+
$postMessageStatus($e->getMessage(), Task::STATUS_FAILED);
378+
throw new PydioException($e);
259379
}
260-
} catch (Exception $e) {
261-
$postMessageStatus($e->getMessage(), Task::STATUS_FAILED);
262-
throw new PydioException($e);
380+
// End of Phar extraction section
263381
}
382+
264383
$postMessageStatus("Done", Task::STATUS_COMPLETE, 100);
265384
$newNode = new AJXP_Node($currentDirUrl . $onlyFileName);
266385
$nodesDiff = new NodesDiff();

0 commit comments

Comments
 (0)