/** construct a zipfile with the current source and stream it to the visitor
 *
 * this routine streams a ZIP-archive to the visitor with either the current
 * websiteatschool program code or the selected manual. This routine is necessary
 * to comply with the provisions of the program license which basically says that the
 * source code of the running program must be made available.
 *
 * Note that it is not strictly necessary to also provide the manual, but this
 * routine can do that nevertheless.
 *
 * Note that we take special care not to download the (private) data directory
 * $CFG->datadir. Of course the datadirectory should live outside the document
 * root and certainly outside the /program directory tree, but accidents will
 * happen and we don't want to create a gaping security hole.
 *
 * If there are severe errors (e.g. no manual is available for download or an invalid
 * component was specified) the program exist immediately with a 404 not found error.
 * Otherwise the ZIP-archive is streamed to the user. If all goes well, we return TRUE,
 * if there were errors we immediately return TRUE (without finishing the task at hand
 * other than a perhasp futile attempt to properly close the  ZIP-archive). The last
 * error message from the Zip is logged.
 *
 * @param string $component either 'program' or 'manual' or 'languages'
 * @return void|bool Exit with 404 not found, OR TRUE and generated ZIP-file sent to user OR FALSE on error
 * @uses download_source_tree()
 */
function download_source($component)
{
    global $CFG;
    global $LANGUAGE;
    $time_start = microtime();
    //
    // Step 0 -- decide what needs to be done
    //
    switch ($component) {
        case 'program':
            $files = array('index.php', 'admin.php', 'cron.php', 'file.php', 'config-example.php');
            $directories = array('program' => $CFG->progdir);
            $excludes = array(realpath($CFG->datadir), realpath($CFG->progdir . '/manuals'));
            $archive = 'websiteatschool-program.zip';
            break;
        case 'manual':
            $language = $LANGUAGE->get_current_language();
            $manuals = $CFG->progdir . '/manuals';
            if (!is_dir($manuals . '/' . $language)) {
                if (!is_dir($manuals . '/en')) {
                    logger(sprintf("Failed download 'websiteatschool/manual/%s': 404 Not Found", $language));
                    error_exit404("websiteatschool/{$component}");
                } else {
                    $language = 'en';
                }
            }
            $files = array();
            $directories = array('program/manuals/' . $language => $manuals . '/' . $language);
            $excludes = array(realpath($CFG->datadir));
            $archive = sprintf('websiteatschool-manual-%s.zip', $language);
            break;
        case 'languages':
            $files = array();
            $directories = array();
            $excludes = array();
            $archive = 'websiteatschool-languages.zip';
            $languages = $LANGUAGE->retrieve_languages();
            foreach ($languages as $language_key => $language) {
                if (db_bool_is(TRUE, $language['is_active']) && db_bool_is(TRUE, $language['dialect_in_file'])) {
                    $directories['languages/' . $language_key] = $CFG->datadir . '/languages/' . $language_key;
                }
            }
            if (sizeof($directories) < 1) {
                logger(sprintf("Failed download websiteatschool/%s': 404 Not Found", $component));
                error_exit404('websiteatschool/' . $component);
            }
            break;
        default:
            logger(sprintf("Failed download websiteatschool/%s': 404 Not Found", $component));
            error_exit404('websiteatschool/' . $component);
            break;
    }
    //
    // Step 1 -- setup Ziparchive
    //
    include_once $CFG->progdir . '/lib/zip.class.php';
    $zip = new Zip();
    $comment = $CFG->www;
    if (!$zip->OpenZipstream($archive, $comment)) {
        $elapsed = diff_microtime($time_start, microtime());
        logger(sprintf("Failed download '%s' (%0.2f seconds): %s", $archive, $elapsed, $zip->Error));
        return FALSE;
    }
    //
    // Step 2 -- add files in the root directory (if any)
    //
    if (sizeof($files) > 0) {
        foreach ($files as $file) {
            $path = $CFG->dir . '/' . $file;
            if (!file_exists($path)) {
                logger(sprintf("%s(): missing file '%s' in archive '%s'", __FUNCTION__, $path, $archive), WLOG_DEBUG);
                $data = sprintf('<' . '?' . 'php echo "%s: file was not found"; ?' . '>', $file);
                $comment = sprintf('%s: missing', $file);
                if (!$zip->AddData($data, $file, $comment)) {
                    $elapsed = diff_microtime($time_start, microtime());
                    logger(sprintf("Failed download '%s' (%0.2f seconds): %s", $archive, $elapsed, $zip->Error));
                    $zip->CloseZip();
                    return FALSE;
                }
            } else {
                if (!$zip->AddFile($path, $file)) {
                    $elapsed = diff_microtime($time_start, microtime());
                    logger(sprintf("Failed download '%s' (%0.2f seconds): %s", $archive, $elapsed, $zip->Error));
                    $zip->CloseZip();
                    return FALSE;
                }
            }
        }
    }
    //
    // Step 3 -- add directories to archive
    //
    foreach ($directories as $vpath => $path) {
        if (!download_source_tree($zip, $path, $vpath, $excludes)) {
            $elapsed = diff_microtime($time_start, microtime());
            logger(sprintf("Failed download '%s' (%0.2f seconds): %s", $archive, $elapsed, $zip->Error));
            $zip->CloseZip();
            return FALSE;
        }
    }
    //
    // Step 4 -- we're done
    //
    if (!$zip->CloseZip()) {
        $elapsed = diff_microtime($time_start, microtime());
        logger(sprintf("Failed download '%s' (%0.2f seconds): %s", $archive, $elapsed, $zip->Error));
        return FALSE;
    }
    logger(sprintf("Download '%s' (%0.2f seconds): success", $archive, diff_microtime($time_start, microtime())));
    return TRUE;
}
function show_testmenu($default_test)
{
    global $tests, $PERFORMANCE, $DB;
    $s = "\n<p>\n<hr>\n<b>WAS TEST MENU</b> " . WAS_RELEASE . ' (' . WAS_VERSION . ")\n<hr>\n" . "<form method=\"get\" action=\"{$_SERVER['PHP_SELF']}\">\n" . "<input type=\"submit\" name=\"b\" value=\"OK\">\n<p>\n";
    foreach ($tests as $k => $v) {
        $checked = $k == $default_test ? ' checked' : '';
        $s .= '<input type="radio" name="test" value="' . htmlspecialchars($k) . "\"{$checked}>" . htmlspecialchars($v) . "<br>\n";
    }
    $s .= "<p>\nExtra 1: <input type=\"text\" name=\"extra1\" value=\"" . htmlspecialchars($_GET['extra1']) . "\" size=\"60\">\n";
    $s .= "<p>\n<input type=\"submit\" name=\"b\" value=\"OK\">\n</form>\n<hr>\n";
    // statistics
    $PERFORMANCE->time_stop = microtime();
    $s .= "Script execution time: <b>" . diff_microtime($PERFORMANCE->time_start, $PERFORMANCE->time_stop) . "</b> seconds.\n" . "Number of database queries: <b>" . $DB->query_counter . '</b>. ' . 'Current date: <b>' . strftime('%Y-%m-%d %T') . "</b>\n<hr>\n";
    echo $s;
}
/** return the script execution time
 *
 * @return double interval between begin execution and now
 * @todo maybe we should get rid of this $PERFORMANCE object,
 *       because it doesn't do that much anyway
 */
function performance_get_seconds()
{
    global $PERFORMANCE;
    $time_stop = microtime();
    return diff_microtime($PERFORMANCE->time_start, $time_stop);
}