scratch – Rev 66

Subversion Repositories:
Rev:
<?php

###########################################################################
##  Copyright (C) Wizardry and Steamworks 2017 - License: GNU GPLv3      ##
###########################################################################

require_once('php/pseudocrypt.php');
require_once('php/functions.php');
require_once('config.php');

#### POST -> upload / GET -> download
switch ($_SERVER['REQUEST_METHOD']) {
    case 'POST':
        #### Retrieve uploaded file.
        if (!empty($_FILES['file']) and
            is_uploaded_file($_FILES['file']['tmp_name'])) {
            if($_FILES['file']['size'] > $ALLOWED_ASSET_SIZE * 1048576) {
                header('File size exceeds '.$ALLOWED_ASSET_SIZE.'MiB.', true, 403);
                return;
            }
            # Regular multipart/form-data upload.
            $name = $_FILES['file']['name'];
            $data = atomized_get_contents($_FILES['file']['tmp_name']);
        } else {
            if((int)get_file_size("php://input") > $ALLOWED_ASSET_SIZE * 1048576) {
                header('File size exceeds '.$ALLOWED_ASSET_SIZE.'MiB.', true, 403);
                return;
            }
            # Raw POST data.
            $name = urldecode(@$_SERVER['HTTP_X_FILE_NAME']);
            $data = atomized_get_contents("php://input");
        }

        #### Grab the file extension.
        $fileExtension = pathinfo($name, PATHINFO_EXTENSION);

        #### If the extension is not allowed then change it to a text extension.
        if (!isset($fileExtension) ||
            !in_array(strtoupper($fileExtension),
                array_map('strtoupper', $ALLOWED_FILE_EXTENSIONS))) {
            header('File extension not allowed.', true, 403);
            return;
        }
    
        #### Hash filename.
        $file = strtolower(
            PseudoCrypt::hash(
                preg_replace(
                    '/\D/',
                    '',
                    hash(
                        'sha512',
                        $data
                    )
                ),
                $ASSET_HASH_SIZE
            )
        );

        #### Build the user path.
        $userPath = join(
            DIRECTORY_SEPARATOR,
            array(
                $STORE_FOLDER,
                $file
            )
        );

        #### Check for path traversals
        $pathPart = pathinfo($userPath.'.'.$fileExtension);
        if (strcasecmp(
            realpath($pathPart['dirname']), realpath($STORE_FOLDER)) != 0) {
            header('Internal server error.', true, 500);
            return;
        }

        #### Store the file.
        atomized_put_contents($userPath.'.'.$fileExtension, $data);

        ### Return the URL to the file.
        header('Content-Type: text/plain; charset=utf-8');
        echo sprintf('%s/%s', trim($URL_PATH, '/'), $file);
    break;
    case 'GET':
        ### If no file has been specified for download then return.
        if (!isset($_GET['o']) or empty($_GET['o'])) {
            header('File not found.', true, 404);
            return;
        }

        ### Find the requested file.
        $file = array_shift(
            preg_grep(
                "/$_GET[o]/",
                scandir($STORE_FOLDER)
            )
        );

        if (!isset($file) or empty($file))
            return;
        
        ### Check the path for path traversals.
        $fileExtension = pathinfo($file, PATHINFO_EXTENSION);

        #### If the extension is not allowed then return.
        if (!isset($fileExtension) ||
            !in_array(strtoupper($fileExtension),
                array_map('strtoupper', $ALLOWED_FILE_EXTENSIONS))) {
            header('File extension not allowed.', true, 403);
            return;
        }
        
        #### Build the user path.
        $userPath = join(
            DIRECTORY_SEPARATOR,
            array(
                $STORE_FOLDER,
                $file
            )
        );

        #### Check for path traversals
        $pathPart = pathinfo($userPath);
        if (strcasecmp(
            realpath($pathPart['dirname']), realpath($STORE_FOLDER)) != 0) {
            header('Internal server error.', true, 500);
            return;
        }

        ### Hook for HTML files to display properly.
        switch(strtoupper($fileExtension)) {
            case "HTML":
            case "HTM":
                header('Content-type: text/html');
                break;
            break;
            default:
                ### Open MIME info database and send the content type.
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                if (!$finfo) {
                    header('Internal server error.', true, 500);
                    return;
                }
                header('Content-type: '.finfo_file($finfo, $userPath));
                finfo_close($finfo);
            break;
        }
        
        ### Send the file along with the inline content disposition.
        header('Content-length: '.(int)get_file_size($userPath));
        header('Content-Disposition: inline; filename="' . basename($userPath) . '"');
        header('Content-Transfer-Encoding: binary');
        header('X-Sendfile: '.$userPath);
    break;
}