Files
Openconf/include-download.inc
2021-04-20 11:13:47 +05:30

230 lines
8.4 KiB
PHP

<?php
// +----------------------------------------------------------------------+
// | OpenConf |
// +----------------------------------------------------------------------+
// | Copyright (c) 2002-2020 Zakon Group LLC. All Rights Reserved. |
// +----------------------------------------------------------------------+
// | This source file is subject to the OpenConf License, available on |
// | the OpenConf web site: www.OpenConf.com |
// +----------------------------------------------------------------------+
// Following variables must be pre-defined:
// $hdr & $hdrfn = page title and user level
// $dir = directory of files to be zip'd
// $savePath = directory to use for files in ZIP archive
// $formatDBFldName = DB field name with file format
// $urlBase = URL base of calling script
// $q = SQL for retrieving file IDs and format, ordered by paperid
// $zipFilePrefix = ZIP filenane prefix to use
// $_GET['t'] = file type
// Zip extension installed?
if ( ! class_exists('ZipArchive') ) {
warn('PHP configuration missing ZipArchive', $hdr, $hdrfn);
}
// Validate file type ID
if ( !isset($_GET['t']) || !preg_match("/^\d+$/", $_GET['t'])) {
warn(oc_('Invalid file type'), $hdr, $hdrfn);
}
// Define variables
$zipFileCount = 20; // number of files at which to reload page to keep number of open files to a minimum
$zipDir = $OC_configAR['OC_dataDir'] . 'zip/'; // directory in which to create ZIP file
$zipFileName = $zipDir . $zipFilePrefix . '-' . $_GET['t'] . '.zip'; // ZIP file name to use
$memoryLimit = trim(ini_get('memory_limit'));
if (preg_match("/^(\d+)([a-z])[a-z]*$/i", $memoryLimit, $matches)) { // catch case where mem limit uses shorthand (e.g., 128M)
$memUnit = strtolower($matches[2]);
$memoryLimit = $matches[1];
switch ($memUnit) {
case 'g':
$memoryLimit *= 1024; // no break, fall through
case 'm':
$memoryLimit *= 1024; // no break, fall through
case 'k':
$memoryLimit *= 1024;
break;
}
}
$memoryLimit = $memoryLimit * 95 / 100; // set memory limit to 95% of capacity
// Display common header
function displayZipNotice($min, $max) {
print '<p>' . oc_('We are generating a ZIP archive of the files. Please do not close this page. Once the ZIP archive is created, you will be prompted to save the file.') . '</p>';
print '<p style="text-align: center;"><progress value="' . $min . '" max="' . $max . '" /></p>';
}
// Action to execute
if (!isset($_GET['daction']) || empty($_GET['daction'])) {
// redirect when starting out in case of double-click and so user knows what is going on
printHeader($hdr, $hdrfn);
displayZipNotice(10, 100);
print '
<noscript>
<p style="font-weight: bold">' . sprintf(oc_('<a href="%s">Click here</a> to continue processing download.'), $urlBase . 't=' . urlencode($_GET['t']) . '&daction=start') . '</p>
</noscript>
';
printFooter();
flush();
print '
<script language="javascript" type="text/javascript">
<!--
window.location.replace("' . $urlBase . 't=' . safeHTMLstr($_GET['t']) . '&daction=start");
// -->
</script>
';
exit;
} elseif ($_GET['daction'] == 'download') {
// send out archive
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=files-" . $_GET['t'] . ".zip");
header("Content-Length: " . filesize($zipFileName));
header("Cache-control: private");
header("Pragma: public"); // IE issue work around
ob_end_flush();
readfile($zipFileName) or err('Unable to read ZIP file', $hdr, $hdrfn);
// delete archive
unlink($zipFileName);
// clean up old ZIP files still around
if ($dh = opendir($zipDir)) {
$fourHoursAgo = time() - (60 * 60 * 4);
while (($file = readdir($dh)) !== false) {
if (preg_match("/\.zip$/", $file) && ($mtime = filemtime($zipDir . $file)) && ($mtime < $fourHoursAgo)) {
unlink($zipDir . $file);
}
}
}
exit;
} elseif ($_GET['daction'] == 'complete') {
printHeader($hdr, $hdrfn);
if (is_file($zipFileName) && (filesize($zipFileName) > 0)) {
displayZipNotice(1, 1);
print '<p>' . oc_('The ZIP archive has been created, and the download should begin shortly.') . '</p>';
if (isset($_GET['skipped']) && ($_GET['skipped'] == 1)) {
print '<p class="warn">' . oc_('One or more files were not found or too large to include in the ZIP archive.') . '</p>';
}
print '
<noscript>
<p>' . sprintf(oc_('If the download does not begin automatically, <a href="%s">click here to download</a>.'), $urlBase . 't=' . urlencode($_GET['t']) . '&daction=download') . '</p>
</noscript>
';
printFooter();
flush();
print '
<script language="javascript" type="text/javascript">
<!--
window.location.replace("' . $urlBase . 't=' . safeHTMLstr($_GET['t']) . '&daction=download");
// -->
</script>
';
} else {
warn('Failed creating ZIP archive (4)');
}
exit;
} else { // daction == start
// let user know what's going on
printHeader($hdr, $hdrfn);
// Retrieve assigned submissions
$r = ocsql_query($q) or err('Unable to retrieve file data');
$fileAR = array();
while ($l = ocsql_fetch_assoc($r)) {
if (!empty($l[$formatDBFldName])) {
$fileAR[$l['paperid']] = $l['paperid'] . '.' . $l[$formatDBFldName];
}
}
ocsql_free_result($r); // free up memory as ZIP could use up a bit
$fileARcount = count($fileAR);
if ($fileARcount == 0) {
warn(oc_('There are no files available'));
}
// Open ZIP archive
$zip = new ZipArchive();
$res = $zip->open($zipFileName, (($_GET['daction']=='start') ? (ZipArchive::OVERWRITE | ZipArchive::CREATE) : ZipArchive::CREATE)) or err('Failed creating ZIP archive (1)');
if (($zip->numFiles > 0) && preg_match("/^\d+$/", $zip->getArchiveComment())) {
$stoppedAt = $zip->getArchiveComment();
} else {
$stoppedAt = 0;
}
// display progress
displayZipNotice((($zip->numFiles > 0) ? $zip->numFiles : ceil($fileARcount * 0.1)), $fileARcount);
// add files
$files = 0; // count of files added per zip->open
if (isset($_GET['skipped']) && ($_GET['skipped'] == 1)) { // track whether we had to skip files
$skippedFiles = true;
} else {
$skippedFiles = false;
}
foreach ($fileAR as $pid => $fname) {
// skip previously processed files
if ($pid <= $stoppedAt) { continue; }
// reload page in case limit(s) being reached
if (
oc_checkTimeout() // processing time reaching timeout
|| ($files++ >= $zipFileCount) // too many files open
|| ( ($fileSize = oc_fileSize($dir . $fname)) === false ) // file does not exist
|| ((memory_get_usage() + ( 2 * $fileSize )) > $memoryLimit) // memory usage will be over limit if file added - 2x factor used (w/memoryLimit @ 95%)
) {
if ($files == 1) {
$zip->setArchiveComment($pid); // skip file because it's too large
$skippedFiles = true;
} else {
$zip->setArchiveComment($pid - 1); // record 1 less than current file ID so it starts w/current file again
}
$zip->close();
print '
<noscript>
<p style="font-weight: bold">' . sprintf(oc_('<a href="%s">Click here</a> to continue processing download.'), $urlBase . 't=' . urlencode($_GET['t']) . '&daction=continue' . ($skippedFiles ? '&skipped=1' : '') ) . '</p>
</noscript>
';
printFooter();
flush();
print '
<script language="javascript" type="text/javascript">
<!--
window.location.replace("' . $urlBase . 't=' . safeHTMLstr($_GET['t']) . '&daction=continue' . ($skippedFiles ? '&skipped=1' : '') . '");
// -->
</script>
';
exit;
}
// add file
if (oc_hookSet('zip_add_file')) {
call_user_func($GLOBALS['OC_hooksAR']['zip_add_file'][0], $zip, $dir . $fname, $savePath . $fname);
} elseif (is_readable($dir . $fname)) {
if ( ! $zip->addFile($dir . $fname, $savePath . $fname) ) {
$zcontent = ocGetFile($dir . $fname) or err('Failed creating ZIP archive (3a)');
$zip->addFromString($savePath . $fname, $zcontent) or err('Failed creating ZIP archive (3b)');
}
}
}
$zip->close();
// done - redirect user
print '
<noscript>
<p style="font-weight: bold">' . sprintf(oc_('<a href="%s">Click here</a> to continue processing download.'), $urlBase . 't=' . urlencode($_GET['t']) . '&daction=complete' . ($skippedFiles ? '&skipped=1' : '') ) . '</p>
</noscript>
';
printFooter();
flush();
print '
<script language="javascript" type="text/javascript">
<!--
window.location.replace("' . $urlBase . 't=' . safeHTMLstr($_GET['t']) . '&daction=complete' . ($skippedFiles ? '&skipped=1' : '') . '");
// -->
</script>
';
}
exit;
?>