mirror of
https://github.com/HaschekSolutions/pictshare.git
synced 2025-11-20 15:08:00 +00:00
added passthrough and updated documentation a bit
This commit is contained in:
@@ -57,6 +57,8 @@ Then open http://localhost:8080 in your browser
|
|||||||
|
|
||||||
#### Dropped configuration options
|
#### Dropped configuration options
|
||||||
- TITLE
|
- TITLE
|
||||||
|
- UPLOAD_FORM_LOCATION
|
||||||
|
- ALLOW_BLOATING
|
||||||
|
|
||||||
#### File name in URL
|
#### File name in URL
|
||||||
In Picthsare v3 we have changed the requirement for the image to be the last part of the URL. While with older PictShare Versions you could put the file name at any place of the URL, we now require the last part of the URL to be the file name.
|
In Picthsare v3 we have changed the requirement for the image to be the last part of the URL. While with older PictShare Versions you could put the file name at any place of the URL, we now require the last part of the URL to be the file name.
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: docker/Dockerfile
|
dockerfile: docker/Dockerfile
|
||||||
environment:
|
environment:
|
||||||
- SERVER_NAME=:80
|
|
||||||
- URL=http://localhost:8080/
|
- URL=http://localhost:8080/
|
||||||
- MAX_UPLOAD_SIZE=1000
|
- MAX_UPLOAD_SIZE=1000
|
||||||
# Security settings
|
# Security settings
|
||||||
@@ -43,8 +42,8 @@ services:
|
|||||||
- LOG_VIEWS=true
|
- LOG_VIEWS=true
|
||||||
# caching
|
# caching
|
||||||
- REDIS_CACHING=true
|
- REDIS_CACHING=true
|
||||||
- REDIS_SERVER=/run/redis/redis.sock
|
- REDIS_SERVER=localhost
|
||||||
- REDIS_PORT=0
|
- REDIS_PORT=6379
|
||||||
ports:
|
ports:
|
||||||
- 8080:80
|
- 8080:80
|
||||||
volumes:
|
volumes:
|
||||||
@@ -52,4 +51,5 @@ services:
|
|||||||
- ./logs:/app/public/logs
|
- ./logs:/app/public/logs
|
||||||
- ./src:/app/public/src
|
- ./src:/app/public/src
|
||||||
- ./web:/app/public/web
|
- ./web:/app/public/web
|
||||||
|
- ./tmp:/app/public/tmp
|
||||||
- ./tmp/redis:/var/lib/redis
|
- ./tmp/redis:/var/lib/redis
|
||||||
@@ -40,9 +40,8 @@ services:
|
|||||||
- LOG_VIEWS=false
|
- LOG_VIEWS=false
|
||||||
# caching
|
# caching
|
||||||
- REDIS_CACHING=true
|
- REDIS_CACHING=true
|
||||||
- REDIS_SERVER=/run/redis/redis.sock
|
- REDIS_SERVER=localhost
|
||||||
- REDIS_PORT=0
|
- REDIS_PORT=6379
|
||||||
- SERVER_NAME=:80 # required by caddy
|
|
||||||
ports:
|
ports:
|
||||||
- 8080:80
|
- 8080:80
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -29,11 +29,9 @@ _buildConfig() {
|
|||||||
echo "define('CONTENTCONTROLLERS', '${CONTENTCONTROLLERS:-}');"
|
echo "define('CONTENTCONTROLLERS', '${CONTENTCONTROLLERS:-}');"
|
||||||
echo "define('MASTER_DELETE_CODE', '${MASTER_DELETE_CODE:-}');"
|
echo "define('MASTER_DELETE_CODE', '${MASTER_DELETE_CODE:-}');"
|
||||||
echo "define('MASTER_DELETE_IP', '${MASTER_DELETE_IP:-}');"
|
echo "define('MASTER_DELETE_IP', '${MASTER_DELETE_IP:-}');"
|
||||||
echo "define('UPLOAD_FORM_LOCATION', '${UPLOAD_FORM_LOCATION:-}');"
|
|
||||||
echo "define('UPLOAD_CODE', '${UPLOAD_CODE:-}');"
|
echo "define('UPLOAD_CODE', '${UPLOAD_CODE:-}');"
|
||||||
echo "define('LOG_UPLOADER', ${LOG_UPLOADER:-false});"
|
echo "define('LOG_UPLOADER', ${LOG_UPLOADER:-false});"
|
||||||
echo "define('MAX_RESIZED_IMAGES',${MAX_RESIZED_IMAGES:--1});"
|
echo "define('MAX_RESIZED_IMAGES',${MAX_RESIZED_IMAGES:--1});"
|
||||||
echo "define('ALLOW_BLOATING', ${ALLOW_BLOATING:-false});"
|
|
||||||
echo "define('SHOW_ERRORS', ${SHOW_ERRORS:-false});"
|
echo "define('SHOW_ERRORS', ${SHOW_ERRORS:-false});"
|
||||||
echo "define('JPEG_COMPRESSION', ${JPEG_COMPRESSION:-90});"
|
echo "define('JPEG_COMPRESSION', ${JPEG_COMPRESSION:-90});"
|
||||||
echo "define('PNG_COMPRESSION', ${PNG_COMPRESSION:-6});"
|
echo "define('PNG_COMPRESSION', ${PNG_COMPRESSION:-6});"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class IdenticonController implements ContentController
|
|||||||
//returns all extensions registered by this type of content
|
//returns all extensions registered by this type of content
|
||||||
public function getRegisteredExtensions(){return array('identicon');}
|
public function getRegisteredExtensions(){return array('identicon');}
|
||||||
|
|
||||||
public function handleHash($hash,$url)
|
public function handleHash($hash,$url,$path=false)
|
||||||
{
|
{
|
||||||
unset($url[array_search('identicon',$url)]);
|
unset($url[array_search('identicon',$url)]);
|
||||||
$url = array_values($url);
|
$url = array_values($url);
|
||||||
@@ -29,7 +29,7 @@ class IdenticonController implements ContentController
|
|||||||
echo $icon;
|
echo $icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleUpload($tmpfile,$hash=false)
|
public function handleUpload($tmpfile,$hash=false,$passthrough=false)
|
||||||
{
|
{
|
||||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Cannot upload to Identicons');
|
return array('status'=>'err','hash'=>$hash,'reason'=>'Cannot upload to Identicons');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ImageController implements ContentController
|
|||||||
public function getRegisteredExtensions(){return array('png','bmp','gif','jpg','jpeg','x-png','webp');}
|
public function getRegisteredExtensions(){return array('png','bmp','gif','jpg','jpeg','x-png','webp');}
|
||||||
|
|
||||||
|
|
||||||
public function handleUpload($tmpfile,$hash=false)
|
public function handleUpload($tmpfile,$hash=false,$passthrough=false)
|
||||||
{
|
{
|
||||||
$type = exif_imagetype($tmpfile); //http://www.php.net/manual/en/function.exif-imagetype.php
|
$type = exif_imagetype($tmpfile); //http://www.php.net/manual/en/function.exif-imagetype.php
|
||||||
switch($type)
|
switch($type)
|
||||||
@@ -91,14 +91,16 @@ class ImageController implements ContentController
|
|||||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Custom hash already exists');
|
return array('status'=>'err','hash'=>$hash,'reason'=>'Custom hash already exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
storeFile($tmpfile,$hash,true);
|
if($passthrough===false)
|
||||||
|
storeFile($tmpfile,$hash,true);
|
||||||
|
|
||||||
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleHash($hash,$url)
|
public function handleHash($hash,$url,$path=false)
|
||||||
{
|
{
|
||||||
$path = getDataDir().DS.$hash.DS.$hash;
|
if($path===false)
|
||||||
|
$path = getDataDir().DS.$hash.DS.$hash;
|
||||||
$type = getExtensionOfFilename($hash);
|
$type = getExtensionOfFilename($hash);
|
||||||
|
|
||||||
//get all our sub files where all the good functions lie
|
//get all our sub files where all the good functions lie
|
||||||
@@ -151,7 +153,7 @@ class ImageController implements ContentController
|
|||||||
//so if we take all parameters in key=>value form and hash it
|
//so if we take all parameters in key=>value form and hash it
|
||||||
//we get one nice little hash for every eventuality
|
//we get one nice little hash for every eventuality
|
||||||
$modhash = md5(http_build_query($modifiers,'',','));
|
$modhash = md5(http_build_query($modifiers,'',','));
|
||||||
$newpath = getDataDir().DS.$hash.DS.$modhash.'_'.$hash;
|
$newpath = dirname($path).DS.$modhash.'_'.$hash;
|
||||||
$im = $this->getObjOfImage($path);
|
$im = $this->getObjOfImage($path);
|
||||||
$f = new Filter();
|
$f = new Filter();
|
||||||
|
|
||||||
@@ -183,7 +185,7 @@ class ImageController implements ContentController
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mp4':
|
case 'mp4':
|
||||||
$mp4path = getDataDir().DS.$hash.DS.$hash.'mp4';
|
$mp4path = dirname($path).DS.$hash.'mp4';
|
||||||
if(!file_exists($mp4path))
|
if(!file_exists($mp4path))
|
||||||
$this->gifToMP4($path,$mp4path);
|
$this->gifToMP4($path,$mp4path);
|
||||||
$path = $mp4path;
|
$path = $mp4path;
|
||||||
@@ -199,7 +201,7 @@ class ImageController implements ContentController
|
|||||||
}
|
}
|
||||||
|
|
||||||
header ("Content-type: image/jpeg");
|
header ("Content-type: image/jpeg");
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$preview));
|
serveFile($preview);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
else if(in_array('download',$url))
|
else if(in_array('download',$url))
|
||||||
@@ -212,7 +214,7 @@ class ImageController implements ContentController
|
|||||||
header('Cache-Control: must-revalidate');
|
header('Cache-Control: must-revalidate');
|
||||||
header('Pragma: public');
|
header('Pragma: public');
|
||||||
header('Content-Length: ' . filesize($path));
|
header('Content-Length: ' . filesize($path));
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$path));
|
serveFile($path);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,7 +246,7 @@ class ImageController implements ContentController
|
|||||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||||
header ("ETag: $hash");
|
header ("ETag: $hash");
|
||||||
header('Cache-control: public, max-age=31536000');
|
header('Cache-control: public, max-age=31536000');
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$path));
|
serveFile($path);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'png':
|
case 'png':
|
||||||
@@ -252,7 +254,7 @@ class ImageController implements ContentController
|
|||||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||||
header ("ETag: $hash");
|
header ("ETag: $hash");
|
||||||
header('Cache-control: public, max-age=31536000');
|
header('Cache-control: public, max-age=31536000');
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$path));
|
serveFile($path);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -261,7 +263,7 @@ class ImageController implements ContentController
|
|||||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||||
header ("ETag: $hash");
|
header ("ETag: $hash");
|
||||||
header('Cache-control: public, max-age=31536000');
|
header('Cache-control: public, max-age=31536000');
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$path));
|
serveFile($path);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'webp':
|
case 'webp':
|
||||||
@@ -269,7 +271,7 @@ class ImageController implements ContentController
|
|||||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||||
header ("ETag: $hash");
|
header ("ETag: $hash");
|
||||||
header('Cache-control: public, max-age=31536000');
|
header('Cache-control: public, max-age=31536000');
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$path));
|
serveFile($path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class PlaceholderController implements ContentController
|
|||||||
//returns all extensions registered by this type of content
|
//returns all extensions registered by this type of content
|
||||||
public function getRegisteredExtensions(){return array('placeholder');}
|
public function getRegisteredExtensions(){return array('placeholder');}
|
||||||
|
|
||||||
public function handleHash($hash,$url)
|
public function handleHash($hash,$url,$path=false)
|
||||||
{
|
{
|
||||||
$path = getDataDir().DS.$hash.DS.$hash;
|
$path = getDataDir().DS.$hash.DS.$hash;
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ class PlaceholderController implements ContentController
|
|||||||
imagejpeg($img,null,(defined('JPEG_COMPRESSION')?JPEG_COMPRESSION:90));
|
imagejpeg($img,null,(defined('JPEG_COMPRESSION')?JPEG_COMPRESSION:90));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleUpload($tmpfile,$hash=false)
|
public function handleUpload($tmpfile,$hash=false,$passthrough=false)
|
||||||
{
|
{
|
||||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Cannot upload to placeholder image');
|
return array('status'=>'err','hash'=>$hash,'reason'=>'Cannot upload to placeholder image');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class TextController implements ContentController
|
|||||||
//returns all extensions registered by this type of content
|
//returns all extensions registered by this type of content
|
||||||
public function getRegisteredExtensions(){return array('txt','text','csv');}
|
public function getRegisteredExtensions(){return array('txt','text','csv');}
|
||||||
|
|
||||||
public function handleHash($hash,$url)
|
public function handleHash($hash,$url,$path=false)
|
||||||
{
|
{
|
||||||
$path = getDataDir().DS.$hash.DS.$hash;
|
$path = getDataDir().DS.$hash.DS.$hash;
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ class TextController implements ContentController
|
|||||||
header('Cache-Control: must-revalidate');
|
header('Cache-Control: must-revalidate');
|
||||||
header('Pragma: public');
|
header('Pragma: public');
|
||||||
header('Content-Length: ' . filesize($path));
|
header('Content-Length: ' . filesize($path));
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$path));
|
serveFile($path);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ class TextController implements ContentController
|
|||||||
return renderTemplate('text.html.php',array('hash'=>$hash,'content'=>htmlentities(file_get_contents($path))));
|
return renderTemplate('text.html.php',array('hash'=>$hash,'content'=>htmlentities(file_get_contents($path))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleUpload($tmpfile,$hash=false)
|
public function handleUpload($tmpfile,$hash=false,$passthrough=false)
|
||||||
{
|
{
|
||||||
if($hash===false)
|
if($hash===false)
|
||||||
{
|
{
|
||||||
@@ -54,7 +54,8 @@ class TextController implements ContentController
|
|||||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Custom hash already exists');
|
return array('status'=>'err','hash'=>$hash,'reason'=>'Custom hash already exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
storeFile($tmpfile,$hash,true);
|
if($passthrough===false)
|
||||||
|
storeFile($tmpfile,$hash,true);
|
||||||
|
|
||||||
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ class UrlController implements ContentController
|
|||||||
|
|
||||||
//returns all extensions registered by this type of content
|
//returns all extensions registered by this type of content
|
||||||
public function getRegisteredExtensions(){return array('url');}
|
public function getRegisteredExtensions(){return array('url');}
|
||||||
public function handleHash($hash,$url){}
|
public function handleHash($hash,$url,$path=false){}
|
||||||
public function handleUpload($tmpfile,$hash=false){}
|
public function handleUpload($tmpfile,$hash=false,$passthrough=false){}
|
||||||
}
|
}
|
||||||
@@ -12,9 +12,10 @@ class VideoController implements ContentController
|
|||||||
//returns all extensions registered by this type of content
|
//returns all extensions registered by this type of content
|
||||||
public function getRegisteredExtensions(){return array('mp4');}
|
public function getRegisteredExtensions(){return array('mp4');}
|
||||||
|
|
||||||
public function handleHash($hash,$url)
|
public function handleHash($hash,$url,$path=false)
|
||||||
{
|
{
|
||||||
$path = getDataDir().DS.$hash.DS.$hash;
|
if($path===false)
|
||||||
|
$path = getDataDir().DS.$hash.DS.$hash;
|
||||||
|
|
||||||
//@todo: - resize by changing $path
|
//@todo: - resize by changing $path
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ class VideoController implements ContentController
|
|||||||
}
|
}
|
||||||
|
|
||||||
header ("Content-type: image/jpeg");
|
header ("Content-type: image/jpeg");
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$preview));
|
serveFile($preview);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if(in_array('download',$url))
|
else if(in_array('download',$url))
|
||||||
@@ -57,7 +58,7 @@ class VideoController implements ContentController
|
|||||||
header('Cache-Control: must-revalidate');
|
header('Cache-Control: must-revalidate');
|
||||||
header('Pragma: public');
|
header('Pragma: public');
|
||||||
header('Content-Length: ' . filesize($path));
|
header('Content-Length: ' . filesize($path));
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$path));
|
serveFile($path);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +70,7 @@ class VideoController implements ContentController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleUpload($tmpfile,$hash=false)
|
public function handleUpload($tmpfile,$hash=false,$passthrough=false)
|
||||||
{
|
{
|
||||||
if($hash===false)
|
if($hash===false)
|
||||||
$hash = getNewHash('mp4',6);
|
$hash = getNewHash('mp4',6);
|
||||||
@@ -80,10 +81,13 @@ class VideoController implements ContentController
|
|||||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Custom hash already exists');
|
return array('status'=>'err','hash'=>$hash,'reason'=>'Custom hash already exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = storeFile($tmpfile,$hash,true);
|
if($passthrough===false)
|
||||||
|
{
|
||||||
|
$file = storeFile($tmpfile,$hash,true);
|
||||||
|
|
||||||
if(!$this->rightEncodedMP4($file))
|
if(!$this->rightEncodedMP4($file))
|
||||||
system("nohup php ".ROOT.DS.'tools'.DS.'re-encode_mp4.php force '.$hash." > /dev/null 2> /dev/null &");
|
system("nohup php ".ROOT.DS.'tools'.DS.'re-encode_mp4.php force '.$hash." > /dev/null 2> /dev/null &");
|
||||||
|
}
|
||||||
|
|
||||||
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
return array('status'=>'ok','hash'=>$hash,'url'=>getURL().$hash);
|
||||||
}
|
}
|
||||||
@@ -96,7 +100,7 @@ class VideoController implements ContentController
|
|||||||
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
header ("Last-Modified: ".gmdate('D, d M Y H:i:s ', filemtime($path)) . 'GMT');
|
||||||
header ("ETag: ".sha1_file($path));
|
header ("ETag: ".sha1_file($path));
|
||||||
header('Cache-control: public, max-age=31536000');
|
header('Cache-control: public, max-age=31536000');
|
||||||
header('X-Accel-Redirect: '.str_replace(getDataDir().DS,'',$path));
|
serveFile($path);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class API
|
|||||||
}
|
}
|
||||||
|
|
||||||
return match ($this->url[0]) {
|
return match ($this->url[0]) {
|
||||||
|
'passthrough' => $this->passthrough(),
|
||||||
'upload' => $this->upload(),
|
'upload' => $this->upload(),
|
||||||
'delete' => $this->delete(),
|
'delete' => $this->delete(),
|
||||||
'info' => $this->info(),
|
'info' => $this->info(),
|
||||||
@@ -25,6 +26,39 @@ class API
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function passthrough(){
|
||||||
|
$type = $this->url[1];
|
||||||
|
array_shift($this->url); //remove the first element which is 'passthrough'
|
||||||
|
array_shift($this->url); //remove the second element which is the type so now what's left is the modifiers
|
||||||
|
$tmpfile = $_FILES['file']['tmp_name'] ?? false;
|
||||||
|
if (!$tmpfile || !is_file($tmpfile))
|
||||||
|
{
|
||||||
|
header('HTTP/1.1 400 Bad Request');
|
||||||
|
return array('status' => 'err', 'reason' => 'No file uploaded');
|
||||||
|
}
|
||||||
|
$tmp_target = $tmpfile;
|
||||||
|
//$tmp_target = ROOT.DS.'tmp'.DS.'pt-'.md5(rand(1,9999)).basename($_FILES['file']['name']);
|
||||||
|
//move_uploaded_file($tmpfile, $tmp_target);
|
||||||
|
|
||||||
|
switch($type) {
|
||||||
|
case 'image':
|
||||||
|
$controller = new ImageController();
|
||||||
|
$upload = $controller->handleUpload($tmp_target, false, true); //only to validate the file
|
||||||
|
if ($upload['status'] == 'ok') {
|
||||||
|
$controller->handleHash($upload['hash'], $this->url, $tmp_target);
|
||||||
|
unlink($tmp_target); //remove the temporary file
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
header('HTTP/1.1 400 Bad Request');
|
||||||
|
return $upload; //error
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return array('status' => 'err', 'reason' => 'Unknown passthrough type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function debug()
|
public function debug()
|
||||||
{
|
{
|
||||||
|
|||||||
108
src/inc/core.php
108
src/inc/core.php
@@ -18,12 +18,9 @@ if(!defined('FFMPEG_BINARY'))
|
|||||||
function architect($u)
|
function architect($u)
|
||||||
{
|
{
|
||||||
|
|
||||||
//if there is no info in the URL, don't even bother checking with the controllers
|
// check if client address is allowed
|
||||||
//just show the site
|
if($u[0] == '' || $u[0] == '/')
|
||||||
if( ( (!defined('UPLOAD_FORM_LOCATION') || (defined('UPLOAD_FORM_LOCATION') && !UPLOAD_FORM_LOCATION)) && count($u)==0) || (defined('UPLOAD_FORM_LOCATION') && UPLOAD_FORM_LOCATION && '/'.implode('/',$u)==UPLOAD_FORM_LOCATION) )
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// check if client address is allowed
|
|
||||||
$forbidden = false;
|
$forbidden = false;
|
||||||
if(defined('ALLOWED_SUBNET') && ALLOWED_SUBNET != '' && !isIPInRange( getUserIP(), ALLOWED_SUBNET ))
|
if(defined('ALLOWED_SUBNET') && ALLOWED_SUBNET != '' && !isIPInRange( getUserIP(), ALLOWED_SUBNET ))
|
||||||
{
|
{
|
||||||
@@ -99,6 +96,7 @@ function architect($u)
|
|||||||
|
|
||||||
//check all parts of the URL for a valid hash
|
//check all parts of the URL for a valid hash
|
||||||
$hash = false;
|
$hash = false;
|
||||||
|
$sc = getStorageControllers();
|
||||||
foreach($u as $el)
|
foreach($u as $el)
|
||||||
{
|
{
|
||||||
if(isExistingHash($el))
|
if(isExistingHash($el))
|
||||||
@@ -111,8 +109,6 @@ function architect($u)
|
|||||||
// if we don't have a hash yet but the element looks like it could be a hash
|
// if we don't have a hash yet but the element looks like it could be a hash
|
||||||
if($hash === false && mightBeAHash($el))
|
if($hash === false && mightBeAHash($el))
|
||||||
{
|
{
|
||||||
if(!$sc)
|
|
||||||
$sc = getStorageControllers();
|
|
||||||
foreach($sc as $contr)
|
foreach($sc as $contr)
|
||||||
{
|
{
|
||||||
$c = new $contr();
|
$c = new $contr();
|
||||||
@@ -363,69 +359,6 @@ function sizeStringToWidthHeight($size)
|
|||||||
return array('width'=>$maxwidth,'height'=>$maxheight);
|
return array('width'=>$maxwidth,'height'=>$maxheight);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// from: https://stackoverflow.com/questions/25975943/php-serve-mp4-chrome-provisional-headers-are-shown-request-is-not-finished-ye
|
|
||||||
//
|
|
||||||
function serveFile($filename, $filename_output = false, $mime = 'application/octet-stream')
|
|
||||||
{
|
|
||||||
$buffer_size = 8192;
|
|
||||||
$expiry = 90; //days
|
|
||||||
if(!file_exists($filename))
|
|
||||||
{
|
|
||||||
throw new Exception('File not found: ' . $filename);
|
|
||||||
}
|
|
||||||
if(!is_readable($filename))
|
|
||||||
{
|
|
||||||
throw new Exception('File not readable: ' . $filename);
|
|
||||||
}
|
|
||||||
header_remove('Cache-Control');
|
|
||||||
header_remove('Pragma');
|
|
||||||
$byte_offset = 0;
|
|
||||||
$filesize_bytes = $filesize_original = filesize($filename);
|
|
||||||
header('Accept-Ranges: bytes', true);
|
|
||||||
header('Content-Type: ' . $mime, true);
|
|
||||||
|
|
||||||
header("Content-Disposition: inline;");
|
|
||||||
// Content-Range header for byte offsets
|
|
||||||
if (isset($_SERVER['HTTP_RANGE']) && preg_match('%bytes=(\d+)-(\d+)?%i', $_SERVER['HTTP_RANGE'], $match))
|
|
||||||
{
|
|
||||||
$byte_offset = (int) $match[1];//Offset signifies where we should begin to read the file
|
|
||||||
if (isset($match[2]))//Length is for how long we should read the file according to the browser, and can never go beyond the file size
|
|
||||||
{
|
|
||||||
$filesize_bytes = min((int) $match[2], $filesize_bytes - $byte_offset);
|
|
||||||
}
|
|
||||||
header("HTTP/1.1 206 Partial content");
|
|
||||||
header(sprintf('Content-Range: bytes %d-%d/%d', $byte_offset, $filesize_bytes - 1, $filesize_original)); ### Decrease by 1 on byte-length since this definition is zero-based index of bytes being sent
|
|
||||||
}
|
|
||||||
$byte_range = $filesize_bytes - $byte_offset;
|
|
||||||
header('Content-Length: ' . $byte_range);
|
|
||||||
header('Expires: ' . date('D, d M Y H:i:s', time() + 60 * 60 * 24 * $expiry) . ' GMT');
|
|
||||||
$buffer = '';
|
|
||||||
$bytes_remaining = $byte_range;
|
|
||||||
$handle = fopen($filename, 'r');
|
|
||||||
if(!$handle)
|
|
||||||
{
|
|
||||||
throw new Exception("Could not get handle for file: " . $filename);
|
|
||||||
}
|
|
||||||
if (fseek($handle, $byte_offset, SEEK_SET) == -1)
|
|
||||||
{
|
|
||||||
throw new Exception("Could not seek to byte offset %d", $byte_offset);
|
|
||||||
}
|
|
||||||
while ($bytes_remaining > 0)
|
|
||||||
{
|
|
||||||
$chunksize_requested = min($buffer_size, $bytes_remaining);
|
|
||||||
$buffer = fread($handle, $chunksize_requested);
|
|
||||||
$chunksize_real = strlen($buffer);
|
|
||||||
if ($chunksize_real == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$bytes_remaining -= $chunksize_real;
|
|
||||||
echo $buffer;
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanatizeString($string)
|
function sanatizeString($string)
|
||||||
{
|
{
|
||||||
return preg_replace("/[^a-zA-Z0-9._\-]+/", "", $string);
|
return preg_replace("/[^a-zA-Z0-9._\-]+/", "", $string);
|
||||||
@@ -1277,3 +1210,38 @@ function getFileMimeType($file)
|
|||||||
return trim($mimeType);
|
return trim($mimeType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRelativeToDataPath(string $path): string
|
||||||
|
{
|
||||||
|
// Resolve real paths
|
||||||
|
$realBase = realpath(ROOT.DS.'data');
|
||||||
|
$realPath = realpath($path);
|
||||||
|
|
||||||
|
if ($realBase === false || $realPath === false) {
|
||||||
|
throw new InvalidArgumentException("Invalid path or base directory: $path, ".ROOT.DS.'data');
|
||||||
|
}
|
||||||
|
|
||||||
|
$baseParts = explode(DIRECTORY_SEPARATOR, trim($realBase, DIRECTORY_SEPARATOR));
|
||||||
|
$pathParts = explode(DIRECTORY_SEPARATOR, trim($realPath, DIRECTORY_SEPARATOR));
|
||||||
|
|
||||||
|
// Find common path length
|
||||||
|
$i = 0;
|
||||||
|
while (isset($baseParts[$i], $pathParts[$i]) && $baseParts[$i] === $pathParts[$i]) {
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// How many directories to go up from base
|
||||||
|
$upDirs = count($baseParts) - $i;
|
||||||
|
$relativeParts = array_merge(array_fill(0, $upDirs, '..'), array_slice($pathParts, $i));
|
||||||
|
|
||||||
|
return implode('/', $relativeParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function serveFile($path){
|
||||||
|
$relativePath = getRelativeToDataPath($path);
|
||||||
|
//since x-accel-redirect does not support paths outside its root, we need to check if the path is relative or absolute
|
||||||
|
if(startsWith($relativePath,'..'))
|
||||||
|
readfile($path);
|
||||||
|
else
|
||||||
|
header('X-Accel-Redirect: '. $relativePath);
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ interface ContentController
|
|||||||
* @param string $hash the hash (with extension eg '5saB2.pdf') of the file this controller will work with
|
* @param string $hash the hash (with extension eg '5saB2.pdf') of the file this controller will work with
|
||||||
* @param array $url contains all URL elements exploded with '/' so you can do your magic.
|
* @param array $url contains all URL elements exploded with '/' so you can do your magic.
|
||||||
*/
|
*/
|
||||||
public function handleHash($hash,$url);
|
public function handleHash($hash,$url,$path=false);
|
||||||
|
|
||||||
/** This method will be called if the upload script detects the content of a newly uploaded file as one of the
|
/** This method will be called if the upload script detects the content of a newly uploaded file as one of the
|
||||||
* extensions registered at "getRegisteredExtensions".
|
* extensions registered at "getRegisteredExtensions".
|
||||||
@@ -41,5 +41,5 @@ interface ContentController
|
|||||||
* @param string $tmpfile is the location on disk of the temp file that was uploaded. It is your job to put it somewhere, your handleHash method will find it again
|
* @param string $tmpfile is the location on disk of the temp file that was uploaded. It is your job to put it somewhere, your handleHash method will find it again
|
||||||
* @param array $hash (optional) if you want your upload to have a certain hash then add it here. This allows for user chosen hashes
|
* @param array $hash (optional) if you want your upload to have a certain hash then add it here. This allows for user chosen hashes
|
||||||
*/
|
*/
|
||||||
public function handleUpload($tmpfile,$hash=false);
|
public function handleUpload($tmpfile,$hash=false,$passthrough=false);
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,26 @@ if (file_exists(ROOT . DS . 'notice.txt'))
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
|
<h2>On-the-fly modifications to images</h2>
|
||||||
|
|
||||||
|
<p>You can modify images on-the-fly (without saving them to the server) by specifying additional parameters in the API call to use Pictshare to "clean up" or "normalize" images uploaded to your app. The passthrough endpoint allows you to use the normal image modifiers in the URL. It won't save the image on the Server and will respond with the modified image directly.</p>
|
||||||
|
|
||||||
|
API call
|
||||||
|
<pre><code class="url">/passthrough/image/[modifiers]</code></pre>
|
||||||
|
|
||||||
|
<p>For example to force an uploaded image to 400x400 and have it grayscale you can do this:</p>
|
||||||
|
|
||||||
|
CURL example
|
||||||
|
<pre><code class="bash">curl -s -F "file=@myphoto.jpg" "<?= getURL() ?>api/passthrough/image/400x400/forcesize/gray" > result.jpg</code></pre>
|
||||||
|
|
||||||
|
If the status code is anything other than "200", it will return an error message in JSON format.
|
||||||
|
<pre><code class="json">
|
||||||
|
{
|
||||||
|
"status": "err",
|
||||||
|
"reason": "Not a valid image"
|
||||||
|
}</code></pre>
|
||||||
|
|
||||||
|
If the status code is "200", it will return the modified image directly.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user