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('/', array(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 = array(); $descriptorspec = array( 0 => array('pipe', 'r'), 1 => array('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'); $album_ids = array_merge(array($album->id_tag), $this->getChildAlbumIds($album->id_tag)); foreach($album_ids as $album_id) { $iterator = AssetIterator::getByOptions( array( 'id_tag' => $album_id ) ); while($asset = $iterator->Next()) { fwrite($pipes[0], join(DIRECTORY_SEPARATOR, array('.', $asset->getSubdir(), $asset->getFilename())) . "\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 = array(); $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; } }