PHP Classes

File: vault/upload.php

Recommend this page to a friend!
  Classes of Caleb  >  PHP Mussel  >  vault/upload.php  >  Download  
File: vault/upload.php
Role: Example script
Content type: text/plain
Description: Example script
Class: PHP Mussel
PHP file virus scanner to detect malware
Author: By
Last change:
Date: 3 years ago
Size: 20,245 bytes
 

Contents

Class file image Download
<?php
/**
 * This file is a part of the phpMussel package.
 * Homepage: https://phpmussel.github.io/
 *
 * PHPMUSSEL COPYRIGHT 2013 AND BEYOND BY THE PHPMUSSEL TEAM.
 *
 * Authors:
 * @see PEOPLE.md
 *
 * License: GNU/GPLv2
 * @see LICENSE.txt
 *
 * This file: Upload handler (last modified: 2019.04.06).
 */

/** Prevents execution from outside of phpMussel. */
if (!defined('phpMussel')) {
    die('[phpMussel] This should not be accessed directly.');
}

/** Sets default error handler for the upload handler. */
set_error_handler($phpMussel['ErrorHandler_1']);

/** Create an array for our working data. */
$phpMussel['upload'] = [];

/**
 * We need to count the number of elements contained by the $_FILES array, to
 * know if there are any file uploads to be scanned during execution of the
 * script.
 */
$phpMussel['upload']['count'] = empty($_FILES) ? 0 : count($_FILES);

