mancubus.class.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <?php
  2. /**
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
  5. *
  6. * You should have received a copy of the
  7. * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
  8. * along with this program. If not, see http://www.sun.com/cddl/cddl.html
  9. *
  10. * 2019 - 2020 https://://www.bananas-playground.net/projekt/selfpaste
  11. */
  12. /**
  13. * Handles the upload and the file itself
  14. */
  15. class Mancubus {
  16. private $_uploadedData;
  17. private $_short;
  18. private $_saveFilename;
  19. private $_storagePath;
  20. private $_shortURL;
  21. /**
  22. * Mancubus constructor.
  23. */
  24. function __construct() {
  25. }
  26. /**
  27. * Requires a single upload from $_FILES
  28. * @see https://www.php.net/manual/en/features.file-upload.post-method.php
  29. * @param $file array
  30. * @return bool
  31. */
  32. public function load($file) {
  33. $ret = false;
  34. if(isset($file['name'])
  35. && isset($file['type'])
  36. && isset($file['size'])
  37. && isset($file['tmp_name'])
  38. && isset($file['error'])
  39. ) {
  40. $this->_uploadedData = $file;
  41. $ret = true;
  42. }
  43. return $ret;
  44. }
  45. /**
  46. * Either set short to given string
  47. * or create from _saveFilename. In this case _saveFilename is a number
  48. * @param string $short
  49. */
  50. public function setShort($short='') {
  51. if($short != '') {
  52. $this->_short = $short;
  53. }
  54. elseif(!empty($this->_saveFilename)) {
  55. $this->_short = Summoner::b64sl_pack_id($this->_saveFilename);
  56. }
  57. }
  58. /**
  59. * Either set _saveFilename to given string
  60. * or create from a random number. In this case _short needs this as a base
  61. * @param string $string
  62. * @throws Exception
  63. */
  64. public function setSaveFilename($string='') {
  65. if($string != '') {
  66. $this->_saveFilename = $string;
  67. }
  68. else {
  69. $r = random_int(1000, 9999);
  70. $this->_saveFilename = (string)$r;
  71. }
  72. }
  73. /**
  74. * Set _shortURL to given string
  75. * or create based on SELFPASTE_URL and _short
  76. * @param string $string
  77. */
  78. public function setShortURL($string='') {
  79. if($string != '') {
  80. $this->_shortURL = $string;
  81. }
  82. elseif(!empty($this->_short)) {
  83. $this->_shortURL = SELFPASTE_URL.'/'.$this->_short;
  84. }
  85. }
  86. /**
  87. * set the right storage path based on _saveFilename
  88. * and SELFPASTE_UPLOAD_DIR
  89. */
  90. public function setStoragePath() {
  91. $string = $this->_saveFilename;
  92. if(!empty($string)) {
  93. $p = SELFPASTE_UPLOAD_DIR.'/';
  94. $p .= Summoner::forwardslashStringToPath($string);
  95. $this->_storagePath = $p;
  96. }
  97. }
  98. /**
  99. * After setting importing stuff process the upload
  100. * return status and message
  101. * @return array
  102. */
  103. public function process() {
  104. $ret = array(
  105. 'message' => '',
  106. 'status' => false
  107. );
  108. try {
  109. $ret = $this->_checkFlood();
  110. $ret = $this->_checkFileUploadStatus();
  111. $ret = $this->_checkAllowedFiletype();
  112. $ret = $this->_checkStorage();
  113. $ret = $this->_moveUploadedFile();
  114. $this->_checkLifetime();
  115. }
  116. catch (Exception $e) {
  117. $ret['message'] = $e->getMessage();
  118. }
  119. return $ret;
  120. }
  121. /**
  122. * Cleans lifetime and floodfiles.
  123. * @param boolean
  124. */
  125. public function cleanupCronjob($verbose=false) {
  126. $this->_cleanupFloodFiles($verbose);
  127. $this->_checkLifetime($verbose);
  128. }
  129. /**
  130. * Check if the POST upload worked
  131. * @return array message,status
  132. * @throws Exception
  133. */
  134. private function _checkFileUploadStatus() {
  135. $check = Summoner::checkFileUploadStatus($this->_uploadedData['error']);
  136. if($check['status'] === true) {
  137. # check has the structure we want already
  138. return $check;
  139. }
  140. else {
  141. throw new Exception($check['message']);
  142. }
  143. }
  144. /**
  145. * Check if the uploaded file matches the allowed filetypes
  146. * @return array message,status
  147. * @throws Exception
  148. */
  149. private function _checkAllowedFiletype() {
  150. $message = "Filetype not supported";
  151. $status = false;
  152. $workWith = $this->_uploadedData['tmp_name'];
  153. if(!empty($workWith)) {
  154. $finfo = finfo_open(FILEINFO_MIME_TYPE);
  155. $mime = finfo_file($finfo, $workWith);
  156. finfo_close($finfo);
  157. if(strpos(SELFPASTE_ALLOWED_FILETYPES,$mime) !== false) {
  158. $status = true;
  159. $message = "Filetype allowed";
  160. }
  161. else {
  162. if(DEBUG) $message .= " $mime";
  163. throw new Exception($message);
  164. }
  165. } else {
  166. throw new Exception($message);
  167. }
  168. return array(
  169. 'message' => $message,
  170. 'status' => $status
  171. );
  172. }
  173. /**
  174. * check if SELFPASTE_UPLOAD_DIR and _storagePath
  175. * is creatable. If so create _storagePath
  176. * @return array
  177. * @throws Exception
  178. */
  179. private function _checkStorage() {
  180. $message = "File storage failure";
  181. $status = false;
  182. $workwith = $this->_storagePath;
  183. if(is_writable(SELFPASTE_UPLOAD_DIR)) {
  184. if (mkdir($workwith,0777,true)) {
  185. $message = "File storage creation success";
  186. $status = true;
  187. }
  188. else {
  189. if(DEBUG) $message .= " ".$workwith;
  190. throw new Exception($message);
  191. }
  192. }
  193. else {
  194. throw new Exception('Storage location not writeable');
  195. }
  196. return array(
  197. 'message' => $message,
  198. 'status' => $status
  199. );
  200. }
  201. /**
  202. * Move the tmp_file from _uploadedData to the new location
  203. * provided by _storagePath and _saveFilename
  204. * @return array
  205. * @throws Exception
  206. */
  207. private function _moveUploadedFile() {
  208. $message = "File storage failure";
  209. $status = false;
  210. $workwithPath = $this->_storagePath;
  211. $workwithFilename = $this->_saveFilename;
  212. if(!empty($workwithPath) && !empty($workwithFilename)) {
  213. $_newFilename = Summoner::endsWith($workwithPath,'/') ? $workwithPath : $workwithPath.'/';
  214. $_newFilename .= $workwithFilename;
  215. if(move_uploaded_file($this->_uploadedData['tmp_name'], $_newFilename)) {
  216. $status = true;
  217. $message = $this->_shortURL;
  218. }
  219. else {
  220. if(DEBUG) $message .= " $_newFilename";
  221. throw new Exception($message);
  222. }
  223. }
  224. else {
  225. throw new Exception('Failing requirements for saving');
  226. }
  227. return array(
  228. 'message' => $message,
  229. 'status' => $status
  230. );
  231. }
  232. /**
  233. * check if the current paste request is within limits
  234. * for this check if the file exists. If so just return the shortURL
  235. * @return array
  236. * @throws Exception
  237. */
  238. private function _checkFlood() {
  239. $message = "Failing flood requirements";
  240. $status = false;
  241. $this->_cleanupFloodFiles();
  242. if(!empty($this->_uploadedData['name']) && !empty($this->_shortURL)) {
  243. $filename = md5($_SERVER['REMOTE_ADDR'].$this->_uploadedData['name']);
  244. $filepath = SELFPASTE_UPLOAD_DIR.'/'.$filename;
  245. if(!file_exists($filepath)) {
  246. if(file_put_contents($filepath,$this->_shortURL)) {
  247. $status = true;
  248. $message = $this->_shortURL;
  249. }
  250. else {
  251. throw new Exception("Failed flood prevention requirements");
  252. }
  253. }
  254. else {
  255. $message = file_get_contents($filepath);
  256. throw new Exception($message);
  257. }
  258. }
  259. else {
  260. throw new Exception($message);
  261. }
  262. return array(
  263. 'message' => $message,
  264. 'status' => $status
  265. );
  266. }
  267. /**
  268. * clean up the flood tmp files. Everything older then 30 sec will be deleted.
  269. */
  270. private function _cleanupFloodFiles($verbose=false) {
  271. $iterator = new DirectoryIterator(SELFPASTE_UPLOAD_DIR);
  272. $now = time();
  273. foreach ($iterator as $file) {
  274. if($file->isDot() || $file->isDir() || Summoner::startsWith($file->getFilename(),'.')) continue;
  275. if ($now - $file->getCTime() >= SELFPASTE_FLOOD_LIFETIME) {
  276. if($verbose === true) echo "Delete ".$file->getFilename()."\n";
  277. unlink(SELFPASTE_UPLOAD_DIR.'/'.$file->getFilename());
  278. }
  279. }
  280. }
  281. /**
  282. * delete all pastes older than SELFPASTE_PASTE_LIFETIME
  283. */
  284. private function _checkLifetime($verbose=false) {
  285. $iterator = new RecursiveDirectoryIterator(SELFPASTE_UPLOAD_DIR);
  286. $datepointInThePastInSec = strtotime('-'.SELFPASTE_PASTE_LIFETIME.' days');
  287. foreach (new RecursiveIteratorIterator($iterator) as $file) {
  288. $fname = $file->getFilename();
  289. if($file->isDir()
  290. || Summoner::startsWith($file->getFilename(),'.')
  291. || isset($fname[4])
  292. ) continue;
  293. if ($file->getMTime() <= $datepointInThePastInSec) {
  294. if(is_writable($file->getPathname())) {
  295. if($verbose === true) echo "Delete ".$file->getPathname()."\n";
  296. unlink($file->getPathname());
  297. }
  298. }
  299. }
  300. }
  301. }