From a0497aadf95b03a6b164f7b8c130041ded7efabb Mon Sep 17 00:00:00 2001 From: Banana Date: Sat, 21 Dec 2019 21:06:05 +0100 Subject: [PATCH] working with bash client so far. Updated documentation. Basic config file. --- README | 2 +- TODO | 7 ++ documentation/clients.txt | 15 ++++ documentation/filetypes.txt | 4 +- documentation/requirements.txt | 2 + webroot/config.default.php | 6 +- webroot/index.php | 35 ++++++---- webroot/lib/summoner.class.php | 123 +++++++++++++++++++++++++++++++-- webroot/pasties/.gitignore | 3 + webroot/view/view.inc.php | 18 +++++ 10 files changed, 196 insertions(+), 19 deletions(-) create mode 100644 documentation/clients.txt create mode 100644 documentation/requirements.txt create mode 100644 webroot/pasties/.gitignore create mode 100644 webroot/view/view.inc.php diff --git a/README b/README index 7388173..62feff4 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ This tool uses PHP fileinfo: https://www.php.net/manual/en/intro.fileinfo.php > by looking for certain magic byte sequences at specific positions within the file. > While this is not a bullet proof approach the heuristics used do a very good job. -It is not really bullet proof, but it does the job. Everything can be manipulated +It is not really bulletproof, but it does the job. Everything can be manipulated to look alike something it isn't. So, here is a friendly REMINDER: diff --git a/TODO b/TODO index e69de29..8483a7e 100644 --- a/TODO +++ b/TODO @@ -0,0 +1,7 @@ +* Documentation +* lifetime +* deletion of old ones +* add force download parameter +* HTTPS for selfpaste bash client +* multiple secrets +* extending allowed filetypes \ No newline at end of file diff --git a/documentation/clients.txt b/documentation/clients.txt new file mode 100644 index 0000000..d677b53 --- /dev/null +++ b/documentation/clients.txt @@ -0,0 +1,15 @@ +A bash client 'selfpaste.sh' is available for use in the client folder. +To create this file just copy selfpaste.default.sh to selfpaste.sh and +make sure you change ENDPOINT and SELFPASTE_UPLOAD_SECRET. + +Requirements to create a new client are: + + - Talk to the selfpaste endpoint over HTTP(S) + - Make a POST with multipart/form-data + - The post must have field pasty and field dl + -- pasty=File to upload + -- dl=YOUR SECRET + - Can parse json at success + -- message: Contains the URL or detailed information + -- status: integer based in HTML status code. + - A normal HTTP 200 without json is not a success \ No newline at end of file diff --git a/documentation/filetypes.txt b/documentation/filetypes.txt index 93d8e41..9e7c867 100644 --- a/documentation/filetypes.txt +++ b/documentation/filetypes.txt @@ -8,4 +8,6 @@ It is not really bullet proof, but it does the job. Everything can be manipulate to look alike something it isn't. To expand or reduce the allowed filetypes, edit the SELFPASTE_ALLOWED_FILETYPES string to your needs. -Again READ the README and security info! \ No newline at end of file +Again READ the README and security info! + +Read more about filetypes here: https://www.iana.org/assignments/media-types/media-types.xhtml \ No newline at end of file diff --git a/documentation/requirements.txt b/documentation/requirements.txt new file mode 100644 index 0000000..0d502e7 --- /dev/null +++ b/documentation/requirements.txt @@ -0,0 +1,2 @@ +PHP >=7.3 +Apache >= 2.4 \ No newline at end of file diff --git a/webroot/config.default.php b/webroot/config.default.php index 15bfe4e..bfd7566 100644 --- a/webroot/config.default.php +++ b/webroot/config.default.php @@ -18,4 +18,8 @@ define('SELFPASTE_UPLOAD_SECRET','PLEASE CHANGE YOUR SECRET'); define('SELFPASTE_UPLOAD_DIR','pasties'); # those are the allowed file types. # Make sure you read the README and documentation! -define(SELFPASTE_ALLOWED_FILETYPES,''); +define('SELFPASTE_ALLOWED_FILETYPES','text/plain,text/comma-separated-values,text/css,text/xml,text/x-php'); +# this is your domain and path on which selfpaste is accessible +# needed to respond with the correct link for your paste +# please NO / at the end +define('SELFPASTE_URL','http://your.tld/path/selfpaste/webroot'); diff --git a/webroot/index.php b/webroot/index.php index 85729a4..7ebb536 100644 --- a/webroot/index.php +++ b/webroot/index.php @@ -62,36 +62,47 @@ $contentView = 'welcome'; $httpResponseCode = 200; if(!empty($_short)) { + $contentType = 'Content-type: text/plain; charset=UTF-8'; $contentView = 'view'; + + $_requestFile = Summoner::createStoragePath($_short); + $_requestFile .= $_short; + if(is_readable($_requestFile)) { + $contentBody = $_requestFile; + } } elseif ($_create === true) { $contentView = 'created'; - $contentBody = array( - 'message' => 'Something went wrong.', - 'status' => '400' - ); $contentType = 'Content-type:application/json;charset=utf-8'; $httpResponseCode = 400; + $_message = 'Something went wrong.'; $_file = $_FILES['pasty']; - $_checks = array('fileupload','filetype','store'); + $_file['short'] = Summoner::createShort(); + $_file['shortUrl'] = SELFPASTE_URL.'/'.$_file['short']; + $_file['storagepath'] = Summoner::createStoragePath($_file['short']); + + $_checks = array('checkFileUploadStatus','checkAllowedFiletype','checkStorage','moveUploadedPasteFile'); $_do['status'] = false; foreach($_checks as $_check) { if(method_exists('Summoner',$_check)) { $_do = Summoner::$_check($_file); - if($_do['status'] !== true) { + $_message = $_do['message']; + if($_do['status'] === true) { + $httpResponseCode = 200; + } + else { + $httpResponseCode = 400; break; } } } - if($_do['status'] === true) { - $contentBody = array( - 'message' => $_do['message'], - 'status' => '200' - ); - } + $contentBody = array( + 'message' => $_message, + 'status' => $httpResponseCode + ); } header($contentType); diff --git a/webroot/lib/summoner.class.php b/webroot/lib/summoner.class.php index 8f3b2da..f05aaf8 100644 --- a/webroot/lib/summoner.class.php +++ b/webroot/lib/summoner.class.php @@ -144,7 +144,7 @@ class Summoner { * @param $file * @return array */ - static function fileupload($file) { + static function checkFileUploadStatus($file) { $message = "Unknown upload error"; $status = false; @@ -193,15 +193,78 @@ class Summoner { * @param $file * @return array */ - static function filetype($file) { - $message = "Filetype not suported"; + static function checkAllowedFiletype($file) { + $message = "Filetype not supported"; $status = false; if(isset($file['tmp_name'])) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $file['tmp_name']); finfo_close($finfo); - var_dump($mime); + if(strpos(SELFPASTE_ALLOWED_FILETYPES,$mime) !== false) { + $status = true; + $message = "Filetype allowed"; + } + if(DEBUG) $message .= " $mime"; + } + + return array( + 'message' => $message, + 'status' => $status + ); + } + + /** + * Simple helper to create and make sure the storage + * location is available + * Expects an array from $_FILE + * with an extra key = storagepath + * + * @param $file + * @return array + */ + static function checkStorage($file) { + $message = "File storage failure"; + $status = false; + + if(isset($file['storagepath']) && !empty($file['storagepath']) + && is_writable(SELFPASTE_UPLOAD_DIR)) { + if (mkdir($file['storagepath'],0777,true)) { + $message = "File storage creation success"; + $status = true; + } + } + + if(DEBUG) $message .= " ".$file['storagepath']; + + return array( + 'message' => $message, + 'status' => $status + ); + } + + /** + * move the uploaded file. + * Depends on the _FILES info and the keys + * storagepath, short, shortUrl + * @param $file + * @return array + */ + static function moveUploadedPasteFile($file) { + $message = "File storage failure"; + $status = false; + //shortUrl + + if(isset($file['storagepath']) && !empty($file['storagepath']) + && isset($file['short']) && !empty($file['short'])) { + $_newFilename = self::endsWith($file['storagepath'],'/') ? $file['storagepath'] : $file['storagepath'].'/'; + $_newFilename .= $file['short']; + if(move_uploaded_file($file['tmp_name'], $_newFilename)) { + $status = true; + $message = $file['shortUrl']; + } + + if(DEBUG) $message .= " $_newFilename"; } return array( @@ -209,4 +272,56 @@ class Summoner { 'status' => $status ); } + + /** + * Simple helper to create a new name + * + * @return string + * @throws Exception + */ + static function createShort() { + $idstring = random_int(1000, 9999); + return self::b64sl_pack_id($idstring); + } + + /** + * create a short string based on a integer + * + * @see https://www.jwz.org/base64-shortlinks/ + * + * @return string + */ + static function b64sl_pack_id($id) { + $id = intval($id); + $ida = ($id > 0xFFFFFFFF ? $id >> 32 : 0); // 32 bit big endian, top + $idb = ($id & 0xFFFFFFFF); // 32 bit big endian, bottom + $id = pack ('N', $ida) . pack ('N', $idb); + $id = preg_replace('/^\000+/', '', "$id"); // omit high-order NUL bytes + $id = base64_encode ($id); + $id = str_replace ('+', '-', $id); // encode URL-unsafe "+" "/" + $id = str_replace ('/', '_', $id); + $id = preg_replace ('/=+$/', '', $id); // omit trailing padding bytes + return $id; + } + + /** + * create based on the given string a path + * each char in string is a dir + * and add SELFPASTE_UPLOAD_DIR + * asd -> SELFPASTE_UPLOAD_DIR/a/s/d + * @param $string + * @return bool|string + */ + static function createStoragePath($string) { + $p = false; + + if(!empty($string) && is_writable(SELFPASTE_UPLOAD_DIR)) { + $p = SELFPASTE_UPLOAD_DIR.'/'; + for($i=0;$i