pics/controllers/Download.php

123 lines
2.8 KiB
PHP

<?php
/*****************************************************************************
* Download.php
* Contains the code to download an album.
*
* Kabuki CMS (C) 2013-2019, Aaron van Geffen
*****************************************************************************/
class Download extends HTMLController
{
public function __construct()
{
// Ensure we're logged in at this point.
$user = Registry::get('user');
if (!$user->isLoggedIn())
throw new NotAllowedException();
if(!isset($_GET['tag']))
throw new UnexpectedValueException('Must specify an album to download');
$tag = (int)$_GET['tag'];
$album = Tag::fromId($tag);
if($album->kind !== 'Album')
throw new UnexpectedValueException('Specified tag does not correspond to an album');
//Yes TOCTOU but it does not need to be perfect.
$lock_file = join('/', [sys_get_temp_dir(), 'pics-export.lock']);
if(!file_exists($lock_file))
{
try
{
$fp = fopen($lock_file, 'x');
if(!$fp)
throw new UnexpectedValueException('Could not open lock-file');
$this->exportAlbum($album);
}
finally
{
fclose($fp);
unlink($lock_file);
}
}
else
throw new UnexpectedValueException('Another export is busy, please try again later');
exit();
}
private function exportAlbum($album)
{
$files = [];
$album_ids = array_merge([$album->id_tag], $this->getChildAlbumIds($album->id_tag));
foreach($album_ids as $album_id)
{
$iterator = AssetIterator::getByOptions(
[
'id_tag' => $album_id
]
);
while($asset = $iterator->Next())
{
$files[] = join(DIRECTORY_SEPARATOR, ['.', $asset->getSubdir(), $asset->getFilename()]);
}
}
$descriptorspec = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
];
$command = 'tar --null -cf - -T -';
$proc = proc_open($command, $descriptorspec, $pipes, ASSETSDIR);
if(!$proc)
throw new UnexpectedValueException('Could not execute TAR command');
if(!$pipes[0])
throw new UnexpectedValueException('Could not open pipe for STDIN');
if(!$pipes[1])
throw new UnexpectedValueException('Could not open pipe for STDOUT');
$album_name = $album->tag;
header('Pragma: no-cache');
header('Content-Description: File Download');
header('Content-disposition: attachment; filename="' . $album_name . '.tar"');
header('Content-Type: application/octet-stream');
header('Content-Transfer-Encoding: binary');
foreach($files as $file)
fwrite($pipes[0], $file . "\0");
fclose($pipes[0]);
while($chunk = stream_get_contents($pipes[1], 4096))
echo $chunk;
fclose($pipes[1]);
proc_close($proc);
}
private function getChildAlbumIds($parent_id)
{
$ids = [];
$albums = Tag::getAlbums($parent_id, 0, PHP_INT_MAX, 'object');
foreach($albums as $album)
{
$ids[] = $album->id_tag;
$ids = array_merge($ids, $this->getChildAlbumIds($album->id_tag));
}
return $ids;
}
}