/** Only continue if there are uploads to deal with. */
if ($phpMussel['upload']['count'] > 0 && !$phpMussel['Config']['general']['maintenance_mode']) {

    /**
     * Whenever something is detected in a file being scanned, a human-readable
     * description of what has been detected is appended to
     * $phpMussel['whyflagged']; This variable contains the "Detected ... !"
     * message you see on the "Upload Denied!" page generated whenever a file
     * upload has been blocked.
     *
     * The $phpMussel['killdata'] variable contains hashes referencing every
     * file upload blocked and every file where something has been detected in
     * that file, in the form of "HASH:FILESIZE:FILENAME".
     *
     * If the file being scanned happens to be a PE file, in addition to the
     * information appended to $phpMussel['whyflagged'] and
     * $phpMussel['killdata'], reconstructed references to the PE sections of
     * that PE file will be appended to the $phpMussel['PEData'] variable.
     *
     * If logging is enabled, the information contained by these three
     * variables will all be logged as per the specified by the logging-related
     * directives of the phpMussel configuration file.
     */
    $phpMussel['whyflagged'] = $phpMussel['killdata'] = $phpMussel['PEData'] = '';

    /** Process the hash cache. */
    $phpMussel['PrepareHashCache']();

    /** File upload scan start time. */
    $phpMussel['InstanceCache']['start_time'] = time() + ($phpMussel['Config']['general']['timeOffset'] * 60);

    /** Create an array for normalising the $_FILES data. */
    $phpMussel['upload']['FilesData'] = [];

    /** Generate "HONEYPOT EVENT" header (if "honeypot_mode" is enabled). */
    if ($phpMussel['Config']['general']['honeypot_mode']) {
        $phpMussel['InstanceCache']['Handle'] = ['qdata' =>
            "== HONEYPOT EVENT ==\nDATE: " . $phpMussel['TimeFormat'](
                $phpMussel['InstanceCache']['start_time'],
                $phpMussel['Config']['general']['timeFormat']
            ) . "\nIP ADDRESS: " . ($phpMussel['Config']['legal']['pseudonymise_ip_addresses'] ? $phpMussel['Pseudonymise-IP'](
                $_SERVER[$phpMussel['IPAddr']]
            ) : $_SERVER[$phpMussel['IPAddr']]) . "\n"
        ];
    }

    foreach ($_FILES as $phpMussel['ThisFileKey'] => $phpMussel['ThisFileData']) {
        if (!isset($phpMussel['ThisFileData']['error'])) {
            continue;
        }
        /** Normalise the structure of the uploads array. */
        if (!is_array($phpMussel['ThisFileData']['error'])) {
            $phpMussel['upload']['FilesData']['FileSet'] = [
                'name' => [$phpMussel['ThisFileData']['name']],
                'type' => [$phpMussel['ThisFileData']['type']],
                'tmp_name' => [$phpMussel['ThisFileData']['tmp_name']],
                'error' => [$phpMussel['ThisFileData']['error']],
                'size' => [$phpMussel['ThisFileData']['size']]
            ];
        } else {
            $phpMussel['upload']['FilesData']['FileSet'] = $phpMussel['ThisFileData'];
        }
        $phpMussel['ThisFilesCount'] = count($phpMussel['upload']['FilesData']['FileSet']['error']);

        for (
            $phpMussel['ThisIter'] = 0, $phpMussel['SkipSerial'] = true;
            $phpMussel['ThisIter'] < $phpMussel['ThisFilesCount'];
            $phpMussel['ThisIter']++
        ) {
            if (!isset($phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['ThisIter']])) {
                $phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['ThisIter']] = '';
            }
            if (!isset($phpMussel['upload']['FilesData']['FileSet']['type'][$phpMussel['ThisIter']])) {
                $phpMussel['upload']['FilesData']['FileSet']['type'][$phpMussel['ThisIter']] = '';
            }
            if (!isset($phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']])) {
                $phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']] = '';
            }
            if (!isset($phpMussel['upload']['FilesData']['FileSet']['error'][$phpMussel['ThisIter']])) {
                $phpMussel['upload']['FilesData']['FileSet']['error'][$phpMussel['ThisIter']] = 0;
            }
            if (!isset($phpMussel['upload']['FilesData']['FileSet']['size'][$phpMussel['ThisIter']])) {
                $phpMussel['upload']['FilesData']['FileSet']['size'][$phpMussel['ThisIter']] = 0;
            }

            unset($phpMussel['upload']['ThisError']);
            $phpMussel['upload']['ThisError'] = &$phpMussel['upload']['FilesData']['FileSet']['error'][$phpMussel['ThisIter']];

            /** Handle upload errors. */
            if ($phpMussel['upload']['ThisError'] > 0) {
                if (
                    $phpMussel['Config']['compatibility']['ignore_upload_errors'] ||
                    $phpMussel['upload']['ThisError'] > 8 ||
                    $phpMussel['upload']['ThisError'] === 5
                ) {
                    continue;
                }
                $phpMussel['killdata'] .= sprintf(
                    "---------UPLOAD-ERROR-%d---------:%d:%s\n",
                    $phpMussel['upload']['ThisError'],
                    $phpMussel['upload']['FilesData']['FileSet']['size'][$phpMussel['ThisIter']],
                    $phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['ThisIter']]
                );
                $phpMussel['whyflagged'] .= $phpMussel['L10N']->getString((
                    $phpMussel['upload']['ThisError'] === 3 || $phpMussel['upload']['ThisError'] === 4
                ) ? 'upload_error_34' : 'upload_error_' . $phpMussel['upload']['ThisError']);
                if (
                    ($phpMussel['upload']['ThisError'] === 1 || $phpMussel['upload']['ThisError'] === 2) &&
                    $phpMussel['Config']['general']['delete_on_sight'] &&
                    is_uploaded_file($phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']]) &&
                    is_readable($phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']])
                ) {
                    unlink($phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']]);
                }
                continue;
            }

            /** Protection against upload spoofing. */
            if (!is_uploaded_file($phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']])) {
                $phpMussel['killdata'] .= sprintf(
                    "UNAUTHORISED-FILE-UPLOAD-NO-HASH:%d:%s\n",
                    $phpMussel['upload']['FilesData']['FileSet']['size'][$phpMussel['ThisIter']],
                    $phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['ThisIter']]
                );
                $phpMussel['whyflagged'] .= sprintf($phpMussel['L10N']->getString('_exclamation'), sprintf(
                    '%s (%s)',
                    $phpMussel['L10N']->getString('scan_unauthorised_upload'),
                    $phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['ThisIter']]
                ));
            } elseif (
                !$phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['ThisIter']] ||
                !$phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']]
            ) {
                $phpMussel['killdata'] .= "-UNAUTHORISED-UPLOAD-MISCONFIG-:?:?\n";
                $phpMussel['whyflagged'] .= $phpMussel['L10N']->getString('scan_unauthorised_upload_or_misconfig');
            } else {

                /** Honeypot code. */
                if (
                    $phpMussel['Config']['general']['honeypot_mode'] &&
                    $phpMussel['Config']['general']['quarantine_key']
                ) {
                    $phpMussel['ReadFile-For-Honeypot'](
                        $phpMussel['InstanceCache']['Handle'],
                        $phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']]
                    );
                }

                /** Process this block if the number of files being uploaded exceeds "max_uploads". */
                if (
                    $phpMussel['upload']['count'] > $phpMussel['Config']['files']['max_uploads'] &&
                    $phpMussel['Config']['files']['max_uploads'] >= 1
                ) {
                    $phpMussel['KillAndUnlink']();
                    continue;
                }

                /** Used for serialised logging. */
                if ($phpMussel['ThisIter'] === ($phpMussel['ThisFilesCount'] - 1)) {
                    unset($phpMussel['SkipSerial']);
                }

                /** Execute the scan! */
                try {
                    $phpMussel['r'] = $phpMussel['Scan'](
                        $phpMussel['upload']['FilesData']['FileSet']['tmp_name'][$phpMussel['ThisIter']],
                        true,
                        true,
                        0,
                        $phpMussel['upload']['FilesData']['FileSet']['name'][$phpMussel['ThisIter']]
                    );
                } catch (\Exception $e) {
                    die($e->getMessage());
                }

            }
        }
    }
    /** Cleanup. */
    unset($phpMussel['r'], $phpMussel['ThisIter'], $phpMussel['ThisFilesCount'], $phpMussel['ThisFileKey'], $phpMussel['ThisFileData']);

    /** Honeypot code. */
    if (
        $phpMussel['Config']['general']['honeypot_mode'] &&
        $phpMussel['Config']['general']['scan_kills'] &&
        is_array($phpMussel['InstanceCache']['Handle'])
    ) {
        $phpMussel['InstanceCache']['Handle']['File'] = $phpMussel['TimeFormat']($phpMussel['Time'], $phpMussel['Config']['general']['scan_kills']);
        $phpMussel['InstanceCache']['Handle']['WriteMode'] = (
            !file_exists($phpMussel['Vault'] . $phpMussel['InstanceCache']['Handle']['File']) || (
                $phpMussel['Config']['general']['truncate'] > 0 &&
                filesize($phpMussel['Vault'] . $phpMussel['InstanceCache']['Handle']['File']) >= $phpMussel['ReadBytes']($phpMussel['Config']['general']['truncate'])
            )
        ) ? 'w' : 'a';
        if (!file_exists($phpMussel['Vault'] . $phpMussel['InstanceCache']['Handle']['File'])) {
            $phpMussel['InstanceCache']['Handle']['qdata'] = $phpMussel['safety'] . "\n\n" . $phpMussel['InstanceCache']['Handle']['qdata'];
        }
        if ($phpMussel['BuildLogPath']($phpMussel['InstanceCache']['Handle']['File'])) {
            $phpMussel['InstanceCache']['Handle']['Stream'] = fopen(
                $phpMussel['Vault'] . $phpMussel['InstanceCache']['Handle']['File'],
                $phpMussel['InstanceCache']['Handle']['WriteMode']
            );
            fwrite($phpMussel['InstanceCache']['Handle']['Stream'], $phpMussel['InstanceCache']['Handle']['qdata']);
            fclose($phpMussel['InstanceCache']['Handle']['Stream']);
            if ($phpMussel['InstanceCache']['Handle']['WriteMode'] === 'w') {
                $phpMussel['LogRotation']($phpMussel['Config']['general']['scan_kills']);
            }
        }
        $phpMussel['InstanceCache']['Handle'] = '';
    }

    /** Update the hash cache. */
    if ($phpMussel['Config']['general']['scan_cache_expiry'] > 0 && !empty($phpMussel['HashCache']['Data']) && is_array($phpMussel['HashCache']['Data'])) {

        $phpMussel['HashCache']['Data'] = array_map(function ($Item) {
            return is_array($Item) ? implode(':', $Item) . ';' : $Item;
        }, $phpMussel['HashCache']['Data']);
        $phpMussel['HashCache']['Data'] = implode('', $phpMussel['HashCache']['Data']);

        /** Update hash cache. */
        $phpMussel['HashCache']['Data'] = $phpMussel['SaveCache'](
            'HashCache',
            $phpMussel['Time'] + $phpMussel['Config']['general']['scan_cache_expiry'],
            $phpMussel['HashCache']['Data']
        );
        unset($phpMussel['HashCache']);

    }

    /** File upload scan finish time. */
    $phpMussel['InstanceCache']['end_time'] = time() + ($phpMussel['Config']['general']['timeOffset'] * 60);

    /** Trim trailing whitespace. */
    $phpMussel['whyflagged'] = trim($phpMussel['whyflagged']);

    /** Begin processing file upload detections. */
    if ($phpMussel['whyflagged']) {

        /** A fix for correctly displaying LTR/RTL text. */
        if ($phpMussel['L10N']->getString('Text Direction') !== 'rtl') {
            $phpMussel['lang']['Text Direction'] = 'ltr';
        }

        /** Merging parsable variables for the template data. */
        $phpMussel['TemplateData'] = $phpMussel['lang'] + $phpMussel['Config']['template_data'];
        $phpMussel['TemplateData']['detected'] = $phpMussel['whyflagged'];
        $phpMussel['TemplateData']['phpmusselversion'] = $phpMussel['ScriptIdent'];
        $phpMussel['TemplateData']['favicon'] = $phpMussel['favicon'];
        $phpMussel['TemplateData']['xmlLang'] = $phpMussel['Config']['general']['lang'];

        /** Determine which template file to use, if this hasn't already been determined. */
        if (!isset($phpMussel['InstanceCache']['template_file'])) {
            $phpMussel['InstanceCache']['template_file'] = !$phpMussel['Config']['template_data']['css_url'] ?
                'template_' . $phpMussel['Config']['template_data']['theme'] . '.html' : 'template_custom.html';
        }

        /** Fallback for themes without default template files. */
        if (
            $phpMussel['Config']['template_data']['theme'] !== 'default' &&
            !$phpMussel['Config']['template_data']['css_url'] &&
            !file_exists($phpMussel['Vault'] . $phpMussel['InstanceCache']['template_file'])
        ) {
            $phpMussel['InstanceCache']['template_file'] = 'template_default.html';
        }

        /** Log "scan_kills" data. */
        if ($phpMussel['Config']['general']['scan_kills'] && !empty($phpMussel['killdata'])) {
            $phpMussel['InstanceCache']['Handle'] = [
                'File' => $phpMussel['TimeFormat']($phpMussel['Time'], $phpMussel['Config']['general']['scan_kills'])
            ];
            $phpMussel['InstanceCache']['Handle']['WriteMode'] = (
                !file_exists($phpMussel['Vault'] . $phpMussel['InstanceCache']['Handle']['File']) || (
                    $phpMussel['Config']['general']['truncate'] > 0 &&
                    filesize($phpMussel['Vault'] . $phpMussel['InstanceCache']['Handle']['File']) >= $phpMussel['ReadBytes']($phpMussel['Config']['general']['truncate'])
                )
            ) ? 'w' : 'a';
            $phpMussel['InstanceCache']['Handle']['Data'] = file_exists(
                $phpMussel['Vault'] . $phpMussel['InstanceCache']['Handle']['File']
            ) ? '' : $phpMussel['safety'] . "\n\n";
            $phpMussel['InstanceCache']['Handle']['Data'] .= sprintf(
                "%s: %s\n%s: %s\n== %s ==\n%s\n== %s ==\n%s",
                $phpMussel['L10N']->getString('field_date'),
                $phpMussel['TimeFormat']($phpMussel['Time'], $phpMussel['Config']['general']['timeFormat']),
                $phpMussel['L10N']->getString('field_ip_address'),
                ($phpMussel['Config']['legal']['pseudonymise_ip_addresses'] ? $phpMussel['Pseudonymise-IP'](
                    $_SERVER[$phpMussel['IPAddr']]
                ) : $_SERVER[$phpMussel['IPAddr']]),
                $phpMussel['L10N']->getString('field_header_scan_results_why_flagged'),
                $phpMussel['whyflagged'],
                $phpMussel['L10N']->getString('field_header_hash_reconstruction'),
                $phpMussel['killdata']
            );
            if ($phpMussel['PEData']) {
                $phpMussel['InstanceCache']['Handle']['Data'] .= sprintf(
                    "== %s ==\n%s",
                    $phpMussel['L10N']->getString('field_header_pe_reconstruction'),
                    $phpMussel['PEData']
                );
            }
            $phpMussel['InstanceCache']['Handle']['Data'] .= "\n";
            if ($phpMussel['BuildLogPath']($phpMussel['InstanceCache']['Handle']['File'])) {
                $phpMussel['InstanceCache']['Handle']['Stream'] = fopen(
                    $phpMussel['Vault'] . $phpMussel['InstanceCache']['Handle']['File'],
                    $phpMussel['InstanceCache']['Handle']['WriteMode']
                );
                fwrite($phpMussel['InstanceCache']['Handle']['Stream'], $phpMussel['InstanceCache']['Handle']['Data']);
                fclose($phpMussel['InstanceCache']['Handle']['Stream']);
                if ($phpMussel['InstanceCache']['Handle']['WriteMode'] === 'w') {
                    $phpMussel['LogRotation']($phpMussel['Config']['general']['scan_kills']);
                }
            }
            $phpMussel['InstanceCache']['Handle'] = '';
        }

        /** Fallback to use if the HTML template file is missing. */
        if (!file_exists($phpMussel['Vault'] . $phpMussel['InstanceCache']['template_file'])) {
            header('Content-Type: text/plain');
            die('[phpMussel] ' . $phpMussel['L10N']->getString('denied') . ' ' . $phpMussel['TemplateData']['detected']);
        }

        /** Send a 403 FORBIDDEN status code to the client if "forbid_on_block" is enabled. */
        if ($phpMussel['Config']['general']['forbid_on_block']) {
            header('HTTP/1.0 403 Forbidden');
            header('HTTP/1.1 403 Forbidden');
            header('Status: 403 Forbidden');
        }

        /** Include privacy policy. */
        $phpMussel['TemplateData']['pp'] = empty(
            $phpMussel['Config']['legal']['privacy_policy']
        ) ? '' : '<br /><a href="' . $phpMussel['Config']['legal']['privacy_policy'] . '">' . $phpMussel['L10N']->getString('PrivacyPolicy') . '</a>';

        /** Generate HTML output. */
        $phpMussel['HTML'] = $phpMussel['ParseVars'](
            $phpMussel['TemplateData'],
            $phpMussel['ReadFile']($phpMussel['Vault'] . $phpMussel['InstanceCache']['template_file'], 0, true)
        );

        /** Plugin hook: "before_html_out". */
        $phpMussel['Execute_Hook']('before_html_out');

        /** Handle webfonts. */
        if (empty($phpMussel['Config']['general']['disable_webfonts'])) {
            $phpMussel['HTML'] = str_replace(['<!-- WebFont Begin -->', '<!-- WebFont End -->'], '', $phpMussel['HTML']);
        } else {
            $phpMussel['WebFontPos'] = [
                'Begin' => strpos($phpMussel['HTML'], '<!-- WebFont Begin -->'),
                'End' => strpos($phpMussel['HTML'], '<!-- WebFont End -->')
            ];
            if ($phpMussel['WebFontPos']['Begin'] !== false && $phpMussel['WebFontPos']['End'] !== false) {
                $phpMussel['HTML'] = (
                    substr($phpMussel['HTML'], 0, $phpMussel['WebFontPos']['Begin']) .
                    substr($phpMussel['HTML'], $phpMussel['WebFontPos']['End'] + 20)
                );
            }
            unset($phpMussel['WebFontPos']);
        }

        /** Send HTML output and the kill the script. */
        die($phpMussel['HTML']);

    }

}

/** Exit the upload handler cleanly. */
unset($phpMussel['upload']);

/** Restores default error handler. */
restore_error_handler();
For more information send a message to info at phpclasses dot org.