implemented alt_folder and reorganized interface class code

This commit is contained in:
Chris
2018-12-23 11:59:31 +01:00
parent 3345eb399c
commit b6612cd838
15 changed files with 258 additions and 56 deletions

View File

@@ -1,7 +1,4 @@
# PictShare # PictShare version 2
**[Live Demo](https://www.pictshare.net)**
PictShare is a selfhostable, open source image, video and text hosting as well as URL shortening service with a simple API.
--- ---
[![Apache License](https://img.shields.io/badge/license-Apache-blue.svg?style=flat)](https://github.com/HaschekSolutions/pictshare/blob/master/LICENSE) [![Apache License](https://img.shields.io/badge/license-Apache-blue.svg?style=flat)](https://github.com/HaschekSolutions/pictshare/blob/master/LICENSE)
@@ -20,6 +17,18 @@ Test site: https://dev.pictshare.net/ (only sometimes on)
- [x] Duplicate detection - [x] Duplicate detection
- [x] Write permission detection - [x] Write permission detection
### Config options
- [x] ALT_FOLDER
- [x] URL
- [x] LOG_UPLOADER
- [x] FFMPEG_BINARY
- [ ] PNG_COMPRESSION
- [ ] JPEG_COMPRESSION
- [ ] MASTER_DELETE_CODE
- [ ] MASTER_DELETE_IP
- [ ] UPLOAD_CODE
### Image hosting ### Image hosting
- [ ] Resizing - [ ] Resizing
- [ ] Filters - [ ] Filters

View File

@@ -10,7 +10,7 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
//loading core and controllers //loading core and controllers
include_once(ROOT . DS . 'inc' . DS. 'core.php'); include_once(ROOT . DS . 'inc' . DS. 'core.php');
require_once(ROOT . DS . 'controllers' . DS. 'text'. DS . 'text.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'text'. DS . 'text.controller.php');
// check write permissions first // check write permissions first
if(!isFolderWritable(ROOT.DS.'data')) if(!isFolderWritable(ROOT.DS.'data'))

View File

@@ -10,10 +10,10 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
//loading core and controllers //loading core and controllers
include_once(ROOT . DS . 'inc' . DS. 'core.php'); include_once(ROOT . DS . 'inc' . DS. 'core.php');
require_once(ROOT . DS . 'controllers' . DS. 'image'. DS . 'image.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'image'. DS . 'image.controller.php');
require_once(ROOT . DS . 'controllers' . DS. 'text'. DS . 'text.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'text'. DS . 'text.controller.php');
require_once(ROOT . DS . 'controllers' . DS. 'url'. DS . 'url.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'url'. DS . 'url.controller.php');
require_once(ROOT . DS . 'controllers' . DS. 'video'. DS . 'video.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'video'. DS . 'video.controller.php');
// check write permissions first // check write permissions first
if(!isFolderWritable(ROOT.DS.'data')) if(!isFolderWritable(ROOT.DS.'data'))
@@ -57,8 +57,19 @@ if ($_FILES['file']["error"] == UPLOAD_ERR_OK)
$answer = array('status'=>'err','reason'=>'Unsupported filetype'); $answer = array('status'=>'err','reason'=>'Unsupported filetype');
if($answer['hash']) if($answer['hash'])
{
//add this sha1 to the list
addSha1($answer['hash'],$sha1); addSha1($answer['hash'],$sha1);
// Lets' check all storage controllers and tell them that a new file was uploaded
$sc = getStorageControllers();
foreach($sc as $contr)
{
if((new $contr())->isEnabled()===true)
(new $contr())->pushFile($answer['hash']);
}
}
echo json_encode($answer); echo json_encode($answer);
} }
else else

View File

@@ -8,7 +8,7 @@
* - Conversion jpg,png to webp * - Conversion jpg,png to webp
*/ */
class ImageController class ImageController 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('png','bmp','gif','jpg','jpeg','x-png','ico','webp');} public function getRegisteredExtensions(){return array('png','bmp','gif','jpg','jpeg','x-png','ico','webp');}
@@ -56,15 +56,6 @@ class ImageController
copy($tmpfile, $file); copy($tmpfile, $file);
unlink($tmpfile); unlink($tmpfile);
if(defined('ALT_FOLDER') && ALT_FOLDER)
{
$altname=ALT_FOLDER.DS.$hash;
if(!file_exists($altname) && is_dir(ALT_FOLDER))
{
copy($file,$altname);
}
}
if(defined('LOG_UPLOADER') && LOG_UPLOADER) if(defined('LOG_UPLOADER') && LOG_UPLOADER)
{ {
$fh = fopen(ROOT.DS.'data'.DS.'uploads.txt', 'a'); $fh = fopen(ROOT.DS.'data'.DS.'uploads.txt', 'a');

View File

@@ -1,6 +1,6 @@
<?php <?php
class TextController 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');} public function getRegisteredExtensions(){return array('txt');}
@@ -52,15 +52,6 @@ class TextController
copy($tmpfile, $file); copy($tmpfile, $file);
unlink($tmpfile); unlink($tmpfile);
if(defined('ALT_FOLDER') && ALT_FOLDER)
{
$altname=ALT_FOLDER.DS.$hash;
if(!file_exists($altname) && is_dir(ALT_FOLDER))
{
copy($file,$altname);
}
}
if(defined('LOG_UPLOADER') && LOG_UPLOADER) if(defined('LOG_UPLOADER') && LOG_UPLOADER)
{ {
$fh = fopen(ROOT.DS.'data'.DS.'uploads.txt', 'a'); $fh = fopen(ROOT.DS.'data'.DS.'uploads.txt', 'a');

View File

@@ -0,0 +1,9 @@
<?php
class UrlController implements ContentController
{
//returns all extensions registered by this type of content
public function getRegisteredExtensions(){return array('url');}
public function handleHash($hash,$url){}
public function handleUpload($tmpfile,$hash=false){}
}

View File

@@ -1,6 +1,6 @@
<?php <?php
class VideoController 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','ogg','webm');} public function getRegisteredExtensions(){return array('mp4','ogg','webm');}
@@ -81,15 +81,6 @@ class VideoController
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 &");
if(defined('ALT_FOLDER') && ALT_FOLDER)
{
$altname=ALT_FOLDER.DS.$hash;
if(!file_exists($altname) && is_dir(ALT_FOLDER))
{
copy($file,$altname);
}
}
if(defined('LOG_UPLOADER') && LOG_UPLOADER) if(defined('LOG_UPLOADER') && LOG_UPLOADER)
{ {
$fh = fopen(ROOT.DS.'data'.DS.'uploads.txt', 'a'); $fh = fopen(ROOT.DS.'data'.DS.'uploads.txt', 'a');

View File

@@ -1,7 +0,0 @@
<?php
class UrlController
{
//returns all extensions registered by this type of content
public function getRegisteredExtensions(){return array('url');}
}

View File

@@ -31,7 +31,25 @@ function architect($url)
foreach($u as $el) foreach($u as $el)
{ {
if(isExistingHash($el)) if(isExistingHash($el))
{
$hash = $el; $hash = $el;
break;
}
if($hash === false && mightBeAHash($el))
{
if(!$sc)
$sc = getStorageControllers();
foreach($sc as $contr)
{
$c = new $contr();
if($c->isEnabled()===true && $c->hashExists($el))
{
$c->pullFile($el);
$hash = $el;
break;
}
}
}
} }
//we didn't find a hash. Well let's just display the webpage instead //we didn't find a hash. Well let's just display the webpage instead
@@ -67,7 +85,7 @@ function architect($url)
(new VideoController())->handleHash($hash,$u); (new VideoController())->handleHash($hash,$u);
} }
//very odd. We know it's a valid hash but no controller says it's one of their kids //very odd. We know it's a valid hash but no controller says it's one of their kids
//oh well, just show the main website //oh well
else else
{ {
var_dump("odd err"); var_dump("odd err");
@@ -93,11 +111,24 @@ function isExistingHash($hash)
return is_dir(ROOT.DS.'data'.DS.$hash); return is_dir(ROOT.DS.'data'.DS.$hash);
} }
function mightBeAHash($string)
{
$len = strlen($string);
$dot = strpos($string,'.');
if(substr_count($string,'.')!=1) return false;
if(!$dot) return false;
$afterdot = substr($string,$dot+1);
//@todo: maybe pull all allowed types and compare to afterdot
return ($afterdot && strlen($afterdot)>=2 && strlen($afterdot)<=5 );
}
function autoload($className) function autoload($className)
{ {
if (file_exists(ROOT . DS . 'controllers' . DS . strtolower($className) . '.php')) if (file_exists(ROOT . DS . 'content-controllers' . DS . strtolower($className) . '.php'))
require_once(ROOT . DS . 'controllers' . DS . strtolower($className) . '.php'); require_once(ROOT . DS . 'content-controllers' . DS . strtolower($className) . '.php');
if (file_exists(ROOT . DS . 'interfaces' . DS . strtolower($className) . '.interface.php'))
require_once(ROOT . DS . 'interfaces' . DS . strtolower($className) . '.interface.php');
} }
function renderTemplate($template,$vars=false) function renderTemplate($template,$vars=false)
@@ -225,8 +256,7 @@ function getTypeOfFile($url)
$fi = new finfo(FILEINFO_MIME); $fi = new finfo(FILEINFO_MIME);
$type = $fi->buffer(file_get_contents($url, false, null, -1, 1024)); $type = $fi->buffer(file_get_contents($url, false, null, -1, 1024));
//to catch a strange error for PHP7 and Alpine Linux // on linux use the "file" command or it will handle everything as octet-stream
//if the file seems to be a stream, use unix file command
if(strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && startsWith($type,'application/octet-stream')) if(strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && startsWith($type,'application/octet-stream'))
{ {
$content_type = exec("file -bi " . escapeshellarg($url)); $content_type = exec("file -bi " . escapeshellarg($url));
@@ -334,3 +364,22 @@ function isSize($var)
return true; return true;
} }
function getStorageControllers()
{
$controllers = array();
if ($handle = opendir(ROOT.DS.'storage-controllers')) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
if(endswith($entry,'.controller.php'))
{
$controllers[] = ucfirst(substr($entry,0,-15)).'Storage';
include_once(ROOT.DS.'storage-controllers'.DS.$entry);
}
}
}
closedir($handle);
}
return $controllers;
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* All settings that are uncommented are mandatory
* Others optional
*/
//Use a specific domain for links presented to the user
//Format: https://your.domain.name/
// MUST HAVE TAILING /
define('URL','https://dev.pictshare.net/');
//define('JPEG_COMPRESSION', 90);
//define('FFMPEG_BINARY','');
//define('ALT_FOLDER','/ftp/pictshare');

View File

@@ -10,10 +10,10 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
//loading core and controllers //loading core and controllers
include_once(ROOT.DS.'inc'.DS.'core.php'); include_once(ROOT.DS.'inc'.DS.'core.php');
require_once(ROOT . DS . 'controllers' . DS. 'image'. DS . 'image.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'image'. DS . 'image.controller.php');
require_once(ROOT . DS . 'controllers' . DS. 'text'. DS . 'text.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'text'. DS . 'text.controller.php');
require_once(ROOT . DS . 'controllers' . DS. 'url'. DS . 'url.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'url'. DS . 'url.controller.php');
require_once(ROOT . DS . 'controllers' . DS. 'video'. DS . 'video.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'video'. DS . 'video.controller.php');
//send the URL to the architect. It'll know what to do //send the URL to the architect. It'll know what to do

View File

@@ -0,0 +1,37 @@
<?php
/**
* Content controller interface for new content types
*/
interface ContentController
{
/** This method will return all file extensions that will be associated with this content type
* for example 'pdf' but it could be anything really. You just need a way later to confirm that a type is what it says it is
*
*
* @return array the extensions of files associated with this controller. eg. return array('pdf');
*/
public function getRegisteredExtensions();
/** This method will be called whenever the system has to find out if a user requested (existing) hash
* belongs to this controller.
* In here the content should be rendered or processed like resized or something.
* You can decide what it does by working with the $url array which gives you every element in the URL
* Does not need to return anything for example you can just set the header and print your data right away
*
* @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.
*/
public function handleHash($hash,$url);
/** 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".
* For Example if someone uploads a PDF and getRegisteredExtensions has registered "pdf", then this method of this
* controller will be called
*
* @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
*/
public function handleUpload($tmpfile,$hash=false);
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* StorageController interface
*
* Must be implemented by all storage systems
*/
interface StorageController
{
/**
* Checks if this storage system is enabled.
* For example check if all depenencies are met
* or config vars are set
*
* @return bool
*/
function isEnabled();
/**
* Is fired whenever a hash is not found locally
* Use this to look in your storage system for the file
*
* @param string $hash is the hash of the file requested
*
* @return bool
*/
function hashExists($hash);
/**
* If a file does exist in this storage system, then this method should
* get the file and put it in the default data directory
*
* The file should be placed in /data/$hash/$hash where the first $hash is obviously
* a folder that you might have to create first before putting the file in
*
* @param string $hash is the hash of the file that should be pulled from this storage system
*
* @return bool true if successful
*/
function pullFile($hash);
/**
* Whenever a new file is uploaded this method will be called
* You should then upload it or do whatever your storage system is meant to do with new files
*
* @param string $hash is the hash of the new file. The file path of this file is always ROOT.DS.'data'.DS.$hash.DS.$hash
*
* @return bool true if successful
*/
function pushFile($hash);
/**
* If deletion of a file is requested, this method is called
*
* @param string $hash is the hash of the file. Delete this hash from your storage system
*
* @return bool true if successful
*/
function deleteFile($hash);
}

View File

@@ -0,0 +1,47 @@
<?php
class AltfolderStorage implements StorageController
{
function isEnabled()
{
return (defined('ALT_FOLDER') && ALT_FOLDER && is_dir(ALT_FOLDER));
}
function hashExists($hash)
{
$altname=ALT_FOLDER.DS.$hash;
return file_exists($altname);
}
function pullFile($hash)
{
$altname=ALT_FOLDER.DS.$hash;
if(file_exists($altname))
{
mkdir(ROOT.DS.'data'.DS.$hash);
copy($altname,ROOT.DS.'data'.DS.$hash.DS.$hash);
//and don't forget to add it to the duplicate detection system
addSha1($hash,sha1_file($altname));
}
}
function pushFile($hash)
{
$altname=ALT_FOLDER.DS.$hash;
$orig = ROOT.DS.'data'.DS.$hash.DS.$hash;
if(file_exists($orig) && !$this->hashExists($hash))
{
copy($orig,$altname);
}
}
function deleteFile($hash)
{
$altname=ALT_FOLDER.DS.$hash;
if(file_exists($altname))
{
unlink($altname);
}
}
}

View File

@@ -26,7 +26,7 @@ include_once(ROOT.DS.'inc'.DS.'config.inc.php');
//loading core and controllers //loading core and controllers
include_once(ROOT.DS.'inc'.DS.'core.php'); include_once(ROOT.DS.'inc'.DS.'core.php');
require_once(ROOT . DS . 'controllers' . DS. 'video'. DS . 'video.controller.php'); require_once(ROOT . DS . 'content-controllers' . DS. 'video'. DS . 'video.controller.php');
if(!defined('FFMPEG_BINARY')||FFMPEG_BINARY=='' || !FFMPEG_BINARY) exit('Error: FFMPEG_BINARY not defined, no clue where to look'); if(!defined('FFMPEG_BINARY')||FFMPEG_BINARY=='' || !FFMPEG_BINARY) exit('Error: FFMPEG_BINARY not defined, no clue where to look');