Skip to content

Instantly share code, notes, and snippets.

@Zegnat
Created May 1, 2016 16:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zegnat/7d37f802579c21f5908262b09d25d913 to your computer and use it in GitHub Desktop.
Save Zegnat/7d37f802579c21f5908262b09d25d913 to your computer and use it in GitHub Desktop.
<?php
/**
* A minimal PhotoBackup API endpoint developed in PHP.
*
* @version 1.0.0
* @author Martijn van der Ven <martijn@zegnat.net>
* @copyright 2015 Martijn van der Ven
* @license http://opensource.org/licenses/MIT The MIT License
*/
/**
* The password required to upload to this server.
*
* The password is currently stored as clear text here. PHP code is not normally
* readable by third-parties so this should be safe enough. Many applications
* store database credentials in this way as well. A secondary and safer way is
* being considered for the next version.
*
* @global string $Password A string (encapsulated by quotes).
*/
$Password = 'example';
/**
* The directory where files should be uploaded to.
*
* This directory path is relative to this index.php file and should not end
* with a /. It should point to an existing directory on the server.
*
* @global string $MediaRoot A string (encapsulated by quotes).
*/
$MediaRoot = 'photos';
// -----------------------------------------------------------------------------
// NO CONFIGURATION NECCESSARY BEYOND THIS POINT.
// -----------------------------------------------------------------------------
/**
* Establish what HTTP version is being used by the server.
*/
if (isset($_SERVER['SERVER_PROTOCOL'])) {
$protocol = $_SERVER['SERVER_PROTOCOL'];
} else {
$protocol = 'HTTP/1.0';
}
/**
* Find out if the client is requesting the test page.
*/
$request = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$testing = substr($request, -5) === '/test';
/**
* If no data has been POSTed to the server and the client did not request the
* test page, exit imidiately.
*/
if (empty($_POST) && !$testing) {
exit();
}
/**
* If we are testing the server and see that no password has been set, exit with
* HTTP code 401.
*/
if (
$testing &&
(
!isset($Password) ||
!is_string($Password)
)
) {
header($protocol . ' 401 Unauthorized');
exit();
}
/**
* Exit with HTTP code 403 if no password has been set on the server, or if the
* client did not submit a password, or the submitted password did not match
* this server's password.
*/
if (
!isset($Password) ||
!is_string($Password) ||
!isset($_POST['password']) ||
$_POST['password'] !== hash('sha512', $Password)
) {
header($protocol . ' 403 Forbidden');
exit();
}
/**
* If the upload destination folder has not been configured, does not exist, or
* is not writable by PHP, exit with HTTP code 500.
*/
if (
!isset($MediaRoot) ||
!is_string($MediaRoot) ||
!file_exists($MediaRoot) ||
!is_dir($MediaRoot) ||
!is_writable($MediaRoot)
) {
header($protocol . ' 500 Internal Server Error');
exit();
}
/**
* If we were only supposed to test the server, end here.
*/
if ($testing) {
exit();
}
/**
* If the client did not submit a filesize, exit with HTTP code 400.
*/
if (!isset($_POST['filesize'])) {
header($protocol . ' 400 Bad Request');
exit();
}
/**
* If the client did not upload a file, or something went wrong in the upload
* process, exit with HTTP code 401.
*/
if (
!isset($_FILES['upfile']) ||
$_FILES['upfile']['error'] !== UPLOAD_ERR_OK ||
!is_uploaded_file($_FILES['upfile']['tmp_name'])
) {
header($protocol . ' 401 Unauthorized');
exit();
}
/**
* If the client submitted filesize did not match the uploaded file's size, exit
* with HTTP code 411.
*/
if (intval($_POST['filesize']) !== $_FILES['upfile']['size']) {
header($protocol . ' 411 Length Required');
exit();
}
/**
* Sanitize the file name to maximise server operating system compatibility and
* minimize possible attacks against this implementation.
*/
$filename = explode('.', $_FILES['upfile']['name']);
array_splice($filename, -1, 0, array(md5_file($_FILES['upfile']['tmp_name'])));
$filename = implode('.', $filename);
$filename = preg_replace('@\s+@', '-', $filename);
$filename = preg_replace('@[^0-9a-z._-]@i', '', $filename);
$target = $MediaRoot . '/' . $filename;
/**
* If a file with the same name and size exists, treat the new upload as a
* duplicate and exit.
*/
if (
file_exists($target) &&
filesize() === $_POST['filesize']
) {
header($protocol . ' 409 Conflict');
exit();
}
/**
* Move the uploaded file into the target directory. If anything did not work,
* exit with HTTP code 500.
*/
if (!move_uploaded_file($_FILES["upfile"]["tmp_name"], $target)) {
header($protocol . ' 500 Internal Server Error');
exit();
}
exit();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment